Fixed: (AlphaRatio/GreatPosterWall) Add freeleech only and exclude scene settings

pull/1356/head
Bogdan 2 years ago committed by Qstick
parent 906d09e162
commit c0b1675627

@ -9,7 +9,7 @@ using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Definitions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Test.Framework;

@ -9,7 +9,7 @@ using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Definitions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Test.Framework;

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration;
[Migration(027)]
public class alpharatio_greatposterwall_config_contract : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Update.Table("Indexers").Set(new { ConfigContract = "AlphaRatioSettings" }).Where(new { Implementation = "AlphaRatio" });
Update.Table("Indexers").Set(new { ConfigContract = "GreatPosterWallSettings" }).Where(new { Implementation = "GreatPosterWall" });
}
}

@ -1,85 +1,120 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions;
public class AlphaRatio : GazelleBase<AlphaRatioSettings>
{
public class AlphaRatio : Gazelle.Gazelle
public override string Name => "AlphaRatio";
public override string[] IndexerUrls => new[] { "https://alpharatio.cc/" };
public override string Description => "AlphaRatio(AR) is a Private Torrent Tracker for 0DAY / GENERAL";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public AlphaRatio(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
public override string Name => "AlphaRatio";
public override string[] IndexerUrls => new string[] { "https://alpharatio.cc/" };
public override string Description => "AlphaRatio(AR) is a Private Torrent Tracker for 0DAY / GENERAL";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
}
public AlphaRatio(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new AlphaRatioRequestGenerator(Settings, Capabilities, _httpClient, _logger);
}
public override IIndexerRequestGenerator GetRequestGenerator()
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
return new AlphaRatioRequestGenerator()
TvSearchParams = new List<TvSearchParam>
{
Settings = Settings,
HttpClient = _httpClient,
Logger = _logger,
Capabilities = Capabilities
};
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
}
};
MovieSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVSD, "TvSD");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVHD, "TvHD");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVUHD, "TvUHD");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVSD, "TvDVDRip");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVSD, "TvPackSD");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TVHD, "TvPackHD");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVUHD, "TvPackUHD");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.MoviesSD, "MovieSD");
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.MoviesHD, "MovieHD");
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.MoviesUHD, "MovieUHD");
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.MoviesSD, "MoviePackSD");
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.MoviesHD, "MoviePackHD");
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.MoviesUHD, "MoviePackUHD");
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.XXX, "MovieXXX");
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.MoviesBluRay, "Bluray");
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.TVAnime, "AnimeSD");
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.TVAnime, "AnimeHD");
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.PCGames, "GamesPC");
caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.ConsoleXBox, "GamesxBox");
caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.ConsolePS4, "GamesPS");
caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.ConsoleWii, "GamesNin");
caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.PC0day, "AppsWindows");
caps.Categories.AddCategoryMapping(23, NewznabStandardCategory.PCMac, "AppsMAC");
caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.PC0day, "AppsLinux");
caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.PCMobileOther, "AppsMobile");
caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.XXX, "0dayXXX");
caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.Books, "eBook");
caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.AudioAudiobook, "AudioBook");
caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.AudioOther, "Music");
caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.Other, "Misc");
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVSD, "TvSD");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVHD, "TvHD");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVUHD, "TvUHD");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVSD, "TvDVDRip");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVSD, "TvPackSD");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TVHD, "TvPackHD");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVUHD, "TvPackUHD");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.MoviesSD, "MovieSD");
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.MoviesHD, "MovieHD");
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.MoviesUHD, "MovieUHD");
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.MoviesSD, "MoviePackSD");
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.MoviesHD, "MoviePackHD");
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.MoviesUHD, "MoviePackUHD");
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.XXX, "MovieXXX");
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.MoviesBluRay, "Bluray");
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.TVAnime, "AnimeSD");
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.TVAnime, "AnimeHD");
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.PCGames, "GamesPC");
caps.Categories.AddCategoryMapping(19, NewznabStandardCategory.ConsoleXBox, "GamesxBox");
caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.ConsolePS4, "GamesPS");
caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.ConsoleWii, "GamesNin");
caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.PC0day, "AppsWindows");
caps.Categories.AddCategoryMapping(23, NewznabStandardCategory.PCMac, "AppsMAC");
caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.PC0day, "AppsLinux");
caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.PCMobileOther, "AppsMobile");
caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.XXX, "0dayXXX");
caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.Books, "eBook");
caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.AudioAudiobook, "AudioBook");
caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.AudioOther, "Music");
caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.Other, "Misc");
return caps;
}
return caps;
}
}
public class AlphaRatioRequestGenerator : GazelleRequestGenerator
{
protected override bool ImdbInTags => true;
private readonly AlphaRatioSettings _settings;
public class AlphaRatioRequestGenerator : Gazelle.GazelleRequestGenerator
public AlphaRatioRequestGenerator(AlphaRatioSettings settings,
IndexerCapabilities capabilities,
IIndexerHttpClient httpClient,
Logger logger)
: base(settings, capabilities, httpClient, logger)
{
protected override bool ImdbInTags => true;
_settings = settings;
}
protected override NameValueCollection GetBasicSearchParameters(string term, int[] categories)
{
var parameters = base.GetBasicSearchParameters(term, categories);
if (_settings.FreeleechOnly)
{
parameters.Set("freetorrent", "1");
}
if (_settings.ExcludeScene)
{
parameters.Set("scene", "0");
}
return parameters;
}
}
public class AlphaRatioSettings : GazelleSettings
{
[FieldDefinition(5, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
public bool FreeleechOnly { get; set; }
[FieldDefinition(6, Label = "Exclude Scene", Type = FieldType.Checkbox, HelpText = "Exclude Scene torrents from results")]
public bool ExcludeScene { get; set; }
}

@ -1,38 +1,39 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions;
public class BrokenStones : GazelleBase<GazelleSettings>
{
public class BrokenStones : Gazelle.Gazelle
{
public override string Name => "BrokenStones";
public override string[] IndexerUrls => new string[] { "https://brokenstones.club/" };
public override string Description => "Broken Stones is a Private site for MacOS and iOS APPS / GAMES";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override string Name => "BrokenStones";
public override string[] IndexerUrls => new[] { "https://brokenstones.club/" };
public override string Description => "Broken Stones is a Private site for MacOS and iOS APPS / GAMES";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public BrokenStones(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public BrokenStones(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
};
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities();
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.PCMac, "MacOS Apps");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PCMac, "MacOS Games");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.PCMobileiOS, "iOS Apps");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCMobileiOS, "iOS Games");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other, "Graphics");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.Audio, "Audio");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "Tutorials");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.Other, "Other");
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.PCMac, "MacOS Apps");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PCMac, "MacOS Games");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.PCMobileiOS, "iOS Apps");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCMobileiOS, "iOS Games");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other, "Graphics");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.Audio, "Audio");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "Tutorials");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.Other, "Other");
return caps;
}
return caps;
}
}

