animebytes: add search by year and rate limit 1req/10s (#14224)

This commit is contained in:
Bogdan
2023-04-06 03:47:04 +03:00
committed by GitHub
parent 7ba1abd2b2
commit 8d6f9d55ae
2 changed files with 298 additions and 252 deletions

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jackett.Common.Extensions;
using Jackett.Common.Models; using Jackett.Common.Models;
using Jackett.Common.Models.IndexerConfig.Bespoke; using Jackett.Common.Models.IndexerConfig.Bespoke;
using Jackett.Common.Services.Interfaces; using Jackett.Common.Services.Interfaces;
@@ -40,6 +41,8 @@ namespace Jackett.Common.Indexers
private bool AddFileNameTitles => ConfigData.AddFileNameTitles.Value; private bool AddFileNameTitles => ConfigData.AddFileNameTitles.Value;
private bool FilterSeasonEpisode => ConfigData.FilterSeasonEpisode.Value; private bool FilterSeasonEpisode => ConfigData.FilterSeasonEpisode.Value;
private static Regex YearRegex => new Regex(@"\b((?:19|20)\d{2})$", RegexOptions.Compiled);
private ConfigurationDataAnimeBytes ConfigData => (ConfigurationDataAnimeBytes)configData; private ConfigurationDataAnimeBytes ConfigData => (ConfigurationDataAnimeBytes)configData;
public AnimeBytes(IIndexerConfigurationService configService, WebClient client, Logger l, public AnimeBytes(IIndexerConfigurationService configService, WebClient client, Logger l,
@@ -51,7 +54,10 @@ namespace Jackett.Common.Indexers
cacheService: cs, cacheService: cs,
configData: new ConfigurationDataAnimeBytes("Note: Go to AnimeBytes site and open your account settings. Go to 'Account' tab, move cursor over black part near 'Passkey' and copy its value. Your username is case sensitive.")) configData: new ConfigurationDataAnimeBytes("Note: Go to AnimeBytes site and open your account settings. Go to 'Account' tab, move cursor over black part near 'Passkey' and copy its value. Your username is case sensitive."))
{ {
webclient.EmulateBrowser = false; // Animebytes doesn't like fake user agents (issue #1535) // Animebytes doesn't like fake user agents (issue #1535)
webclient.EmulateBrowser = false;
// requestDelay for API Limit (1 request per 10 seconds)
webclient.requestDelay = 10;
} }
private TorznabCapabilities SetCapabilities() private TorznabCapabilities SetCapabilities()
@@ -113,27 +119,45 @@ namespace Jackett.Common.Indexers
return IndexerConfigurationStatus.Completed; return IndexerConfigurationStatus.Completed;
} }
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var releases = new List<ReleaseInfo>();
releases.AddRange(await GetResults(query, "anime", StripEpisodeNumber(query.SanitizedSearchTerm.Trim())));
if (ContainsMusicCategories(query.Categories))
releases.AddRange(await GetResults(query, "music", query.SanitizedSearchTerm.Trim()));
return releases
.OrderByDescending(o => o.PublishDate)
.ToArray();
}
private string StripEpisodeNumber(string term) private string StripEpisodeNumber(string term)
{ {
// Tracer does not support searching with episode number so strip it if we have one // Tracer does not support searching with episode number so strip it if we have one
term = Regex.Replace(term, @"\W(\dx)?\d?\d$", string.Empty); term = Regex.Replace(term, @"\W(\dx)?\d?\d$", string.Empty);
term = Regex.Replace(term, @"\W(S\d\d?E)?\d?\d$", string.Empty); term = Regex.Replace(term, @"\W(S\d\d?E)?\d?\d$", string.Empty);
term = Regex.Replace(term, @"\W\d+$", string.Empty); term = Regex.Replace(term, @"\W\d+$", string.Empty);
return term;
return term.Trim();
} }
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query) private static int? ParseYearFromSearchTerm(string term)
{ {
var releases = new List<ReleaseInfo>(); if (term.IsNullOrWhiteSpace())
{
return null;
}
if (ContainsMusicCategories(query.Categories)) var yearMatch = YearRegex.Match(term);
releases.AddRange(await GetResults(query, "music", query.SanitizedSearchTerm));
releases.AddRange( if (!yearMatch.Success)
await GetResults(query, "anime", StripEpisodeNumber(query.SanitizedSearchTerm)) {
); return null;
}
return releases.ToArray(); return ParseUtil.CoerceInt(yearMatch.Groups[1].Value);
} }
private bool ContainsMusicCategories(int[] categories) private bool ContainsMusicCategories(int[] categories)
@@ -154,20 +178,35 @@ namespace Jackett.Common.Indexers
{ {
var releases = new List<ReleaseInfo>(); var releases = new List<ReleaseInfo>();
var queryCollection = new NameValueCollection var parameters = new NameValueCollection
{ {
{ "username", ConfigData.Username.Value }, { "username", ConfigData.Username.Value },
{ "torrent_pass", ConfigData.Passkey.Value }, { "torrent_pass", ConfigData.Passkey.Value },
{ "sort", "grouptime" },
{ "way", "desc" },
{ "type", searchType }, { "type", searchType },
{ "limit", searchTerm.IsNotNullOrWhiteSpace() ? "50" : "15" },
{ "searchstr", searchTerm } { "searchstr", searchTerm }
}; };
var queryCats = MapTorznabCapsToTrackers(query); if (ConfigData.SearchByYear.Value)
if (queryCats.Count > 0) {
foreach (var cat in queryCats) var searchYear = ParseYearFromSearchTerm(query.SanitizedSearchTerm.Trim());
queryCollection.Add(cat, "1");
var queryUrl = ScrapeUrl + "?" + queryCollection.GetQueryString(); if (searchYear > 0)
{
parameters.Set("year", searchYear.ToString());
}
}
var queryCats = MapTorznabCapsToTrackers(query);
if (queryCats.Any())
{
queryCats.ForEach(cat => parameters.Set(cat, "1"));
}
var searchUrl = ScrapeUrl + "?" + parameters.GetQueryString();
// Check cache first so we don't query the server for each episode when searching for each episode in a series. // Check cache first so we don't query the server for each episode when searching for each episode in a series.
lock (cache) lock (cache)
@@ -175,15 +214,19 @@ namespace Jackett.Common.Indexers
// Remove old cache items // Remove old cache items
CleanCache(); CleanCache();
var cachedResult = cache.Where(i => i.Query == queryUrl).FirstOrDefault(); var cachedResult = cache.FirstOrDefault(i => i.Query == searchUrl);
if (cachedResult != null) if (cachedResult != null)
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray(); {
return cachedResult.Results.Select(r => (ReleaseInfo)r.Clone()).ToArray();
}
} }
// Get the content from the tracker // Get the content from the tracker
var response = await RequestWithCookiesAndRetryAsync(queryUrl); var response = await RequestWithCookiesAndRetryAsync(searchUrl);
if (!response.ContentString.StartsWith("{")) // not JSON => error if (!response.ContentString.StartsWith("{")) // not JSON => error
throw new ExceptionWithConfigData("Unexpected response (not JSON)", ConfigData); throw new ExceptionWithConfigData("Unexpected response (not JSON)", ConfigData);
var json = JsonConvert.DeserializeObject<dynamic>(response.ContentString); var json = JsonConvert.DeserializeObject<dynamic>(response.ContentString);
// Parse // Parse
@@ -194,8 +237,11 @@ namespace Jackett.Common.Indexers
var matches = (long)json["Matches"]; var matches = (long)json["Matches"];
if (matches > 0) if (matches == 0)
{ {
return releases;
}
var groups = (JArray)json.Groups; var groups = (JArray)json.Groups;
foreach (var group in groups) foreach (var group in groups)
@@ -207,7 +253,8 @@ namespace Jackett.Common.Indexers
var groupName = (string)group["GroupName"]; var groupName = (string)group["GroupName"];
var seriesName = (string)group["SeriesName"]; var seriesName = (string)group["SeriesName"];
var mainTitle = WebUtility.HtmlDecode((string)group["FullName"]); var mainTitle = WebUtility.HtmlDecode((string)group["FullName"]);
if (seriesName != null)
if (seriesName.IsNotNullOrWhiteSpace())
mainTitle = seriesName; mainTitle = seriesName;
synonyms.Add(mainTitle); synonyms.Add(mainTitle);
@@ -319,9 +366,7 @@ namespace Jackett.Common.Indexers
{ {
if (property.Contains(" PSP ")) if (property.Contains(" PSP "))
category = new List<int> { TorznabCatType.ConsolePSP.ID }; category = new List<int> { TorznabCatType.ConsolePSP.ID };
if (property.Contains("PSX")) if (property.Contains("PSX") || property.Contains(" NES ") || property.Contains(" Switch "))
category = new List<int> { TorznabCatType.ConsoleOther.ID };
if (property.Contains(" NES "))
category = new List<int> { TorznabCatType.ConsoleOther.ID }; category = new List<int> { TorznabCatType.ConsoleOther.ID };
if (property.Contains(" PC ")) if (property.Contains(" PC "))
category = new List<int> { TorznabCatType.PCGames.ID }; category = new List<int> { TorznabCatType.PCGames.ID };
@@ -380,6 +425,7 @@ namespace Jackett.Common.Indexers
$"{releaseGroup}{title} {releaseInfo} {infoString}"; $"{releaseGroup}{title} {releaseInfo} {infoString}";
var guid = new Uri(details + "&nh=" + StringUtil.Hash(title)); var guid = new Uri(details + "&nh=" + StringUtil.Hash(title));
var release = new ReleaseInfo var release = new ReleaseInfo
{ {
MinimumRatio = 1, MinimumRatio = 1,
@@ -436,7 +482,6 @@ namespace Jackett.Common.Indexers
} }
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
OnParseError(response.ContentString, ex); OnParseError(response.ContentString, ex);
@@ -445,10 +490,10 @@ namespace Jackett.Common.Indexers
// Add to the cache // Add to the cache
lock (cache) lock (cache)
{ {
cache.Add(new CachedQueryResult(queryUrl, releases)); cache.Add(new CachedQueryResult(searchUrl, releases));
} }
return releases.Select(s => (ReleaseInfo)s.Clone()); return releases.Select(r => (ReleaseInfo)r.Clone());
} }
} }
} }

