2017-10-29 06:50:47 +00:00
using System ;
2015-08-10 20:33:16 +00:00
using System.Collections.Generic ;
using System.Globalization ;
using System.Linq ;
using System.Text ;
using System.Text.RegularExpressions ;
2017-10-29 06:50:47 +00:00
using System.Threading.Tasks ;
2015-08-10 20:33:16 +00:00
using System.Xml.Linq ;
2017-10-29 06:50:47 +00:00
using CsQuery ;
2018-03-10 08:05:56 +00:00
using Jackett.Common.Models ;
using Jackett.Common.Models.IndexerConfig ;
using Jackett.Common.Services.Interfaces ;
using Jackett.Common.Utils ;
using Jackett.Common.Utils.Clients ;
2017-10-29 06:50:47 +00:00
using Newtonsoft.Json.Linq ;
using NLog ;
2015-08-10 20:33:16 +00:00
2018-03-10 08:05:56 +00:00
namespace Jackett.Common.Indexers
2015-08-10 20:33:16 +00:00
{
2017-07-10 20:58:44 +00:00
public class TVChaosUK : BaseWebIndexer
2015-08-10 20:33:16 +00:00
{
2017-10-29 06:50:47 +00:00
private string LoginUrl { get { return SiteLink + "takelogin.php" ; } }
private string GetRSSKeyUrl { get { return SiteLink + "getrss.php" ; } }
private string SearchUrl { get { return SiteLink + "browse.php" ; } }
private string RSSUrl { get { return SiteLink + "rss.php?secret_key={0}&feedtype=download&timezone=0&showrows=50&categories=all" ; } }
private string CommentUrl { get { return SiteLink + "details.php?id={0}" ; } }
private string DownloadUrl { get { return SiteLink + "download.php?id={0}" ; } }
private new ConfigurationDataBasicLoginWithRSS configData
2015-08-10 20:33:16 +00:00
{
get { return ( ConfigurationDataBasicLoginWithRSS ) base . configData ; }
set { base . configData = value ; }
}
2017-11-05 09:42:03 +00:00
public TVChaosUK ( IIndexerConfigurationService configService , WebClient wc , Logger l , IProtectionService ps )
2015-08-10 20:33:16 +00:00
: base ( name : "TV Chaos" ,
description : "Total Chaos" ,
2016-01-03 20:40:59 +00:00
link : "https://www.tvchaosuk.com/" ,
2015-08-11 18:48:25 +00:00
caps : TorznabUtil . CreateDefaultTorznabTVCaps ( ) ,
2017-07-10 20:58:44 +00:00
configService : configService ,
2015-08-10 20:33:16 +00:00
client : wc ,
logger : l ,
p : ps ,
configData : new ConfigurationDataBasicLoginWithRSS ( ) )
{
2017-11-05 09:42:03 +00:00
Encoding = Encoding . UTF8 ;
2016-12-09 17:20:58 +00:00
Language = "en-uk" ;
2017-01-27 15:57:32 +00:00
Type = "private" ;
2016-12-06 13:56:47 +00:00
2017-10-01 12:27:30 +00:00
AddCategoryMapping ( 72 , TorznabCatType . PC , "Appz" ) ;
AddCategoryMapping ( 115 , TorznabCatType . TV , "Classic TV" ) ;
AddCategoryMapping ( 139 , TorznabCatType . TV , "Classic Comedy" ) ;
AddCategoryMapping ( 141 , TorznabCatType . TV , "Classic Comedy Drama" ) ;
AddCategoryMapping ( 140 , TorznabCatType . TV , "Classic Crime Drama" ) ;
AddCategoryMapping ( 118 , TorznabCatType . TV , "Classic Documentary" ) ;
AddCategoryMapping ( 138 , TorznabCatType . TV , "Classic Drama" ) ;
AddCategoryMapping ( 149 , TorznabCatType . TV , "Classic Kids/Family" ) ;
AddCategoryMapping ( 142 , TorznabCatType . TV , "Classic Sci-fi" ) ;
AddCategoryMapping ( 148 , TorznabCatType . TV , "Classic Soap" ) ;
AddCategoryMapping ( 78 , TorznabCatType . TV , "Comedy" ) ;
AddCategoryMapping ( 187 , TorznabCatType . TV , "Comedy Panel/Talk show" ) ;
AddCategoryMapping ( 172 , TorznabCatType . TVHD , "HD Comedy" ) ;
AddCategoryMapping ( 107 , TorznabCatType . TV , "Stand-up Comedy" ) ;
AddCategoryMapping ( 75 , TorznabCatType . TVDocumentary , "Documentary & News" ) ;
AddCategoryMapping ( 189 , TorznabCatType . TVDocumentary , "Docudrama" ) ;
AddCategoryMapping ( 224 , TorznabCatType . TVDocumentary , "Documentary" ) ;
AddCategoryMapping ( 174 , TorznabCatType . TVDocumentary , "HD Documentary" ) ;
AddCategoryMapping ( 113 , TorznabCatType . TVDocumentary , "Historical" ) ;
AddCategoryMapping ( 218 , TorznabCatType . TVDocumentary , "News and Current Affairs" ) ;
AddCategoryMapping ( 100 , TorznabCatType . TVDocumentary , "True Crime" ) ;
AddCategoryMapping ( 98 , TorznabCatType . TVDocumentary , "Wildlife/Nature" ) ;
AddCategoryMapping ( 74 , TorznabCatType . TV , "Drama" ) ;
AddCategoryMapping ( 180 , TorznabCatType . TV , "Comedy-Drama" ) ;
AddCategoryMapping ( 76 , TorznabCatType . TV , "Crime Drama" ) ;
AddCategoryMapping ( 99 , TorznabCatType . TV , "Cult Drama" ) ;
AddCategoryMapping ( 175 , TorznabCatType . TVHD , "HD Drama" ) ;
AddCategoryMapping ( 91 , TorznabCatType . TV , "Entertainment" ) ;
AddCategoryMapping ( 212 , TorznabCatType . TV , "Chat Shows" ) ;
AddCategoryMapping ( 223 , TorznabCatType . TVHD , "HD Entertainment" ) ;
AddCategoryMapping ( 188 , TorznabCatType . TV , "Musical TV" ) ;
AddCategoryMapping ( 217 , TorznabCatType . TV , "Quiz, Panel & Game Shows" ) ;
AddCategoryMapping ( 101 , TorznabCatType . TV , "Special Interest" ) ;
AddCategoryMapping ( 106 , TorznabCatType . TV , "Factual & Reality" ) ;
AddCategoryMapping ( 103 , TorznabCatType . TV , "Cookery, Food and Drink" ) ;
AddCategoryMapping ( 114 , TorznabCatType . TV , "Factual TV" ) ;
AddCategoryMapping ( 221 , TorznabCatType . TVHD , "HD Factual/Reality" ) ;
AddCategoryMapping ( 215 , TorznabCatType . TV , "Home and Garden" ) ;
AddCategoryMapping ( 219 , TorznabCatType . TV , "Motoring" ) ;
AddCategoryMapping ( 216 , TorznabCatType . TV , "Reality TV" ) ;
AddCategoryMapping ( 184 , TorznabCatType . TV , "Full Series Packs" ) ;
AddCategoryMapping ( 194 , TorznabCatType . TV , "Classic Comedy" ) ;
AddCategoryMapping ( 193 , TorznabCatType . TV , "Classic Drama" ) ;
AddCategoryMapping ( 196 , TorznabCatType . TV , "Classic Kids/Family" ) ;
AddCategoryMapping ( 170 , TorznabCatType . TV , "Comedy" ) ;
AddCategoryMapping ( 168 , TorznabCatType . TV , "Comedy Drama" ) ;
AddCategoryMapping ( 228 , TorznabCatType . TV , "Commonwealth" ) ;
AddCategoryMapping ( 190 , TorznabCatType . TV , "Crime Drama" ) ;
AddCategoryMapping ( 166 , TorznabCatType . TV , "Documentary" ) ;
AddCategoryMapping ( 185 , TorznabCatType . TV , "Drama" ) ;
AddCategoryMapping ( 191 , TorznabCatType . TV , "Entertainment" ) ;
AddCategoryMapping ( 210 , TorznabCatType . TV , "Factual" ) ;
AddCategoryMapping ( 226 , TorznabCatType . TV , "Foreign" ) ;
AddCategoryMapping ( 167 , TorznabCatType . TV , "Kids/Family" ) ;
AddCategoryMapping ( 186 , TorznabCatType . TV , "Sci-fi" ) ;
AddCategoryMapping ( 82 , TorznabCatType . TV , "Kids/Family" ) ;
AddCategoryMapping ( 198 , TorznabCatType . TVFOREIGN , "Non-UK" ) ;
AddCategoryMapping ( 201 , TorznabCatType . TVFOREIGN , "Comedy" ) ;
AddCategoryMapping ( 208 , TorznabCatType . TVFOREIGN , "Documentary" ) ;
AddCategoryMapping ( 200 , TorznabCatType . TVFOREIGN , "Drama" ) ;
AddCategoryMapping ( 209 , TorznabCatType . TVFOREIGN , "Entertainment" ) ;
AddCategoryMapping ( 203 , TorznabCatType . TVFOREIGN , "Factual/Reality" ) ;
AddCategoryMapping ( 227 , TorznabCatType . TVFOREIGN , "Foreign" ) ;
AddCategoryMapping ( 202 , TorznabCatType . TVFOREIGN , "Sci-fi" ) ;
AddCategoryMapping ( 199 , TorznabCatType . TVFOREIGN , "Soaps" ) ;
AddCategoryMapping ( 204 , TorznabCatType . TVFOREIGN , "Special Interest" ) ;
AddCategoryMapping ( 86 , TorznabCatType . Audio , "Radio/Audio" ) ;
AddCategoryMapping ( 87 , TorznabCatType . AudioAudiobook , "Audio Books" ) ;
AddCategoryMapping ( 88 , TorznabCatType . Audio , "Radio Comedy" ) ;
AddCategoryMapping ( 90 , TorznabCatType . TVSD , "Sci-Fi" ) ;
AddCategoryMapping ( 183 , TorznabCatType . TVSD , "Fantasy" ) ;
AddCategoryMapping ( 176 , TorznabCatType . TVHD , "HD Sci-Fi" ) ;
AddCategoryMapping ( 173 , TorznabCatType . TV , "Supernatural/fantasy" ) ;
AddCategoryMapping ( 220 , TorznabCatType . TV , "Soaps" ) ;
AddCategoryMapping ( 229 , TorznabCatType . TV , "Monthly Archives" ) ;
AddCategoryMapping ( 177 , TorznabCatType . TVHD , "Soap HD" ) ;
AddCategoryMapping ( 230 , TorznabCatType . TVSD , "Soap SD" ) ;
AddCategoryMapping ( 92 , TorznabCatType . TVSport , "Sport" ) ;
AddCategoryMapping ( 222 , TorznabCatType . TVSport , "HD Sport" ) ;
AddCategoryMapping ( 83 , TorznabCatType . Movies , "TV Aired Movies" ) ;
AddCategoryMapping ( 171 , TorznabCatType . Movies , "Classic Movies" ) ;
AddCategoryMapping ( 178 , TorznabCatType . MoviesHD , "HD TV Aired Movies" ) ;
AddCategoryMapping ( 181 , TorznabCatType . Movies , "Made for TV" ) ;
AddCategoryMapping ( 182 , TorznabCatType . Movies , "TV Aired Movies" ) ;
2015-08-10 20:33:16 +00:00
}
2017-06-28 05:31:38 +00:00
public override async Task < IndexerConfigurationStatus > ApplyConfiguration ( JToken configJson )
2015-08-10 20:33:16 +00:00
{
2017-01-02 20:39:28 +00:00
LoadValuesFromJson ( configJson ) ;
2015-08-10 20:33:16 +00:00
var pairs = new Dictionary < string , string > {
{ "username" , configData . Username . Value } ,
{ "password" , configData . Password . Value }
} ;
var result = await RequestLoginAndFollowRedirect ( LoginUrl , pairs , null , true , SearchUrl , SiteLink ) ;
await ConfigureIfOK ( result . Cookies , result . Content ! = null & & result . Content . Contains ( "logout.php" ) , ( ) = >
{
CQ dom = result . Content ;
var errorMessage = dom [ ".left_side table:eq(0) tr:eq(1)" ] . Text ( ) . Trim ( ) . Replace ( "\n\t" , " " ) ;
throw new ExceptionWithConfigData ( errorMessage , configData ) ;
} ) ;
2015-08-16 17:34:45 +00:00
try
{
// Get RSS key
var rssParams = new Dictionary < string , string > {
{ "feedtype" , "download" } ,
{ "timezone" , "0" } ,
{ "showrows" , "50" }
} ;
var rssPage = await PostDataWithCookies ( GetRSSKeyUrl , rssParams , result . Cookies ) ;
var match = Regex . Match ( rssPage . Content , "(?<=secret_key\\=)([a-zA-z0-9]*)" ) ;
configData . RSSKey . Value = match . Success ? match . Value : string . Empty ;
if ( string . IsNullOrWhiteSpace ( configData . RSSKey . Value ) )
throw new Exception ( "Failed to get RSS Key" ) ;
SaveConfig ( ) ;
}
catch ( Exception e )
{
IsConfigured = false ;
throw e ;
}
2015-08-22 20:57:13 +00:00
return IndexerConfigurationStatus . RequiresTesting ;
2015-08-10 20:33:16 +00:00
}
2017-07-03 05:15:47 +00:00
protected override async Task < IEnumerable < ReleaseInfo > > PerformQuery ( TorznabQuery query )
2015-08-10 20:33:16 +00:00
{
var releases = new List < ReleaseInfo > ( ) ;
2015-08-14 21:20:51 +00:00
var searchString = query . GetQueryString ( ) ;
2015-08-10 20:33:16 +00:00
// If we have no query use the RSS Page as their server is slow enough at times!
2016-12-17 12:39:52 +00:00
if ( query . IsTest | | string . IsNullOrWhiteSpace ( searchString ) )
2015-08-10 20:33:16 +00:00
{
var rssPage = await RequestStringWithCookiesAndRetry ( string . Format ( RSSUrl , configData . RSSKey . Value ) ) ;
var rssDoc = XDocument . Parse ( rssPage . Content ) ;
foreach ( var item in rssDoc . Descendants ( "item" ) )
{
var title = item . Descendants ( "title" ) . First ( ) . Value ;
var description = item . Descendants ( "description" ) . First ( ) . Value ;
var link = item . Descendants ( "link" ) . First ( ) . Value ;
var category = item . Descendants ( "category" ) . First ( ) . Value ;
var date = item . Descendants ( "pubDate" ) . First ( ) . Value ;
var torrentIdMatch = Regex . Match ( link , "(?<=id=)(\\d)*" ) ;
var torrentId = torrentIdMatch . Success ? torrentIdMatch . Value : string . Empty ;
if ( string . IsNullOrWhiteSpace ( torrentId ) )
throw new Exception ( "Missing torrent id" ) ;
2017-01-02 14:45:01 +00:00
var infoMatch = Regex . Match ( description , @"Category:\W(?<cat>.*)\W\/\WSeeders:\W(?<seeders>[\d,]*)\W\/\WLeechers:\W(?<leechers>[\d,]*)\W\/\WSize:\W(?<size>[\d\.]*\W\S*)\W\/\WSnatched:\W(?<snatched>[\d,]*) x times" ) ;
2015-08-10 20:33:16 +00:00
if ( ! infoMatch . Success )
2017-01-02 14:45:01 +00:00
throw new Exception ( string . Format ( "Unable to find info in {0}: " , description ) ) ;
2018-04-11 16:36:22 +00:00
2015-08-10 20:33:16 +00:00
var release = new ReleaseInfo ( )
{
Title = title ,
2017-01-02 14:45:01 +00:00
Description = description ,
2015-08-10 20:33:16 +00:00
Guid = new Uri ( string . Format ( DownloadUrl , torrentId ) ) ,
Comments = new Uri ( string . Format ( CommentUrl , torrentId ) ) ,
2017-10-29 06:50:47 +00:00
PublishDate = DateTime . ParseExact ( date , "yyyy-MM-dd H:mm:ss" , CultureInfo . InvariantCulture ) , //2015-08-08 21:20:31
2015-08-10 20:33:16 +00:00
Link = new Uri ( string . Format ( DownloadUrl , torrentId ) ) ,
Seeders = ParseUtil . CoerceInt ( infoMatch . Groups [ "seeders" ] . Value ) ,
Peers = ParseUtil . CoerceInt ( infoMatch . Groups [ "leechers" ] . Value ) ,
2017-01-02 14:45:01 +00:00
Grabs = ParseUtil . CoerceInt ( infoMatch . Groups [ "snatched" ] . Value ) ,
2015-08-10 20:33:16 +00:00
Size = ReleaseInfo . GetBytes ( infoMatch . Groups [ "size" ] . Value ) ,
2017-10-01 12:27:30 +00:00
Category = MapTrackerCatDescToNewznab ( infoMatch . Groups [ "cat" ] . Value )
2015-08-10 20:33:16 +00:00
} ;
release . Peers + = release . Seeders ;
releases . Add ( release ) ;
}
}
2016-12-17 12:39:52 +00:00
if ( query . IsTest | | ! string . IsNullOrWhiteSpace ( searchString ) )
2015-08-10 20:33:16 +00:00
{
2016-09-18 12:23:04 +00:00
// The TVChaos UK search requires an exact match of the search string.
// But it seems like they just send the unfiltered search to the SQL server in a like query (LIKE '%$searchstring%').
// So we replace any whitespace/special character with % to make the search more usable.
2017-04-15 08:45:10 +00:00
Regex ReplaceRegex = new Regex ( "[^a-zA-Z0-9]+" ) ;
2016-09-18 12:23:04 +00:00
searchString = ReplaceRegex . Replace ( searchString , "%" ) ;
2015-08-10 20:33:16 +00:00
var searchParams = new Dictionary < string , string > {
{ "do" , "search" } ,
{ "keywords" , searchString } ,
{ "search_type" , "t_name" } ,
{ "category" , "0" } ,
{ "include_dead_torrents" , "no" }
} ;
var searchPage = await PostDataWithCookiesAndRetry ( SearchUrl , searchParams ) ;
2017-04-15 08:45:10 +00:00
if ( searchPage . IsRedirect )
{
// re-login
await ApplyConfiguration ( null ) ;
searchPage = await PostDataWithCookiesAndRetry ( SearchUrl , searchParams ) ;
2016-12-17 12:57:40 +00:00
}
2015-08-10 20:33:16 +00:00
try
{
CQ dom = searchPage . Content ;
var rows = dom [ "#listtorrents tbody tr" ] ;
foreach ( var row in rows . Skip ( 1 ) )
{
var release = new ReleaseInfo ( ) ;
var qRow = row . Cq ( ) ;
release . Title = qRow . Find ( "td:eq(1) .tooltip-content div:eq(0)" ) . Text ( ) ;
if ( string . IsNullOrWhiteSpace ( release . Title ) )
continue ;
2017-01-02 14:45:01 +00:00
var tooltip = qRow . Find ( "div.tooltip-content" ) ;
var banner = tooltip . Find ( "img" ) ;
release . Description = tooltip . Text ( ) ;
if ( banner . Any ( ) )
release . BannerUrl = new Uri ( banner . Attr ( "src" ) ) ;
2015-08-10 20:33:16 +00:00
release . Guid = new Uri ( qRow . Find ( "td:eq(2) a" ) . Attr ( "href" ) ) ;
release . Link = release . Guid ;
release . Comments = new Uri ( qRow . Find ( "td:eq(1) .tooltip-target a" ) . Attr ( "href" ) ) ;
2018-04-11 16:36:22 +00:00
var qDate = qRow . Find ( "td:eq(1) div" ) . Last ( ) ;
var date = qDate . Text ( ) . Trim ( ) ;
if ( date . StartsWith ( "Pre Release Time:" ) ) // in some cases the pre time is shown
{
date = date . Replace ( "Pre Release Time:" , "" ) ;
date = date . Split ( new string [ ] { "Uploaded:" } , StringSplitOptions . None ) [ 0 ] ;
date = date . Trim ( ) ;
}
release . PublishDate = DateTime . ParseExact ( date , "dd-MM-yyyy H:mm" , CultureInfo . InvariantCulture ) ; //08-08-2015 12:51
2015-08-10 20:33:16 +00:00
release . Seeders = ParseUtil . CoerceInt ( qRow . Find ( "td:eq(6)" ) . Text ( ) ) ;
release . Peers = release . Seeders + ParseUtil . CoerceInt ( qRow . Find ( "td:eq(7)" ) . Text ( ) . Trim ( ) ) ;
release . Size = ReleaseInfo . GetBytes ( qRow . Find ( "td:eq(4)" ) . Text ( ) . Trim ( ) ) ;
var cat = row . Cq ( ) . Find ( "td:eq(0) a" ) . First ( ) . Attr ( "href" ) ;
var catSplit = cat . LastIndexOf ( '=' ) ;
if ( catSplit > - 1 )
cat = cat . Substring ( catSplit + 1 ) ;
release . Category = MapTrackerCatToNewznab ( cat ) ;
2017-04-15 08:45:10 +00:00
var grabs = qRow . Find ( "td:nth-child(6)" ) . Text ( ) ;
release . Grabs = ParseUtil . CoerceInt ( grabs ) ;
if ( qRow . Find ( "img[alt*=\"Free Torrent\"]" ) . Length > = 1 )
release . DownloadVolumeFactor = 0 ;
else
release . DownloadVolumeFactor = 1 ;
if ( qRow . Find ( "img[alt*=\"x2 Torrent\"]" ) . Length > = 1 )
release . UploadVolumeFactor = 2 ;
else
2016-10-27 07:35:31 +00:00
release . UploadVolumeFactor = 1 ;
2015-08-10 20:33:16 +00:00
releases . Add ( release ) ;
}
}
catch ( Exception ex )
{
OnParseError ( searchPage . Content , ex ) ;
}
}
return releases ;
}
}
}