@ -1,37 +1,38 @@
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions;
public class CGPeers : GazelleBase<GazelleSettings>
{
public class CGPeers : Gazelle.Gazelle
{
public override string Name => "CGPeers";
public override string[] IndexerUrls => new string[] { "https://cgpeers.to/" };
public override string Description => "CGPeers is a Private Torrent Tracker for GRAPHICS SOFTWARE / TUTORIALS / ETC";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override string Name => "CGPeers";
public override string[] IndexerUrls => new[] { "https://cgpeers.to/" };
public override string Description => "CGPeers is a Private Torrent Tracker for GRAPHICS SOFTWARE / TUTORIALS / ETC";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public CGPeers(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public CGPeers(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
};
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities();
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.PCISO, "Full Applications");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC0day, "Plugins");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Other, "Tutorials");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.Other, "Models");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other, "Materials");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.OtherMisc, "Misc");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "GameDev");
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.PCISO, "Full Applications");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC0day, "Plugins");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Other, "Tutorials");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.Other, "Models");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Other, "Materials");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.OtherMisc, "Misc");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Other, "GameDev");
return caps;
}
return caps;
}
}

@ -1,117 +0,0 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Gazelle
{
public abstract class Gazelle : TorrentIndexerBase<GazelleSettings>
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string[] IndexerUrls => new string[] { "" };
protected virtual string LoginUrl => Settings.BaseUrl + "login.php";
public override bool SupportsRss => true;
public override bool SupportsSearch => true;
public override int PageSize => 50;
public override IndexerCapabilities Capabilities => SetCapabilities();
public Gazelle(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new GazelleRequestGenerator()
{
Settings = Settings,
HttpClient = _httpClient,
Logger = _logger,
Capabilities = Capabilities
};
}
public override IParseIndexerResponse GetParser()
{
return new GazelleParser(Settings, Capabilities);
}
protected virtual IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities();
return caps;
}
protected override async Task DoLogin()
{
var requestBuilder = new HttpRequestBuilder(LoginUrl)
{
LogResponseContent = true
};
requestBuilder.Method = HttpMethod.Post;
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
var cookies = Cookies;
Cookies = null;
var authLoginRequest = requestBuilder
.AddFormParameter("username", Settings.Username)
.AddFormParameter("password", Settings.Password)
.AddFormParameter("keeplogged", "1")
.SetHeader("Content-Type", "multipart/form-data")
.Accept(HttpAccept.Json)
.Build();
var response = await ExecuteAuth(authLoginRequest);
cookies = response.GetCookies();
UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30));
_logger.Debug("Gazelle authentication succeeded.");
}
public override async Task<byte[]> Download(Uri link)
{
var response = await base.Download(link);
if (response.Length >= 1
&& response[0] != 'd' // simple test for torrent vs HTML content
&& link.Query.Contains("usetoken=1"))
{
var html = Encoding.GetString(response);
if (html.Contains("You do not have any freeleech tokens left.")
|| html.Contains("You do not have enough freeleech tokens")
|| html.Contains("This torrent is too large.")
|| html.Contains("You cannot use tokens here"))
{
// download again with usetoken=0
var requestLinkNew = link.ToString().Replace("usetoken=1", "usetoken=0");
response = await base.Download(new Uri(requestLinkNew));
}
}
return response;
}
protected override bool CheckIfLoginNeeded(HttpResponse response)
{
if (response.HasHttpRedirect || (response.Content != null && response.Content.Contains("\"bad credentials\"")))
{
return true;
}
return false;
}
}
}