View File

@@ -5,6 +5,7 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
internal class ConfigurationDataAnimeBytes : ConfigurationDataUserPasskey internal class ConfigurationDataAnimeBytes : ConfigurationDataUserPasskey
{ {
public BoolConfigurationItem SearchByYear { get; private set; }
public BoolConfigurationItem IncludeRaw { get; private set; } public BoolConfigurationItem IncludeRaw { get; private set; }
//public DisplayItem DateWarning { get; private set; } //public DisplayItem DateWarning { get; private set; }
public BoolConfigurationItem PadEpisode { get; private set; } public BoolConfigurationItem PadEpisode { get; private set; }
@@ -15,8 +16,9 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
public BoolConfigurationItem FilterSeasonEpisode { get; private set; } public BoolConfigurationItem FilterSeasonEpisode { get; private set; }
public ConfigurationDataAnimeBytes(string instructionMessageOptional = null) public ConfigurationDataAnimeBytes(string instructionMessageOptional = null)
: base() : base(instructionMessageOptional)
{ {
SearchByYear = new BoolConfigurationItem("Search by year as a different argument in the request") { Value = false };
IncludeRaw = new BoolConfigurationItem("IncludeRaw") { Value = false }; IncludeRaw = new BoolConfigurationItem("IncludeRaw") { Value = false };
//DateWarning = new DisplayItem("This tracker does not supply upload dates so they are based off year of release.") { Name = "DateWarning" }; //DateWarning = new DisplayItem("This tracker does not supply upload dates so they are based off year of release.") { Name = "DateWarning" };
PadEpisode = new BoolConfigurationItem("Pad episode number for Sonarr compatability") { Value = false }; PadEpisode = new BoolConfigurationItem("Pad episode number for Sonarr compatability") { Value = false };
@@ -25,7 +27,6 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
AddAlternativeTitles = new BoolConfigurationItem("Add releases for Alternative Title(s)") { Value = false }; AddAlternativeTitles = new BoolConfigurationItem("Add releases for Alternative Title(s)") { Value = false };
AddFileNameTitles = new BoolConfigurationItem("Add releases based on single filename") { Value = false }; AddFileNameTitles = new BoolConfigurationItem("Add releases based on single filename") { Value = false };
FilterSeasonEpisode = new BoolConfigurationItem("Filter results by season/episode") { Value = false }; FilterSeasonEpisode = new BoolConfigurationItem("Filter results by season/episode") { Value = false };
Instructions = new DisplayInfoConfigurationItem("", instructionMessageOptional);
} }
} }
} }