2020-02-09 02:35:16 +00:00
using System ;
2015-04-19 21:56:20 +00:00
using System.Collections.Generic ;
2017-10-29 06:21:18 +00:00
using System.Collections.Specialized ;
2020-05-03 23:35:52 +00:00
using System.Diagnostics.CodeAnalysis ;
2015-04-19 21:56:20 +00:00
using System.Globalization ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
2020-03-14 18:16:03 +00:00
using System.Web ;
2020-02-28 21:42:08 +00:00
using AngleSharp.Html.Parser ;
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:21:18 +00:00
using Newtonsoft.Json.Linq ;
using NLog ;
2023-03-28 07:54:16 +00:00
using static Jackett . Common . Models . IndexerConfig . ConfigurationData ;
2015-04-19 21:56:20 +00:00
2018-03-10 08:05:56 +00:00
namespace Jackett.Common.Indexers
2015-04-19 21:56:20 +00:00
{
2020-05-03 23:35:52 +00:00
[ExcludeFromCodeCoverage]
2023-03-09 14:06:12 +00:00
public class BitHDTV : IndexerBase
2015-05-04 04:12:14 +00:00
{
2023-03-09 14:06:12 +00:00
public override string Id = > "bithdtv" ;
public override string Name = > "BIT-HDTV" ;
public override string Description = > "BIT-HDTV - Home of High Definition" ;
public override string SiteLink { get ; protected set ; } = "https://www.bit-hdtv.com/" ;
public override Encoding Encoding = > Encoding . GetEncoding ( "iso-8859-1" ) ;
public override string Language = > "en-US" ;
public override string Type = > "private" ;
2023-03-10 11:20:29 +00:00
public override TorznabCapabilities TorznabCaps = > SetCapabilities ( ) ;
2020-03-14 18:16:03 +00:00
private string SearchUrl = > SiteLink + "torrents.php" ;
2015-05-04 04:12:14 +00:00
2020-11-05 02:19:09 +00:00
private new ConfigurationDataCookie configData = > ( ConfigurationDataCookie ) base . configData ;
2015-08-03 21:38:45 +00:00
2020-12-11 22:14:21 +00:00
public BitHDTV ( IIndexerConfigurationService configService , WebClient w , Logger l , IProtectionService ps ,
ICacheService cs )
2023-03-10 11:20:29 +00:00
: base ( configService : configService ,
2020-05-03 22:48:42 +00:00
client : w ,
logger : l ,
p : ps ,
2020-12-11 22:14:21 +00:00
cacheService : cs ,
2020-11-05 02:19:09 +00:00
configData : new ConfigurationDataCookie ( "For best results, change the 'Torrents per page' setting to 100 in your profile." ) )
2015-05-04 04:12:14 +00:00
{
2023-03-28 07:54:16 +00:00
configData . AddDynamic ( "freeleech" , new BoolConfigurationItem ( "Search freeleech only" ) { Value = false } ) ;
2024-03-01 16:01:47 +00:00
configData . AddDynamic ( "Account Inactivity" , new DisplayInfoConfigurationItem ( "Account Inactivity" , "Inactive accounts are disabled after 90 days for User class, after 180 days for Power User class, after 270 days for Elite User & Insane User class. This doesn't apply to Veteran User class or above. Parking your account doubles the maximum inactive time. Only the login and browsing the site is considered activity." ) ) ;
2023-03-10 11:20:29 +00:00
}
private TorznabCapabilities SetCapabilities ( )
{
var caps = new TorznabCapabilities
{
TvSearchParams = new List < TvSearchParam >
{
TvSearchParam . Q , TvSearchParam . Season , TvSearchParam . Ep , TvSearchParam . ImdbId , TvSearchParam . Genre
} ,
MovieSearchParams = new List < MovieSearchParam >
{
MovieSearchParam . Q , MovieSearchParam . ImdbId , MovieSearchParam . Genre
}
} ;
caps . Categories . AddCategoryMapping ( 6 , TorznabCatType . AudioLossless , "HQ Audio" ) ;
caps . Categories . AddCategoryMapping ( 7 , TorznabCatType . Movies , "Movies" ) ;
caps . Categories . AddCategoryMapping ( 8 , TorznabCatType . AudioVideo , "Music Videos" ) ;
caps . Categories . AddCategoryMapping ( 9 , TorznabCatType . Other , "Other" ) ;
caps . Categories . AddCategoryMapping ( 10 , TorznabCatType . TV , "TV" ) ;
caps . Categories . AddCategoryMapping ( 12 , TorznabCatType . TV , "TV/Seasonpack" ) ;
caps . Categories . AddCategoryMapping ( 11 , TorznabCatType . XXX , "XXX" ) ;
return caps ;
2015-05-04 04:12:14 +00:00
}
2017-06-28 05:31:38 +00:00
public override async Task < IndexerConfigurationStatus > ApplyConfiguration ( JToken configJson )
2015-05-04 04:12:14 +00:00
{
2017-01-02 20:39:28 +00:00
LoadValuesFromJson ( configJson ) ;
2020-11-05 02:19:09 +00:00
CookieHeader = configData . Cookie . Value ;
try
2017-06-03 14:46:27 +00:00
{
2020-11-05 02:19:09 +00:00
var results = await PerformQuery ( new TorznabQuery ( ) ) ;
if ( ! results . Any ( ) )
throw new Exception ( "Found 0 results in the tracker" ) ;
2017-06-03 14:46:27 +00:00
2020-11-05 02:19:09 +00:00
IsConfigured = true ;
SaveConfig ( ) ;
return IndexerConfigurationStatus . Completed ;
}
catch ( Exception e )
2015-05-04 04:12:14 +00:00
{
2020-11-05 02:19:09 +00:00
IsConfigured = false ;
throw new Exception ( "Your cookie did not work: " + e . Message ) ;
}
2015-05-04 04:12:14 +00:00
}
2017-07-03 05:15:47 +00:00
protected override async Task < IEnumerable < ReleaseInfo > > PerformQuery ( TorznabQuery query )
2015-05-04 04:12:14 +00:00
{
2015-07-28 23:10:04 +00:00
var releases = new List < ReleaseInfo > ( ) ;
2020-03-15 21:49:31 +00:00
var qc = new NameValueCollection
{
2020-03-25 17:54:51 +00:00
{ "cat" , MapTorznabCapsToTrackers ( query , true ) . FirstIfSingleOrDefault ( "0" ) }
2020-03-15 21:49:31 +00:00
} ;
2023-03-28 07:54:16 +00:00
// free=4 green: (DL won't be counted, and UL will be counted double.)
// free=3 grey: (DL will be counted as normal, and UL will be counted double.)
// free=2 yellow: (DL won't be counted, and UL will be counted as normal.)
// free=1 normal: (DL and UL counted as normal.)
// free=0 (any)
if ( ( ( BoolConfigurationItem ) configData . GetDynamic ( "freeleech" ) ) . Value )
qc . Add ( "free" , "2" ) ;
2020-06-10 21:22:29 +00:00
var results = new List < WebResult > ( ) ;
2020-03-14 18:16:03 +00:00
var search = new UriBuilder ( SearchUrl ) ;
2022-07-30 21:36:13 +00:00
if ( query . IsGenreQuery )
{
qc . Add ( "search" , query . GetQueryString ( ) + " " + query . Genre ) ;
qc . Add ( "options" , "2" ) ; //Search Title and Genre
search . Query = qc . GetQueryString ( ) ;
results . Add ( await RequestWithCookiesAndRetryAsync ( search . ToString ( ) ) ) ;
}
else if ( query . IsImdbQuery )
2015-08-22 22:06:07 +00:00
{
2020-03-08 13:12:08 +00:00
qc . Add ( "search" , query . ImdbID ) ;
2020-03-14 18:16:03 +00:00
qc . Add ( "options" , "4" ) ; //Search URL field for IMDB link
search . Query = qc . GetQueryString ( ) ;
2020-06-11 15:09:27 +00:00
results . Add ( await RequestWithCookiesAndRetryAsync ( search . ToString ( ) ) ) ;
2020-03-14 18:16:03 +00:00
qc [ "Options" ] = "1" ; //Search Title and Description
search . Query = qc . GetQueryString ( ) ;
2020-06-11 15:09:27 +00:00
results . Add ( await RequestWithCookiesAndRetryAsync ( search . ToString ( ) ) ) ;
2015-08-22 22:06:07 +00:00
}
2020-03-14 18:16:03 +00:00
else
2020-03-08 13:12:08 +00:00
{
2020-03-14 18:16:03 +00:00
//Site handles empty string on search param. No need to check for IsNullOrEmpty()
2020-03-08 13:12:08 +00:00
qc . Add ( "search" , query . GetQueryString ( ) ) ;
2020-03-14 18:16:03 +00:00
qc . Add ( "options" , "0" ) ; //Search Title Only
search . Query = qc . GetQueryString ( ) ;
2020-06-11 15:09:27 +00:00
results . Add ( await RequestWithCookiesAndRetryAsync ( search . ToString ( ) ) ) ;
2020-03-08 13:12:08 +00:00
}
2015-08-22 22:06:07 +00:00
2020-03-14 18:16:03 +00:00
var parser = new HtmlParser ( ) ;
foreach ( var result in results )
try
2015-05-04 04:12:14 +00:00
{
2023-10-05 01:06:23 +00:00
using var dom = parser . ParseDocument ( result . ContentString ) ;
2022-08-30 19:14:28 +00:00
var tableBody = dom . QuerySelector ( "#torrents-index-table > #torrents-index-table-body" ) ;
if ( tableBody = = null ) // No results, so skip this search
2020-03-15 21:49:31 +00:00
continue ;
2022-08-30 19:14:28 +00:00
foreach ( var row in tableBody . Children )
2017-04-01 12:34:23 +00:00
{
var release = new ReleaseInfo ( ) ;
2020-02-28 21:42:08 +00:00
var qLink = row . Children [ 2 ] . QuerySelector ( "a" ) ;
2017-04-01 12:34:23 +00:00
release . MinimumRatio = 1 ;
2020-01-11 18:07:19 +00:00
release . MinimumSeedTime = 172800 ; // 48 hours
2022-06-20 07:49:38 +00:00
release . Title = qLink . GetAttribute ( "title" ) . Replace ( '.' , ' ' ) ;
2020-03-14 18:16:03 +00:00
var detailsLink = new Uri ( qLink . GetAttribute ( "href" ) ) ;
//Skip irrelevant and duplicate entries
if ( ! query . MatchQueryStringAND ( release . Title ) | | releases . Any ( r = > r . Guid = = detailsLink ) )
2017-04-01 12:34:23 +00:00
continue ;
2022-08-30 19:14:28 +00:00
2022-07-30 21:36:13 +00:00
var genres = row . QuerySelector ( "font.small" ) ? . TextContent ;
if ( ! string . IsNullOrEmpty ( genres ) )
{
2022-08-07 08:44:25 +00:00
genres = genres . Replace ( "[ " , "" ) . Replace ( " ]" , "" ) . Replace ( " / " , "," ) . Replace ( " | " , "," ) ;
2022-07-30 21:36:13 +00:00
release . Description = genres ;
2022-08-30 19:14:28 +00:00
release . Genres ? ? = new List < string > ( ) ;
2022-07-30 21:36:13 +00:00
release . Genres = release . Genres . Union ( genres . Split ( ',' ) ) . ToList ( ) ;
}
2020-03-14 18:16:03 +00:00
release . Files = ParseUtil . CoerceLong ( row . Children [ 3 ] . TextContent ) ;
release . Grabs = ParseUtil . CoerceLong ( row . Children [ 7 ] . TextContent ) ;
release . Guid = detailsLink ;
2020-11-08 02:11:27 +00:00
release . Details = release . Guid ;
2020-03-14 18:16:03 +00:00
release . Link = new Uri ( SiteLink + row . QuerySelector ( "a[href^=\"download.php\"]" ) . GetAttribute ( "href" ) ) ;
var catUrl = new Uri ( SiteLink + row . Children [ 1 ] . FirstElementChild . GetAttribute ( "href" ) ) ;
var catQuery = HttpUtility . ParseQueryString ( catUrl . Query ) ;
var catNum = catQuery [ "cat" ] ;
2017-04-01 12:34:23 +00:00
release . Category = MapTrackerCatToNewznab ( catNum ) ;
2020-02-28 21:42:08 +00:00
var dateString = row . Children [ 5 ] . TextContent . Trim ( ) ;
2017-06-08 08:34:53 +00:00
var pubDate = DateTime . ParseExact ( dateString , "yyyy-MM-ddHH:mm:ss" , CultureInfo . InvariantCulture ) ;
2017-04-01 12:34:23 +00:00
release . PublishDate = DateTime . SpecifyKind ( pubDate , DateTimeKind . Local ) ;
2020-02-28 21:42:08 +00:00
var sizeStr = row . Children [ 6 ] . TextContent ;
2023-02-24 23:22:13 +00:00
release . Size = ParseUtil . GetBytes ( sizeStr ) ;
2020-02-28 21:42:08 +00:00
release . Seeders = ParseUtil . CoerceInt ( row . Children [ 8 ] . TextContent . Trim ( ) ) ;
release . Peers = ParseUtil . CoerceInt ( row . Children [ 9 ] . TextContent . Trim ( ) ) + release . Seeders ;
2020-03-14 18:16:03 +00:00
switch ( row . GetAttribute ( "bgcolor" ) )
2017-04-01 12:34:23 +00:00
{
2023-03-28 07:54:16 +00:00
case "#DDDDDD" : // grey
2020-03-14 18:16:03 +00:00
release . DownloadVolumeFactor = 1 ;
release . UploadVolumeFactor = 2 ;
break ;
2023-03-28 07:54:16 +00:00
case "#FFFF99" : // yellow
2020-03-14 18:16:03 +00:00
release . DownloadVolumeFactor = 0 ;
release . UploadVolumeFactor = 1 ;
break ;
2023-03-28 07:54:16 +00:00
case "#CCFF99" : // green
2020-03-14 18:16:03 +00:00
release . DownloadVolumeFactor = 0 ;
release . UploadVolumeFactor = 2 ;
break ;
2023-03-28 07:54:16 +00:00
default : // normal
2020-03-14 18:16:03 +00:00
release . DownloadVolumeFactor = 1 ;
release . UploadVolumeFactor = 1 ;
break ;
2017-04-01 12:34:23 +00:00
}
2020-03-14 18:16:03 +00:00
2017-04-01 12:34:23 +00:00
releases . Add ( release ) ;
2016-12-06 14:19:23 +00:00
}
2015-05-04 04:12:14 +00:00
}
2020-03-14 18:16:03 +00:00
catch ( Exception ex )
{
2020-06-09 17:36:57 +00:00
OnParseError ( result . ContentString , ex ) ;
2020-03-14 18:16:03 +00:00
}
2015-05-04 04:12:14 +00:00
2015-07-28 23:10:04 +00:00
return releases ;
2015-05-04 04:12:14 +00:00
}
}
2015-04-19 21:56:20 +00:00
}