@ -1,96 +1,95 @@
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.Indexers.Gazelle
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public class GazelleArtist
{
public class GazelleArtist
{
public string Name { get; set; }
public string Id { get; set; }
public string Aliasid { get; set; }
}
public string Name { get; set; }
public string Id { get; set; }
public string Aliasid { get; set; }
}
public class GazelleTorrent
{
public int TorrentId { get; set; }
public int EditionId { get; set; }
public List<GazelleArtist> Artists { get; set; }
public bool Remastered { get; set; }
public string RemasterYear { get; set; }
public string RemasterTitle { get; set; }
public string Media { get; set; }
public string Encoding { get; set; }
public string Format { get; set; }
public bool HasLog { get; set; }
public int LogScore { get; set; }
public bool HasCue { get; set; }
public bool Scene { get; set; }
public bool VanityHouse { get; set; }
public int FileCount { get; set; }
public DateTime Time { get; set; }
public string Size { get; set; }
public int? Snatches { get; set; }
public string Seeders { get; set; }
public string Leechers { get; set; }
public string Category { get; set; }
public bool IsFreeLeech { get; set; }
public bool IsNeutralLeech { get; set; }
public bool IsPersonalFreeLeech { get; set; }
public bool CanUseToken { get; set; }
}
public class GazelleTorrent
{
public int TorrentId { get; set; }
public int EditionId { get; set; }
public List<GazelleArtist> Artists { get; set; }
public bool Remastered { get; set; }
public string RemasterYear { get; set; }
public string RemasterTitle { get; set; }
public string Media { get; set; }
public string Encoding { get; set; }
public string Format { get; set; }
public bool HasLog { get; set; }
public int LogScore { get; set; }
public bool HasCue { get; set; }
public bool Scene { get; set; }
public bool VanityHouse { get; set; }
public int FileCount { get; set; }
public DateTime Time { get; set; }
public string Size { get; set; }
public int? Snatches { get; set; }
public string Seeders { get; set; }
public string Leechers { get; set; }
public string Category { get; set; }
public bool IsFreeLeech { get; set; }
public bool IsNeutralLeech { get; set; }
public bool IsPersonalFreeLeech { get; set; }
public bool CanUseToken { get; set; }
}
public class GazelleRelease
{
public string GroupId { get; set; }
public string GroupName { get; set; }
public int TorrentId { get; set; }
public string Size { get; set; }
public int FileCount { get; set; }
public int? Snatches { get; set; }
public string Seeders { get; set; }
public string Leechers { get; set; }
public string Category { get; set; }
public string Artist { get; set; }
public string GroupYear { get; set; }
public string Cover { get; set; }
public List<string> Tags { get; set; }
public string ReleaseType { get; set; }
public int TotalLeechers { get; set; }
public int TotalSeeders { get; set; }
public int TotalSnatched { get; set; }
public long MaxSize { get; set; }
public string GroupTime { get; set; }
public List<GazelleTorrent> Torrents { get; set; }
public bool IsFreeLeech { get; set; }
public bool IsNeutralLeech { get; set; }
public bool IsPersonalFreeLeech { get; set; }
public bool CanUseToken { get; set; }
}
public class GazelleRelease
{
public string GroupId { get; set; }
public string GroupName { get; set; }
public int TorrentId { get; set; }
public string Size { get; set; }
public int FileCount { get; set; }
public int? Snatches { get; set; }
public string Seeders { get; set; }
public string Leechers { get; set; }
public string Category { get; set; }
public string Artist { get; set; }
public string GroupYear { get; set; }
public string Cover { get; set; }
public List<string> Tags { get; set; }
public string ReleaseType { get; set; }
public int TotalLeechers { get; set; }
public int TotalSeeders { get; set; }
public int TotalSnatched { get; set; }
public long MaxSize { get; set; }
public string GroupTime { get; set; }
public List<GazelleTorrent> Torrents { get; set; }
public bool IsFreeLeech { get; set; }
public bool IsNeutralLeech { get; set; }
public bool IsPersonalFreeLeech { get; set; }
public bool CanUseToken { get; set; }
}
public class GazelleResponse
{
public string Status { get; set; }
public GazelleBrowseResponse Response { get; set; }
}
public class GazelleResponse
{
public string Status { get; set; }
public GazelleBrowseResponse Response { get; set; }
}
public class GazelleBrowseResponse
{
public List<GazelleRelease> Results { get; set; }
public string CurrentPage { get; set; }
public string Pages { get; set; }
}
public class GazelleBrowseResponse
{
public List<GazelleRelease> Results { get; set; }
public string CurrentPage { get; set; }
public string Pages { get; set; }
}
public class GazelleAuthResponse
{
public string Status { get; set; }
public GazelleIndexResponse Response { get; set; }
}
public class GazelleAuthResponse
{
public string Status { get; set; }
public GazelleIndexResponse Response { get; set; }
}
public class GazelleIndexResponse
{
public string Username { get; set; }
public string Id { get; set; }
public string Authkey { get; set; }
public string Passkey { get; set; }
}
public class GazelleIndexResponse
{
public string Username { get; set; }
public string Id { get; set; }
public string Authkey { get; set; }
public string Passkey { get; set; }
}

