From de442cc659a533cf889c2a93dffc9f2731499acb Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Sat, 10 Jul 2021 22:19:15 +0300 Subject: [PATCH] New: (Indexer) Anilibria --- .../Indexers/Definitions/Anilibria.cs | 294 ++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs diff --git a/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs b/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs new file mode 100644 index 000000000..464e392a8 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/Definitions/Anilibria.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; +using System.Text.RegularExpressions; +using FluentValidation; +using Newtonsoft.Json; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Indexers.Definitions +{ + public class Anilibria : TorrentIndexerBase + { + public override string Name => "Anilibria"; + public override string[] IndexerUrls => new string[] { "https://anilibria.tv/" }; + public override string Description => "Anilibria is russian anime voiceover group and eponymous anime tracker."; + public override string Language => "ru-ru"; + public override Encoding Encoding => Encoding.UTF8; + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override IndexerPrivacy Privacy => IndexerPrivacy.Public; + public override IndexerCapabilities Capabilities => SetCapabilities(); + + public Anilibria(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger) + : base(httpClient, eventAggregator, indexerStatusService, configService, logger) + { + } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new AnilibriaRequestGenerator() { Settings = Settings, Capabilities = Capabilities }; + } + + public override IParseIndexerResponse GetParser() + { + return new AnilibriaParser(Settings, Capabilities.Categories); + } + + private IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q + } + }; + caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVAnime, "ТВ"); + caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.TVAnime, "ONA"); + caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVAnime, "OVA"); + caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.Movies, "Фильм"); + return caps; + } + } + + public class AnilibriaRequestGenerator : IIndexerRequestGenerator + { + public AnilibriaSettings Settings { get; set; } + public IndexerCapabilities Capabilities { get; set; } + + public AnilibriaRequestGenerator() + { + } + + private IEnumerable GetPagedRequests(string term, int[] categories) + { + var apiUrl = Regex.Replace(Settings.BaseUrl, @"(https?:\/\/)(.*)", "$1api.$2v2"); + var queryCollection = new NameValueCollection + { + { "limit", "100" }, + { "filter", "names,code,torrents.list,season.year,type.string" } + }; + + if (string.IsNullOrWhiteSpace(term)) + { + apiUrl += "/getUpdates?" + queryCollection.GetQueryString(); + } + else + { + apiUrl += "/searchTitles?" + queryCollection.GetQueryString() + "&search=" + Uri.EscapeDataString(term); + } + + var request = new IndexerRequest(apiUrl, HttpAccept.Json); + yield return request; + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories)); + + return pageableRequests; + } + + public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories)); + + return pageableRequests; + } + + // Anilibria doesn't support music, but this function required by interface + public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + // Anilibria doesn't support books, but this function required by interface + public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public Func> GetCookies { get; set; } + public Action, DateTime?> CookiesUpdater { get; set; } + } + + public class AnilibriaParser : IParseIndexerResponse + { + private readonly AnilibriaSettings _settings; + private readonly IndexerCapabilitiesCategories _categories; + + public AnilibriaParser(AnilibriaSettings settings, IndexerCapabilitiesCategories categories) + { + _settings = settings; + _categories = categories; + } + + private string composeTitle(AnilibriaTitle tl, AnilibriaTorrent tr) + { + var title = tl.Names.Ru; + title += " / " + tl.Names.En; + if (tl.Names.Alternative is string) + { + title += " / " + tl.Names.Alternative; + } + + title += " " + tl.Season.Year; + title += " [" + tr.Quality.String + "]"; + if (!string.IsNullOrWhiteSpace(tr.Series.String)) + { + title += " - E" + tr.Series.String; + } + + return title; + } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var torrentInfos = new List(); + var queryResponseItems = JsonConvert.DeserializeObject>(indexerResponse.Content); + + foreach (var tl in queryResponseItems) + { + foreach (var tr in tl.Torrents.List) + { + var torrentInfo = new TorrentInfo + { + Title = composeTitle(tl, tr), + InfoUrl = string.Format("{0}/release/{1}.html", _settings.BaseUrl.TrimEnd('/'), tl.Code), + DownloadVolumeFactor = 0, + UploadVolumeFactor = 1, + Seeders = tr.Seeders, + Peers = tr.Leechers + tr.Seeders, + Grabs = tr.Downloads, + Categories = _categories.MapTrackerCatDescToNewznab(tl.Type.String), + + // API provides timestamp in UTC+3 timezone, so we need to substract 3 hours + PublishDate = DateTimeUtil.UnixTimestampToDateTime(tr.UploadedTimestamp).AddHours(-3), + Guid = _settings.BaseUrl + tr.Url, + DownloadUrl = _settings.BaseUrl + tr.Url, + Size = tr.TotalSize, + Resolution = tr.Quality.Resolution, + Codec = tr.Quality.Encoder + }; + + torrentInfos.Add(torrentInfo); + } + } + + return torrentInfos.ToArray(); + } + + public Action, DateTime?> CookiesUpdater { get; set; } + } + + public class AnilibriaSettingsValidator : AbstractValidator + { + public AnilibriaSettingsValidator() + { + } + } + + public class AnilibriaSettings : IIndexerSettings + { + private static readonly AnilibriaSettingsValidator Validator = new AnilibriaSettingsValidator(); + + [FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")] + public string BaseUrl { get; set; } + + [FieldDefinition(2)] + public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings(); + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } + + public class AnilibriaTitle + { + public AnilibriaNames Names { get; set; } + public string Code { get; set; } + public AnilibriaTorrents Torrents { get; set; } + public AnilibriaSeason Season { get; set; } + public AnilibriaTitleType Type { get; set; } + } + + public class AnilibriaTitleType + { + public string String { get; set; } + } + + public class AnilibriaNames + { + public string Ru { get; set; } + public string En { get; set; } + public object Alternative { get; set; } + } + + public class AnilibriaSeason + { + public long Year { get; set; } + } + + public class AnilibriaTorrents + { + public AnilibriaTorrent[] List { get; set; } + } + + public class AnilibriaTorrent + { + public AnilibriaSeries Series { get; set; } + public AnilibriaQuality Quality { get; set; } + public int Leechers { get; set; } + public int Seeders { get; set; } + public int Downloads { get; set; } + + [JsonProperty("total_size")] + public long TotalSize { get; set; } + public string Url { get; set; } + + [JsonProperty("uploaded_timestamp")] + public long UploadedTimestamp { get; set; } + } + + public class AnilibriaQuality + { + public string String { get; set; } + public string Type { get; set; } + public string Resolution { get; set; } + public string Encoder { get; set; } + } + + public class AnilibriaSeries + { + public long First { get; set; } + public long Last { get; set; } + public string String { get; set; } + } +}