@ -5,11 +5,9 @@ using System.Globalization;
using System.Linq ;
using System.Net.Http ;
using System.Text ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
using AngleSharp.Dom ;
using AngleSharp.Html.Parser ;
using FluentValidation ;
using NLog ;
using NzbDrone.Common.Extensions ;
using NzbDrone.Common.Http ;
@ -21,386 +19,331 @@ using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events ;
using NzbDrone.Core.Parser ;
using NzbDrone.Core.Parser.Model ;
using NzbDrone.Core.ThingiProvider ;
using NzbDrone.Core.Validation ;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions ;
public class NorBits : TorrentIndexerBase < NorBitsSettings >
{
public class NorBits : TorrentIndexerBase < NorBitsSettings >
public override string Name = > "NorBits" ;
public override string [ ] IndexerUrls = > new [ ] { "https://norbits.net/" } ;
public override string Description = > "NorBits is a Norwegian Private site for MOVIES / TV / GENERAL" ;
public override string Language = > "nb-NO" ;
public override Encoding Encoding = > Encoding . GetEncoding ( "iso-8859-1" ) ;
public override DownloadProtocol Protocol = > DownloadProtocol . Torrent ;
public override IndexerPrivacy Privacy = > IndexerPrivacy . Private ;
public override IndexerCapabilities Capabilities = > SetCapabilities ( ) ;
public NorBits ( IIndexerHttpClient httpClient , IEventAggregator eventAggregator , IIndexerStatusService indexerStatusService , IConfigService configService , Logger logger )
: base ( httpClient , eventAggregator , indexerStatusService , configService , logger )
{
public override string Name = > "NorBits" ;
public override string [ ] IndexerUrls = > new string [ ] { "https://norbits.net/" } ;
public override string Description = > "NorBits is a Norwegian Private site for MOVIES / TV / GENERAL" ;
public override string Language = > "nb-NO" ;
public override Encoding Encoding = > Encoding . GetEncoding ( "iso-8859-1" ) ;
public override DownloadProtocol Protocol = > DownloadProtocol . Torrent ;
public override IndexerPrivacy Privacy = > IndexerPrivacy . Private ;
public override IndexerCapabilities Capabilities = > SetCapabilities ( ) ;
public NorBits ( IIndexerHttpClient httpClient , IEventAggregator eventAggregator , IIndexerStatusService indexerStatusService , IConfigService configService , Logger logger )
: base ( httpClient , eventAggregator , indexerStatusService , configService , logger )
{
}
}
public override IIndexerRequestGenerator GetRequestGenerator ( )
{
return new NorBitsRequestGenerator ( ) { Settings = Settings , Capabilities = Capabilities } ;
}
public override IIndexerRequestGenerator GetRequestGenerator ( )
{
return new NorBitsRequestGenerator ( Settings , Capabilities ) ;
}
public override IParseIndexerResponse GetParser ( )
{
return new NorBitsParser ( Settings , Capabilities . Categories ) ;
}
public override IParseIndexerResponse GetParser ( )
{
return new NorBitsParser ( Settings , Capabilities . Categories ) ;
}
protected override async Task DoLogin ( )
protected override async Task DoLogin ( )
{
var requestBuilder = new HttpRequestBuilder ( Settings . BaseUrl )
{
var requestBuilder = new HttpRequestBuilder ( Settings . BaseUrl )
{
LogResponseContent = true ,
AllowAutoRedirect = true
} ;
LogResponseContent = true ,
AllowAutoRedirect = true
} ;
var indexPage = await ExecuteAuth ( requestBuilder . Build ( ) ) ;
var indexPage = await ExecuteAuth ( requestBuilder . Build ( ) ) ;
var loginUrl = string . Format ( "{0}/{1}" , Settings . BaseUrl . TrimEnd ( '/' ) , "login.php" ) ;
var loginUrl = $"{Settings.BaseUrl.TrimEnd('/')}/login.php" ;
var requestBuilder2 = new HttpRequestBuilder ( loginUrl )
{
LogResponseContent = true ,
AllowAutoRedirect = true
} ;
var requestBuilder2 = new HttpRequestBuilder ( loginUrl )
{
LogResponseContent = true ,
AllowAutoRedirect = true
} ;
var authLoginRequest = requestBuilder2
. SetCookies ( indexPage . GetCookies ( ) )
. Build ( ) ;
var authLoginRequest = requestBuilder2
. SetCookies ( indexPage . GetCookies ( ) )
. Build ( ) ;
// Get login page -- (not used, but simulation needed by tracker security's checks)
await ExecuteAuth ( authLoginRequest ) ;
// Get login page -- (not used, but simulation needed by tracker security's checks)
await ExecuteAuth ( authLoginRequest ) ;
var requestBuilder3 = new HttpRequestBuilder ( string . Format ( "{0}/{1}" , Settings . BaseUrl . TrimEnd ( '/' ) , "takelogin.php" ) )
{
LogResponseContent = true ,
AllowAutoRedirect = true ,
Method = HttpMethod . Post
} ;
var authLoginCheckRequest = requestBuilder3
. AddFormParameter ( "username" , Settings . Username )
. AddFormParameter ( "password" , Settings . Password )
. SetCookies ( indexPage . GetCookies ( ) )
. SetHeader ( "Referer" , loginUrl )
. Build ( ) ;
var requestBuilder3 = new HttpRequestBuilder ( $"{Settings.BaseUrl.TrimEnd('/')}/takelogin.php" )
{
LogResponseContent = true ,
AllowAutoRedirect = true ,
Method = HttpMethod . Post
} ;
var loginResponse = await ExecuteAuth ( authLoginCheckRequest ) ;
var authLoginCheckRequest = requestBuilder3
. AddFormParameter ( "username" , Settings . Username )
. AddFormParameter ( "password" , Settings . Password )
. SetCookies ( indexPage . GetCookies ( ) )
. SetHeader ( "Referer" , loginUrl )
. Build ( ) ;
if ( ! loginResponse . GetCookies ( ) . ContainsKey ( "uid" ) )
{
// Default error message
var message = "Error during attempt !" ;
var loginResponse = await ExecuteAuth ( authLoginCheckRequest ) ;
// Oops, unable to login
_logger . Info ( "NorBits - Login failed: " + message , "error" ) ;
if ( ! loginResponse . GetCookies ( ) . ContainsKey ( "uid" ) )
{
throw new IndexerAuthException ( "Login failed" ) ;
}
throw new IndexerAuthException ( message ) ;
}
var cookies = loginResponse . GetCookies ( ) ;
UpdateCookies ( cookies , DateTime . Now + TimeSpan . FromDays ( 30 ) ) ;
var cookies = loginResponse . GetCookies ( ) ;
UpdateCookies ( cookies , DateTime . Now + TimeSpan . FromDays ( 30 ) ) ;
_logger . Debug ( "Authentication succeeded." ) ;
}
_logger . Debug ( "NorBits authentication succeeded." ) ;
}
protected override bool CheckIfLoginNeeded ( HttpResponse httpResponse )
{
return ! httpResponse . Content . Contains ( "logout.php" ) ;
}
protected override bool CheckIfLoginNeeded ( HttpResponse httpResponse )
private IndexerCapabilities SetCapabilities ( )
{
var caps = new IndexerCapabilities
{
if ( ! httpResponse . Content . Contains ( "logout.php" ) )
TvSearchParams = new List < TvSearchParam >
{
TvSearchParam . Q , TvSearchParam . Season , TvSearchParam . Ep
} ,
MovieSearchParams = new List < MovieSearchParam >
{
MovieSearchParam . Q , MovieSearchParam . ImdbId
} ,
MusicSearchParams = new List < MusicSearchParam >
{
return true ;
MusicSearchParam . Q
} ,
BookSearchParams = new List < BookSearchParam >
{
BookSearchParam . Q
}
} ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=49" , NewznabStandardCategory . MoviesUHD , "Filmer - UHD-2160p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=19" , NewznabStandardCategory . MoviesHD , "Filmer - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=20" , NewznabStandardCategory . MoviesHD , "Filmer - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=22" , NewznabStandardCategory . MoviesSD , "Filmer - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=49" , NewznabStandardCategory . TVUHD , "TV - UHD-2160p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=19" , NewznabStandardCategory . TVHD , "TV - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=20" , NewznabStandardCategory . TVHD , "TV - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=22" , NewznabStandardCategory . TVSD , "TV - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=3" , NewznabStandardCategory . PC , "Programmer" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=4" , NewznabStandardCategory . Console , "Spill" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=42" , NewznabStandardCategory . AudioMP3 , "Musikk - 192" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=43" , NewznabStandardCategory . AudioMP3 , "Musikk - 256" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=44" , NewznabStandardCategory . AudioMP3 , "Musikk - 320" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=45" , NewznabStandardCategory . AudioMP3 , "Musikk - VBR" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=46" , NewznabStandardCategory . AudioLossless , "Musikk - Lossless" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=6" , NewznabStandardCategory . Books , "Tidsskrift" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=7" , NewznabStandardCategory . AudioAudiobook , "Lydbøker" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=19" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=20" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=22" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=40" , NewznabStandardCategory . AudioOther , "Podcasts" ) ;
return caps ;
}
}
return false ;
}
private IndexerCapabilities SetCapabilities ( )
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List < TvSearchParam >
{
TvSearchParam . Q , TvSearchParam . Season , TvSearchParam . Ep
} ,
MovieSearchParams = new List < MovieSearchParam >
{
MovieSearchParam . Q , MovieSearchParam . ImdbId
} ,
MusicSearchParams = new List < MusicSearchParam >
{
MusicSearchParam . Q
} ,
BookSearchParams = new List < BookSearchParam >
{
BookSearchParam . Q
}
} ;
public class NorBitsRequestGenerator : IIndexerRequestGenerator
{
private readonly NorBitsSettings _settings ;
private readonly IndexerCapabilities _capabilities ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=49" , NewznabStandardCategory . MoviesUHD , "Filmer - UHD-2160p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=19" , NewznabStandardCategory . MoviesHD , "Filmer - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=20" , NewznabStandardCategory . MoviesHD , "Filmer - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=1&sub2_cat[]=22" , NewznabStandardCategory . MoviesSD , "Filmer - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=49" , NewznabStandardCategory . TVUHD , "TV - UHD-2160p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=19" , NewznabStandardCategory . TVHD , "TV - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=20" , NewznabStandardCategory . TVHD , "TV - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=2&sub2_cat[]=22" , NewznabStandardCategory . TVSD , "TV - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=3" , NewznabStandardCategory . PC , "Programmer" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=4" , NewznabStandardCategory . Console , "Spill" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=42" , NewznabStandardCategory . AudioMP3 , "Musikk - 192" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=43" , NewznabStandardCategory . AudioMP3 , "Musikk - 256" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=44" , NewznabStandardCategory . AudioMP3 , "Musikk - 320" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=45" , NewznabStandardCategory . AudioMP3 , "Musikk - VBR" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=5&sub2_cat[]=46" , NewznabStandardCategory . AudioLossless , "Musikk - Lossless" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=6" , NewznabStandardCategory . Books , "Tidsskrift" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=7" , NewznabStandardCategory . AudioAudiobook , "Lydbøker" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=19" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - HD-1080p/i" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=20" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - HD-720p" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=8&sub2_cat[]=22" , NewznabStandardCategory . AudioVideo , "Musikkvideoer - SD" ) ;
caps . Categories . AddCategoryMapping ( "main_cat[]=40" , NewznabStandardCategory . AudioOther , "Podcasts" ) ;
return caps ;
}
public NorBitsRequestGenerator ( NorBitsSettings settings , IndexerCapabilities capabilities )
{
_settings = settings ;
_capabilities = capabilities ;
}
p ublic class NorBitsRequestGenerator : IIndexerRequestGenerator
private IEnumerable < IndexerRequest > GetPagedRequests ( string term , int [ ] categories , string imdbId = null )
{
public NorBitsSettings Settings { get ; set ; }
public IndexerCapabilities Capabilities { get ; set ; }
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/browse.php" ;
public NorBitsRequestGenerator ( )
var parameters = new NameValueCollection
{
}
{ "incldead" , "1" } ,
{ "fullsearch" , _settings . UseFullSearch ? "1" : "0" } ,
{ "scenerelease" , "0" }
} ;
private IEnumerable < IndexerRequest > GetPagedRequests ( string term , int [ ] categories , string imdbId = null )
{
var searchUrl = string . Format ( "{0}/browse.php" , Settings . BaseUrl . TrimEnd ( '/' ) ) ;
var searchTerm = "search=" ;
var parameters = new NameValueCollection ( ) ;
var categoriesList = Capabilities . Categories . MapTorznabCapsToTrackers ( categories ) ;
var searchterm = term ;
// Building our tracker query
parameters . Add ( "incldead" , "1" ) ;
parameters. Add ( "fullsearch" , Settings . UseFullSearch ? "1" : "0" ) ;
parameters . Add ( "scenerelease" , "0" ) ;
if ( ! string . IsNullOrWhiteSpace ( imdbId ) )
{
searchTerm = "imdbsearch=" + imdbId ;
}
else if ( ! string . IsNullOrWhiteSpace ( term ) )
{
searchTerm = "search=" + term . UrlEncode ( Encoding . GetEncoding ( 28591 ) ) ;
}
// If search term provided
if ( ! string . IsNullOrWhiteSpace ( imdbId ) )
{
searchterm = "imdbsearch=" + imdbId ;
}
else if ( ! string . IsNullOrWhiteSpace ( term ) )
{
searchterm = "search=" + term . UrlEncode ( Encoding . GetEncoding ( 28591 ) ) ;
}
else
{
// Showing all torrents (just for output function)
searchterm = "search=" ;
}
searchUrl + = "?" + searchTerm + "&" + parameters . GetQueryString ( ) ;
var catQryStr = "" ;
var categoriesList = _capabilities . Categories . MapTorznabCapsToTrackers ( categories ) ;
if ( categoriesList . Any ( ) )
{
searchUrl + = "&" + string . Join ( "&" , categoriesList ) ;
}
foreach ( var cat in categoriesList )
{
catQryStr + = "&" + cat ;
}
var request = new IndexerRequest ( searchUrl , HttpAccept . Html ) ;
// Building our query
searchUrl + = "?" + searchterm + "&" + parameters . GetQueryString ( ) + "&" + catQryStr ;
yield return request ;
}
var request = new IndexerRequest ( searchUrl , HttpAccept . Html ) ;
public IndexerPageableRequestChain GetSearchRequests ( MovieSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
yield return request ;
}
pageableRequests . Add ( GetPagedRequests ( $"{searchCriteria.SanitizedSearchTerm}" , searchCriteria . Categories , searchCriteria . FullImdbId ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( MovieSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( string . Format ( "{0}" , searchCriteria . SanitizedSearchTerm ) , searchCriteria . Categories , searchCriteria . FullImdbId ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( MusicSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( $"{searchCriteria.SanitizedSearchTerm}" , searchCriteria . Categories ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( MusicSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( string . Format ( "{0}" , searchCriteria . SanitizedSearchTerm ) , searchCriteria . Categories ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( TvSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( $"{searchCriteria.SanitizedTvSearchString}" , searchCriteria . Categories , searchCriteria . FullImdbId ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( TvSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( string . Format ( "{0}" , searchCriteria . SanitizedTvSearchString ) , searchCriteria . Categories , searchCriteria . FullImdbId ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( BookSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( $"{searchCriteria.SanitizedSearchTerm}" , searchCriteria . Categories ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( BookSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( string . Format ( "{0}" , searchCriteria . SanitizedSearchTerm ) , searchCriteria . Categories ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( BasicSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( $"{searchCriteria.SanitizedSearchTerm}" , searchCriteria . Categories ) ) ;
public IndexerPageableRequestChain GetSearchRequests ( BasicSearchCriteria searchCriteria )
{
var pageableRequests = new IndexerPageableRequestChain ( ) ;
return pageableRequests ;
}
pageableRequests . Add ( GetPagedRequests ( string . Format ( "{0}" , searchCriteria . SanitizedSearchTerm ) , searchCriteria . Categories ) ) ;
public Func < IDictionary < string , string > > GetCookies { get ; set ; }
public Action < IDictionary < string , string > , DateTime ? > CookiesUpdater { get ; set ; }
}
return pageableRequests ;
}
public class NorBitsParser : IParseIndexerResponse
{
private readonly NorBitsSettings _settings ;
private readonly IndexerCapabilitiesCategories _categories ;
public Func < IDictionary < string , string > > GetCookies { get ; set ; }
public Action < IDictionary < string , string > , DateTime ? > CookiesUpdater { get ; set ; }
public NorBitsParser ( NorBitsSettings settings , IndexerCapabilitiesCategories categories )
{
_settings = settings ;
_categories = categories ;
}
public class NorBitsParser : IParseIndexerResponse
public IList < ReleaseInfo > ParseResponse ( IndexerResponse indexerResponse )
{
private readonly NorBitsSettings _settings ;
private readonly IndexerCapabilitiesCategories _categories ;
var releaseInfos = new List < ReleaseInfo > ( ) ;
public NorBitsParser ( NorBitsSettings settings , IndexerCapabilitiesCategories categories )
{
_settings = settings ;
_categories = categories ;
}
var parser = new HtmlParser ( ) ;
var dom = parser . ParseDocument ( indexerResponse . Content ) ;
public IList < ReleaseInfo > ParseResponse ( IndexerResponse indexerResponse )
var rows = dom . QuerySelectorAll ( "#torrentTable > tbody > tr" ) . Skip ( 1 ) . ToCollection ( ) ;
foreach ( var row in rows )
{
var torrentInfos = new List < ReleaseInfo > ( ) ;
var link = _settings . BaseUrl + row . QuerySelector ( "td:nth-of-type(2) > a[href*=\"download.php?id=\"]" ) ? . GetAttribute ( "href" ) . TrimStart ( '/' ) ;
var qDetails = row . QuerySelector ( "td:nth-of-type(2) > a[href*=\"details.php?id=\"]" ) ;
var title = qDetails ? . GetAttribute ( "title" ) . Trim ( ) ;
var details = _settings . BaseUrl + qDetails ? . GetAttribute ( "href" ) . TrimStart ( '/' ) ;
var parser = new HtmlParser ( ) ;
var dom = parser . ParseDocument ( indexerResponse . Content ) ;
var mainCategory = row . QuerySelector ( "td:nth-of-type(1) > div > a[href*=\"main_cat[]\"]" ) ? . GetAttribute ( "href" ) ? . Split ( '?' ) . Last ( ) ;
var secondCategory = row . QuerySelector ( "td:nth-of-type(1) > div > a[href*=\"sub2_cat[]\"]" ) ? . GetAttribute ( "href" ) ? . Split ( '?' ) . Last ( ) ;
var firstPageRows = dom . QuerySelectorAll ( "#torrentTable > tbody > tr" ) . Skip ( 1 ) . ToCollection ( ) ;
var categoryList = new [ ] { mainCategory , secondCategory } ;
var cat = string . Join ( "&" , categoryList . Where ( c = > ! string . IsNullOrWhiteSpace ( c ) ) ) ;
// If pagination available
int nbResults ;
var seeders = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(9)" ) . TextContent ) ;
var leechers = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(10)" ) . TextContent ) ;
// Check if we have a minimum of one result
if ( firstPageRows ? . Length > = 1 )
var release = new TorrentInfo
{
// Retrieve total count on our alone page
nbResults = firstPageRows . Count ( ) ;
}
else
Guid = details ,
InfoUrl = details ,
DownloadUrl = link ,
Title = title ,
Categories = _categories . MapTrackerCatToNewznab ( cat ) ,
Size = ParseUtil . GetBytes ( row . QuerySelector ( "td:nth-of-type(7)" ) ? . TextContent ) ,
Files = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(3) > a" ) ? . TextContent . Trim ( ) ) ,
Grabs = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(8)" ) ? . FirstChild ? . TextContent . Trim ( ) ) ,
Seeders = seeders ,
Peers = seeders + leechers ,
PublishDate = DateTime . ParseExact ( row . QuerySelector ( "td:nth-of-type(5)" ) ? . TextContent . Trim ( ) , "yyyy-MM-ddHH:mm:ss" , CultureInfo . InvariantCulture ) ,
DownloadVolumeFactor = 1 ,
UploadVolumeFactor = 1 ,
MinimumRatio = 1 ,
MinimumSeedTime = 172800 // 48 hours
} ;
var genres = row . QuerySelector ( "span.genres" ) ? . TextContent ;
if ( ! string . IsNullOrEmpty ( genres ) )
{
// No result found for this query
return torrentInfos ;
genres = genres . Trim ( ) . Replace ( "\xA0" , " " ) . Replace ( "(" , "" ) . Replace ( ")" , "" ) . Replace ( " | " , "," ) ;
release . Description = genres ;
release . Genres = genres . Split ( ',' , StringSplitOptions . TrimEntries | StringSplitOptions . RemoveEmptyEntries ) . ToList ( ) ;
}
var torrentDetailsUrl = _settings . BaseUrl + "details.php?id={id}" ;
var torrentDownloadUrl = _settings . BaseUrl + "download.php?id={id}&passkey={passkey}" ;
var imdbLink = row . QuerySelector ( "a[href*=\"imdb.com/title/tt\"]" ) ? . GetAttribute ( "href" ) ;
release . ImdbId = ParseUtil . GetImdbID ( imdbLink ) ? ? 0 ;
// Loop on results
foreach ( var row in firstPageRows )
if ( row . QuerySelector ( "img[title=\"100% freeleech\"]" ) ! = null )
{
release . DownloadVolumeFactor = 0 ;
}
else if ( row . QuerySelector ( "img[title=\"Halfleech\"]" ) ! = null )
{
var id = row . QuerySelector ( "td:nth-of-type(2) > a:nth-of-type(1)" ) . GetAttribute ( "href" ) . Split ( '=' ) . Last ( ) ; // ID
var name = row . QuerySelector ( "td:nth-of-type(2) > a:nth-of-type(1)" ) . GetAttribute ( "title" ) ; // Release Name
var categoryName = row . QuerySelector ( "td:nth-of-type(1) > div > a:nth-of-type(1)" ) . GetAttribute ( "title" ) ; // Category
var mainCat = row . QuerySelector ( "td:nth-of-type(1) > div > a:nth-of-type(1)" ) . GetAttribute ( "href" ) . Split ( '?' ) . Last ( ) ;
var qSubCat2 = row . QuerySelector ( "td:nth-of-type(1) > div > a[href^=\"/browse.php?sub2_cat[]=\"]" ) ;
var cat = mainCat ;
if ( qSubCat2 ! = null )
{
cat + = '&' + qSubCat2 . GetAttribute ( "href" ) . Split ( '?' ) . Last ( ) ;
}
var seeders = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(9)" ) . TextContent ) ; // Seeders
var leechers = ParseUtil . CoerceInt ( row . QuerySelector ( "td:nth-of-type(10)" ) . TextContent ) ; // Leechers
var regexObj = new Regex ( @"[^\d]" ) ; // Completed
var completed2 = row . QuerySelector ( "td:nth-of-type(8)" ) . TextContent ;
var completed = ParseUtil . CoerceInt ( regexObj . Replace ( completed2 , "" ) ) ;
var qFiles = row . QuerySelector ( "td:nth-of-type(3) > a" ) ; // Files
var files = qFiles ! = null ? ParseUtil . CoerceInt ( Regex . Match ( qFiles . TextContent , @"\d+" ) . Value ) : 1 ;
var humanSize = row . QuerySelector ( "td:nth-of-type(7)" ) . TextContent . ToLowerInvariant ( ) ; // Size
var size = ParseUtil . GetBytes ( humanSize ) ; // Date
var dateTimeOrig = row . QuerySelector ( "td:nth-of-type(5)" ) . TextContent ;
var dateTime = Regex . Replace ( dateTimeOrig , @"<[^>]+>| " , "" ) . Trim ( ) ;
var date = DateTime . ParseExact ( dateTime , "yyyy-MM-ddHH:mm:ss" , CultureInfo . InvariantCulture , DateTimeStyles . AssumeUniversal ) . ToLocalTime ( ) ;
var details = new Uri ( torrentDetailsUrl . Replace ( "{id}" , id . ToString ( ) ) ) ; // Description Link
var passkey = row . QuerySelector ( "td:nth-of-type(2) > a:nth-of-type(2)" ) . GetAttribute ( "href" ) ; // Download Link
var key = Regex . Match ( passkey , "(?<=passkey\\=)([a-zA-z0-9]*)" ) ;
var downloadLink = new Uri ( torrentDownloadUrl . Replace ( "{id}" , id . ToString ( ) ) . Replace ( "{passkey}" , key . ToString ( ) ) ) ;
// Building release infos
var release = new TorrentInfo
{
Categories = _categories . MapTrackerCatToNewznab ( cat ) ,
Title = name ,
Seeders = seeders ,
Peers = seeders + leechers ,
PublishDate = date ,
Size = size ,
Files = files ,
Grabs = completed ,
Guid = details . AbsoluteUri ,
InfoUrl = details . AbsoluteUri ,
DownloadUrl = downloadLink . AbsoluteUri ,
MinimumRatio = 1 ,
MinimumSeedTime = 172800 // 48 hours
} ;
var genres = row . QuerySelector ( "span.genres" ) ? . TextContent ;
if ( ! string . IsNullOrEmpty ( genres ) )
{
release . Description = genres ;
}
// IMDB
var imdbLink = row . QuerySelector ( "a[href*=\"imdb.com/title/tt\"]" ) ? . GetAttribute ( "href" ) ;
release . ImdbId = ParseUtil . GetImdbID ( imdbLink ) ? ? 0 ;
if ( row . QuerySelector ( "img[title=\"100% freeleech\"]" ) ! = null )
{
release . DownloadVolumeFactor = 0 ;
}
else if ( row . QuerySelector ( "img[title=\"Halfleech\"]" ) ! = null )
{
release . DownloadVolumeFactor = 0.5 ;
}
else if ( row . QuerySelector ( "img[title=\"90% Freeleech\"]" ) ! = null )
{
release . DownloadVolumeFactor = 0.1 ;
}
else
{
release . DownloadVolumeFactor = 1 ;
}
release . UploadVolumeFactor = 1 ;
torrentInfos . Add ( release ) ;
release . DownloadVolumeFactor = 0.5 ;
}
else if ( row . QuerySelector ( "img[title=\"90% Freeleech\"]" ) ! = null )
{
release . DownloadVolumeFactor = 0.1 ;
}
return torrentInfos . ToArray ( ) ;
releaseInfos . Add ( release ) ;
}
public Action < IDictionary < string , string > , DateTime ? > CookiesUpdater { get ; set ; }
return releaseInfos . ToArray ( ) ;
}
public class NorBitsSettings : UserPassTorrentBaseSettings
{
public NorBitsSettings ( )
{
}
public Action < IDictionary < string , string > , DateTime ? > CookiesUpdater { get ; set ; }
}
[FieldDefinition(4, Label = "Use Full Search", HelpText = "Use Full Search from Site", Type = FieldType.Checkbox)]
public bool UseFullSearch { get ; set ; }
public class NorBitsSettings : UserPassTorrentBaseSettings
{
public NorBitsSettings ( )
{
UseFullSearch = false ;
}
[FieldDefinition(4, Label = "Use Full Search", HelpText = "Use Full Search from Site", Type = FieldType.Checkbox)]
public bool UseFullSearch { get ; set ; }
}