@ -0,0 +1,105 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public abstract class GazelleBase<TSettings> : TorrentIndexerBase<TSettings>
where TSettings : GazelleSettings, new()
{
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override string[] IndexerUrls => new[] { "" };
protected virtual string LoginUrl => Settings.BaseUrl + "login.php";
public override bool SupportsRss => true;
public override bool SupportsSearch => true;
public override int PageSize => 50;
public override IndexerCapabilities Capabilities => SetCapabilities();
public GazelleBase(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new GazelleRequestGenerator(Settings, Capabilities, _httpClient, _logger);
}
public override IParseIndexerResponse GetParser()
{
return new GazelleParser(Settings, Capabilities);
}
protected virtual IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities();
return caps;
}
protected override async Task DoLogin()
{
var requestBuilder = new HttpRequestBuilder(LoginUrl)
{
LogResponseContent = true,
Method = HttpMethod.Post
};
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
var cookies = Cookies;
Cookies = null;
var authLoginRequest = requestBuilder
.AddFormParameter("username", Settings.Username)
.AddFormParameter("password", Settings.Password)
.AddFormParameter("keeplogged", "1")
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
.Accept(HttpAccept.Json)
.Build();
var response = await ExecuteAuth(authLoginRequest);
cookies = response.GetCookies();
UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30));
_logger.Debug("Gazelle authentication succeeded.");
}
public override async Task<byte[]> Download(Uri link)
{
var response = await base.Download(link);
if (response.Length >= 1
&& response[0] != 'd' // simple test for torrent vs HTML content
&& link.Query.Contains("usetoken=1"))
{
var html = Encoding.GetString(response);
if (html.Contains("You do not have any freeleech tokens left.")
|| html.Contains("You do not have enough freeleech tokens")
|| html.Contains("This torrent is too large.")
|| html.Contains("You cannot use tokens here"))
{
// download again with usetoken=0
var requestLinkNew = link.ToString().Replace("usetoken=1", "usetoken=0");
response = await base.Download(new Uri(requestLinkNew));
}
}
return response;
}
protected override bool CheckIfLoginNeeded(HttpResponse response)
{
return response.HasHttpRedirect || (response.Content != null && response.Content.Contains("\"bad credentials\""));
}
}

@ -1,9 +1,8 @@
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Gazelle
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public class GazelleInfo : TorrentInfo
{
public class GazelleInfo : TorrentInfo
{
public bool? Scene { get; set; }
}
public bool? Scene { get; set; }
}

@ -8,177 +8,174 @@ using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Gazelle
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public class GazelleParser : IParseIndexerResponse
{
public class GazelleParser : IParseIndexerResponse
protected GazelleSettings Settings { get; }
protected IndexerCapabilities Capabilities { get; }
public GazelleParser(GazelleSettings settings, IndexerCapabilities capabilities)
{
protected readonly GazelleSettings _settings;
protected readonly IndexerCapabilities _capabilities;
Settings = settings;
Capabilities = capabilities;
}
public GazelleParser(GazelleSettings settings, IndexerCapabilities capabilities)
{
_settings = settings;
_capabilities = capabilities;
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
public virtual IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
public virtual IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
var torrentInfos = new List<ReleaseInfo>();
// Remove cookie cache
CookiesUpdater(null, null);
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
}
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
}
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
{
// Remove cookie cache
CookiesUpdater(null, null);
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
{
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
}
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
}
var jsonResponse = new HttpResponse<GazelleResponse>(indexerResponse.HttpResponse);
if (jsonResponse.Resource.Status != "success" ||
jsonResponse.Resource.Status.IsNullOrWhiteSpace() ||
jsonResponse.Resource.Response == null)
{
return torrentInfos;
}
var jsonResponse = new HttpResponse<GazelleResponse>(indexerResponse.HttpResponse);
if (jsonResponse.Resource.Status != "success" ||
jsonResponse.Resource.Status.IsNullOrWhiteSpace() ||
jsonResponse.Resource.Response == null)
{
return torrentInfos;
}
foreach (var result in jsonResponse.Resource.Response.Results)
{
var posterUrl = GetPosterUrl(result.Cover);
foreach (var result in jsonResponse.Resource.Response.Results)
if (result.Torrents != null)
{
var posterUrl = GetPosterUrl(result.Cover);
if (result.Torrents != null)
foreach (var torrent in result.Torrents)
{
foreach (var torrent in result.Torrents)
var id = torrent.TorrentId;
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
if (torrent.HasCue)
{
var id = torrent.TorrentId;
var artist = WebUtility.HtmlDecode(result.Artist);
var album = WebUtility.HtmlDecode(result.GroupName);
var title = $"{result.Artist} - {result.GroupName} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
if (torrent.HasCue)
{
title += " [Cue]";
}
var infoUrl = GetInfoUrl(result.GroupId, id);
var release = new GazelleInfo()
{
Guid = infoUrl,
Title = WebUtility.HtmlDecode(title),
Container = torrent.Encoding,
Files = torrent.FileCount,
Grabs = torrent.Snatches,
Codec = torrent.Format,
Size = long.Parse(torrent.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = infoUrl,
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.Time.ToUniversalTime(),
Scene = torrent.Scene,
PosterUrl = posterUrl,
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
};
var category = torrent.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
torrentInfos.Add(release);
title += " [Cue]";
}
}
else
{
var id = result.TorrentId;
var groupName = WebUtility.HtmlDecode(result.GroupName);
var infoUrl = GetInfoUrl(result.GroupId, id);
var release = new GazelleInfo()
var release = new GazelleInfo
{
Guid = infoUrl,
Title = groupName,
Size = long.Parse(result.Size),
Title = WebUtility.HtmlDecode(title),
Container = torrent.Encoding,
Files = torrent.FileCount,
Grabs = torrent.Snatches,
Codec = torrent.Format,
Size = long.Parse(torrent.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = infoUrl,
Seeders = int.Parse(result.Seeders),
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime),
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.Time.ToUniversalTime(),
Scene = torrent.Scene,
PosterUrl = posterUrl,
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
};
var category = result.Category;
var category = torrent.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
release.Categories = Capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
release.Categories = Capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
torrentInfos.Add(release);
}
}
else
{
var id = result.TorrentId;
var groupName = WebUtility.HtmlDecode(result.GroupName);
var infoUrl = GetInfoUrl(result.GroupId, id);
var release = new GazelleInfo
{
Guid = infoUrl,
Title = groupName,
Size = long.Parse(result.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = infoUrl,
Seeders = int.Parse(result.Seeders),
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = long.TryParse(result.GroupTime, out var num) ? DateTimeOffset.FromUnixTimeSeconds(num).UtcDateTime : DateTimeUtil.FromFuzzyTime((string)result.GroupTime),
PosterUrl = posterUrl,
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
};
var category = result.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = Capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = Capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
// order by date
return
torrentInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
torrentInfos.Add(release);
}
}
protected virtual string GetDownloadUrl(int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("usetoken", _settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("id", torrentId);
// order by date
return
torrentInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
}
return url.FullUri;
}
protected virtual string GetDownloadUrl(int torrentId)
{
var url = new HttpUri(Settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("usetoken", Settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("id", torrentId);
protected virtual string GetPosterUrl(string cover)
{
if (!string.IsNullOrEmpty(cover))
{
return cover.StartsWith("http") ?
new HttpUri(cover).FullUri :
new HttpUri(_settings.BaseUrl).CombinePath(cover).FullUri;
}
return url.FullUri;
}
return null;
protected virtual string GetPosterUrl(string cover)
{
if (!string.IsNullOrEmpty(cover))
{
return cover.StartsWith("http") ?
new HttpUri(cover).FullUri :
new HttpUri(Settings.BaseUrl).CombinePath(cover).FullUri;
}
protected virtual string GetInfoUrl(string groupId, int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("id", groupId)
.AddQueryParam("torrentid", torrentId);
return null;
}
return url.FullUri;
}
protected virtual string GetInfoUrl(string groupId, int torrentId)
{
var url = new HttpUri(Settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("id", groupId)
.AddQueryParam("torrentid", torrentId);
return url.FullUri;
}
}

@ -1,154 +1,153 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.Gazelle
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public class GazelleRequestGenerator : IIndexerRequestGenerator
{
public class GazelleRequestGenerator : IIndexerRequestGenerator
public GazelleSettings Settings { get; }
public IndexerCapabilities Capabilities { get; }
public IIndexerHttpClient HttpClient { get; }
public Logger Logger { get; }
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
protected virtual bool ImdbInTags => false;
protected virtual string ApiUrl => Settings.BaseUrl + "ajax.php";
public GazelleRequestGenerator(GazelleSettings settings, IndexerCapabilities capabilities, IIndexerHttpClient httpClient, Logger logger)
{
Settings = settings;
Capabilities = capabilities;
HttpClient = httpClient;
Logger = logger;
}
protected IEnumerable<IndexerRequest> GetRequest(NameValueCollection parameters)
{
public GazelleSettings Settings { get; set; }
var request = new IndexerRequest($"{ApiUrl}?{parameters.GetQueryString()}", HttpAccept.Json)
{
HttpRequest =
{
AllowAutoRedirect = false
}
};
public IDictionary<string, string> AuthCookieCache { get; set; }
public IIndexerHttpClient HttpClient { get; set; }
public IndexerCapabilities Capabilities { get; set; }
public Logger Logger { get; set; }
yield return request;
}
protected virtual string APIUrl => Settings.BaseUrl + "ajax.php";
protected virtual bool ImdbInTags => false;
public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
public virtual IndexerPageableRequestChain GetRecentRequests()
if (searchCriteria.ImdbId != null)
{
var pageableRequests = new IndexerPageableRequestChain();
parameters.Set(ImdbInTags ? "taglist" : "cataloguenumber", searchCriteria.FullImdbId);
}
pageableRequests.Add(GetRequest(null));
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
}
return pageableRequests;
}
protected IEnumerable<IndexerRequest> GetRequest(string searchParameters)
{
var request =
new IndexerRequest(
$"{APIUrl}?{searchParameters}",
HttpAccept.Json);
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
request.HttpRequest.AllowAutoRedirect = false;
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
yield return request;
if (searchCriteria.Artist.IsNotNullOrWhiteSpace())
{
parameters.Set("artistname", searchCriteria.Artist);
}
protected string GetBasicSearchParameters(string searchTerm, int[] categories)
if (searchCriteria.Album.IsNotNullOrWhiteSpace())
{
var searchString = GetSearchTerm(searchTerm);
var parameters = "action=browse&order_by=time&order_way=desc";
parameters.Set("groupname", searchCriteria.Album);
}
if (!string.IsNullOrWhiteSpace(searchString))
{
parameters += string.Format("&searchstr={0}", searchString.Replace(".", " "));
}
if (searchCriteria.Label.IsNotNullOrWhiteSpace())
{
parameters.Set("recordlabel", searchCriteria.Label);
}
if (categories != null)
{
foreach (var cat in Capabilities.Categories.MapTorznabCapsToTrackers(categories))
{
parameters += string.Format("&filter_cat[{0}]=1", cat);
}
}
pageableRequests.Add(GetRequest(parameters));
return parameters;
}
return pageableRequests;
}
public virtual IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
if (searchCriteria.ImdbId != null)
{
if (ImdbInTags)
{
parameters += string.Format("&taglist={0}", searchCriteria.FullImdbId);
}
else
{
parameters += string.Format("&cataloguenumber={0}", searchCriteria.FullImdbId);
}
}
var parameters = GetBasicSearchParameters(searchCriteria.SanitizedTvSearchString, searchCriteria.Categories);
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
if (searchCriteria.ImdbId != null)
{
parameters.Set(ImdbInTags ? "taglist" : "cataloguenumber", searchCriteria.FullImdbId);
}
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
{
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
pageableRequests.Add(GetRequest(parameters));
if (searchCriteria.Artist != null)
{
parameters += string.Format("&artistname={0}", searchCriteria.Artist);
}
return pageableRequests;
}
if (searchCriteria.Label != null)
{
parameters += string.Format("&recordlabel={0}", searchCriteria.Label);
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
if (searchCriteria.Album != null)
{
parameters += string.Format("&groupname={0}", searchCriteria.Album);
}
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
pageableRequests.Add(GetRequest(parameters));
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
}
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
{
var parameters = GetBasicSearchParameters(searchCriteria.SanitizedTvSearchString, searchCriteria.Categories);
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
if (searchCriteria.ImdbId != null)
{
if (ImdbInTags)
{
parameters += string.Format("&taglist={0}", searchCriteria.FullImdbId);
}
else
{
parameters += string.Format("&cataloguenumber={0}", searchCriteria.FullImdbId);
}
}
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
pageableRequests.Add(GetRequest(parameters));
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
}
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
// hook to adjust the search term
protected virtual string GetSearchTerm(string term) => term;
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
}
protected virtual NameValueCollection GetBasicSearchParameters(string term, int[] categories)
{
var parameters = new NameValueCollection
{
{ "action", "browse" },
{ "order_by", "time" },
{ "order_way", "desc" }
};
// hook to adjust the search term
protected virtual string GetSearchTerm(string term) => term;
var searchTerm = GetSearchTerm(term);
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
if (searchTerm.IsNotNullOrWhiteSpace())
{
var parameters = GetBasicSearchParameters(searchCriteria.SearchTerm, searchCriteria.Categories);
parameters.Set("searchstr", searchTerm.Replace(".", " "));
}
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
if (categories != null)
{
var queryCats = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
if (queryCats.Any())
{
queryCats.ForEach(cat => parameters.Set($"filter_cat[{cat}]", "1"));
}
}
return parameters;
}
}

@ -2,25 +2,24 @@ using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers.Settings;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Gazelle
namespace NzbDrone.Core.Indexers.Definitions.Gazelle;
public class GazelleSettingsValidator : UserPassBaseSettingsValidator<GazelleSettings>
{
public class GazelleSettingsValidator : UserPassBaseSettingsValidator<GazelleSettings>
{
}
}
public class GazelleSettings : UserPassTorrentBaseSettings
{
private static readonly GazelleSettingsValidator Validator = new ();
public class GazelleSettings : UserPassTorrentBaseSettings
{
private static readonly GazelleSettingsValidator Validator = new ();
public string AuthKey;
public string PassKey;
public string AuthKey { get; set; }
public string PassKey { get; set; }
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use freeleech tokens when available")]
public bool UseFreeleechToken { get; set; }
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use freeleech tokens when available")]
public bool UseFreeleechToken { get; set; }
public override NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
public override NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}

@ -1,14 +1,16 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
@ -16,27 +18,25 @@ using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Definitions;
public class GreatPosterWall : Gazelle.Gazelle
public class GreatPosterWall : GazelleBase<GreatPosterWallSettings>
{
public override string Name => "GreatPosterWall";
public override string[] IndexerUrls => new[] { "https://greatposterwall.com/" };
public override string Description => "GreatPosterWall (GPW) is a CHINESE Private site for MOVIES";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public GreatPosterWall(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
public GreatPosterWall(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new GreatPosterWallRequestGenerator
{
Settings = Settings,
HttpClient = _httpClient,
Logger = _logger,
Capabilities = Capabilities
};
return new GreatPosterWallRequestGenerator(Settings, Capabilities, _httpClient, _logger);
}
public override IParseIndexerResponse GetParser()
@ -63,6 +63,16 @@ public class GreatPosterWall : Gazelle.Gazelle
public class GreatPosterWallRequestGenerator : GazelleRequestGenerator
{
protected override bool ImdbInTags => false;
private readonly GreatPosterWallSettings _settings;
public GreatPosterWallRequestGenerator(GreatPosterWallSettings settings,
IndexerCapabilities capabilities,
IIndexerHttpClient httpClient,
Logger logger)
: base(settings, capabilities, httpClient, logger)
{
_settings = settings;
}
public override IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
@ -70,20 +80,35 @@ public class GreatPosterWallRequestGenerator : GazelleRequestGenerator
if (searchCriteria.ImdbId != null)
{
parameters += string.Format("&searchstr={0}", searchCriteria.FullImdbId);
parameters.Set("searchstr", searchCriteria.FullImdbId);
}
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetRequest(parameters));
return pageableRequests;
}
protected override NameValueCollection GetBasicSearchParameters(string term, int[] categories)
{
var parameters = base.GetBasicSearchParameters(term, categories);
if (_settings.FreeleechOnly)
{
parameters.Set("freetorrent", "1");
}
return parameters;
}
}
public class GreatPosterWallParser : GazelleParser
{
public GreatPosterWallParser(GazelleSettings settings, IndexerCapabilities capabilities)
private readonly GreatPosterWallSettings _settings;
public GreatPosterWallParser(GreatPosterWallSettings settings, IndexerCapabilities capabilities)
: base(settings, capabilities)
{
_settings = settings;
}
public override IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
@ -190,6 +215,12 @@ public class GreatPosterWallParser : GazelleParser
}
}
public class GreatPosterWallSettings : GazelleSettings
{
[FieldDefinition(5, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
public bool FreeleechOnly { get; set; }
}
public class GreatPosterWallResponse
{
[JsonProperty("status")]

@ -1,54 +1,57 @@
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions;
public class NotWhatCD : GazelleBase<GazelleSettings>
{
public class NotWhatCD : Gazelle.Gazelle
{
public override string Name => "notwhat.cd";
public override string[] IndexerUrls => new string[] { "https://notwhat.cd/" };
public override string Description => "NotWhat.CD (NWCD) is a private Music tracker that arised after the former (WCD) shut down.";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override string Name => "notwhat.cd";
public override string[] IndexerUrls => new[] { "https://notwhat.cd/" };
public override string Description => "NotWhat.CD (NWCD) is a private Music tracker that arised after the former (WCD) shut down.";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public NotWhatCD(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public NotWhatCD(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
var caps = new IndexerCapabilities
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Label, MusicSearchParam.Year
},
BookSearchParams = new List<BookSearchParam>
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};
BookSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio, "Music");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC, "Applications");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Books, "E-Books");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook, "Audiobooks");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies, "E-Learning Videos");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TV, "Comedy");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Books, "Comics");
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Audio, "Music");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC, "Applications");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Books, "E-Books");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.AudioAudiobook, "Audiobooks");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies, "E-Learning Videos");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.TV, "Comedy");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Books, "Comics");
return caps;
}
return caps;
}
}

@ -10,8 +10,8 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Indexers.Settings;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;

@ -10,8 +10,8 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Indexers.Settings;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;

@ -7,227 +7,229 @@ using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.Gazelle;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Gazelle;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Definitions
namespace NzbDrone.Core.Indexers.Definitions;
public class SecretCinema : GazelleBase<GazelleSettings>
{
public class SecretCinema : Gazelle.Gazelle
public override string Name => "Secret Cinema";
public override string[] IndexerUrls => new[] { "https://secret-cinema.pw/" };
public override string Description => "A tracker for rare movies.";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override IndexerCapabilities Capabilities => SetCapabilities();
public SecretCinema(IIndexerHttpClient httpClient,
IEventAggregator eventAggregator,
IIndexerStatusService indexerStatusService,
IConfigService configService,
Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
public override string Name => "Secret Cinema";
public override string[] IndexerUrls => new string[] { "https://secret-cinema.pw/" };
public override string Description => "A tracker for rare movies.";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override IndexerCapabilities Capabilities => SetCapabilities();
public SecretCinema(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
}
public override IParseIndexerResponse GetParser()
{
return new SecretCinemaParser(Settings, Capabilities);
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
}
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album, MusicSearchParam.Label, MusicSearchParam.Year
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.Audio, "Music");
return caps;
}
}
public class SecretCinemaParser : IParseIndexerResponse
{
private readonly GazelleSettings _settings;
private readonly IndexerCapabilities _capabilities;
public SecretCinemaParser(GazelleSettings settings, IndexerCapabilities capabilities)
{
_settings = settings;
_capabilities = capabilities;
}
public override IParseIndexerResponse GetParser()
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
return new SecretCinemaParser(Settings, Capabilities);
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
}
protected override IndexerCapabilities SetCapabilities()
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
{
var caps = new IndexerCapabilities
{
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q, MusicSearchParam.Album, MusicSearchParam.Artist, MusicSearchParam.Label, MusicSearchParam.Year
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.Audio, "Music");
return caps;
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
}
public class SecretCinemaParser : IParseIndexerResponse
var jsonResponse = new HttpResponse<GazelleResponse>(indexerResponse.HttpResponse);
if (jsonResponse.Resource.Status != "success" ||
jsonResponse.Resource.Status.IsNullOrWhiteSpace() ||
jsonResponse.Resource.Response == null)
{
protected readonly GazelleSettings _settings;
protected readonly IndexerCapabilities _capabilities;
return torrentInfos;
}
public SecretCinemaParser(GazelleSettings settings, IndexerCapabilities capabilities)
foreach (var result in jsonResponse.Resource.Response.Results)
{
if (result.Torrents != null)
{
_settings = settings;
_capabilities = capabilities;
}
foreach (var torrent in result.Torrents)
{
var id = torrent.TorrentId;
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
// in SC movies, artist=director and GroupName=title
var artist = WebUtility.HtmlDecode(result.Artist);
var title = WebUtility.HtmlDecode(result.GroupName);
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
var release = new GazelleInfo
{
Guid = string.Format("SecretCinema-{0}", id),
Title = title,
Container = torrent.Encoding,
Files = torrent.FileCount,
Grabs = torrent.Snatches,
Codec = torrent.Format,
Size = long.Parse(torrent.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = GetInfoUrl(result.GroupId, id),
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.Time.ToUniversalTime(),
Scene = torrent.Scene,
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
};
var category = torrent.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
// Remove cookie cache
CookiesUpdater(null, null);
if (IsAnyMovieCategory(release.Categories))
{
// Remove director from title
// SC API returns no more useful information than this
release.Title = $"{title} ({result.GroupYear}) {torrent.Media}";
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
}
// Replace media formats with standards
release.Title = Regex.Replace(release.Title, "BDMV", "COMPLETE BLURAY", RegexOptions.IgnoreCase);
release.Title = Regex.Replace(release.Title, "SD", "DVDRip", RegexOptions.IgnoreCase);
}
else
{
// SC API currently doesn't return anything but title.
release.Title = $"{artist} - {title} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
}
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
{
// Remove cookie cache
CookiesUpdater(null, null);
if (torrent.HasCue)
{
release.Title += " [Cue]";
}
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
torrentInfos.Add(release);
}
}
else
{
var id = result.TorrentId;
var groupName = WebUtility.HtmlDecode(result.GroupName);
var jsonResponse = new HttpResponse<GazelleResponse>(indexerResponse.HttpResponse);
if (jsonResponse.Resource.Status != "success" ||
jsonResponse.Resource.Status.IsNullOrWhiteSpace() ||
jsonResponse.Resource.Response == null)
var release = new GazelleInfo
{
return torrentInfos;
Guid = string.Format("SecretCinema-{0}", id),
Title = groupName,
Size = long.Parse(result.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = GetInfoUrl(result.GroupId, id),
Seeders = int.Parse(result.Seeders),
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
};
var category = result.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
}
foreach (var result in jsonResponse.Resource.Response.Results)
else
{
if (result.Torrents != null)
{
foreach (var torrent in result.Torrents)
{
var id = torrent.TorrentId;
// in SC movies, artist=director and GroupName=title
var artist = WebUtility.HtmlDecode(result.Artist);
var title = WebUtility.HtmlDecode(result.GroupName);
var release = new GazelleInfo()
{
Guid = string.Format("SecretCinema-{0}", id),
Title = title,
Container = torrent.Encoding,
Files = torrent.FileCount,
Grabs = torrent.Snatches,
Codec = torrent.Format,
Size = long.Parse(torrent.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = GetInfoUrl(result.GroupId, id),
Seeders = int.Parse(torrent.Seeders),
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
PublishDate = torrent.Time.ToUniversalTime(),
Scene = torrent.Scene,
DownloadVolumeFactor = torrent.IsFreeLeech || torrent.IsNeutralLeech || torrent.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = torrent.IsNeutralLeech ? 0 : 1
};
var category = torrent.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
if (IsAnyMovieCategory(release.Categories))
{
// Remove director from title
// SC API returns no more useful information than this
release.Title = $"{title} ({result.GroupYear}) {torrent.Media}";
// Replace media formats with standards
release.Title = Regex.Replace(release.Title, "BDMV", "COMPLETE BLURAY", RegexOptions.IgnoreCase);
release.Title = Regex.Replace(release.Title, "SD", "DVDRip", RegexOptions.IgnoreCase);
}
else
{
// SC API currently doesn't return anything but title.
release.Title = $"{artist} - {title} ({result.GroupYear}) [{torrent.Format} {torrent.Encoding}] [{torrent.Media}]";
}
if (torrent.HasCue)
{
release.Title += " [Cue]";
}
torrentInfos.Add(release);
}
}
else
{
var id = result.TorrentId;
var groupName = WebUtility.HtmlDecode(result.GroupName);
var release = new GazelleInfo()
{
Guid = string.Format("SecretCinema-{0}", id),
Title = groupName,
Size = long.Parse(result.Size),
DownloadUrl = GetDownloadUrl(id),
InfoUrl = GetInfoUrl(result.GroupId, id),
Seeders = int.Parse(result.Seeders),
Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders),
Files = result.FileCount,
Grabs = result.Snatches,
PublishDate = DateTimeOffset.FromUnixTimeSeconds(ParseUtil.CoerceLong(result.GroupTime)).UtcDateTime,
DownloadVolumeFactor = result.IsFreeLeech || result.IsNeutralLeech || result.IsPersonalFreeLeech ? 0 : 1,
UploadVolumeFactor = result.IsNeutralLeech ? 0 : 1
};
var category = result.Category;
if (category == null || category.Contains("Select Category"))
{
release.Categories = _capabilities.Categories.MapTrackerCatToNewznab("1");
}
else
{
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
torrentInfos.Add(release);
}
release.Categories = _capabilities.Categories.MapTrackerCatDescToNewznab(category);
}
// order by date
return
torrentInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
torrentInfos.Add(release);
}
}
private bool IsAnyMovieCategory(ICollection<IndexerCategory> category)
{
return category.Contains(NewznabStandardCategory.Movies)
|| NewznabStandardCategory.Movies.SubCategories.Any(subCat => category.Contains(subCat));
}
// order by date
return
torrentInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
}
protected virtual string GetDownloadUrl(int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("useToken", _settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("id", torrentId);
private bool IsAnyMovieCategory(ICollection<IndexerCategory> category)
{
return category.Contains(NewznabStandardCategory.Movies) || NewznabStandardCategory.Movies.SubCategories.Any(subCat => category.Contains(subCat));
}
return url.FullUri;
}
protected virtual string GetDownloadUrl(int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("useToken", _settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("id", torrentId);
private string GetInfoUrl(string groupId, int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("id", groupId)
.AddQueryParam("torrentid", torrentId);
return url.FullUri;
}
return url.FullUri;
}
}
private string GetInfoUrl(string groupId, int torrentId)
{
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("id", groupId)
.AddQueryParam("torrentid", torrentId);
return url.FullUri;
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

Loading…
Cancel
Save