Fixed: (AnimeBytes) Add `limit` and refactor parser

pull/1597/head
Bogdan 2 years ago
parent 3192990874
commit 3fbc2912f0

@ -0,0 +1,371 @@
{
"Results": 9999,
"Pagination": {
"Current": 1,
"Max": 99,
"Limit": {
"Min": 15,
"Coerced": 15,
"Max": 50
}
},
"Matches": 2,
"Groups": [
{
"ID": 575,
"CategoryName": "Anime",
"FullName": "Cowboy Bebop: Tengoku no Tobira - Movie  [2001]",
"GroupName": "Movie",
"SeriesID": "141",
"SeriesName": "Cowboy Bebop: Tengoku no Tobira",
"Artists": null,
"Year": "2001",
"Image": "https://mei.animebytes.tv/2bac1a04148be77ce41251d3cb44bbd5.jpg",
"Synonymns": [
"カウボーイビバップ天国の扉",
"Cowboy Bebop: Knockin' on Heaven's Door"
],
"SynonymnsV2": {
"Japanese": "カウボーイビバップ天国の扉",
"Romaji": "",
"Alternative": "Cowboy Bebop: Knockin' on Heaven's Door"
},
"Snatched": 4900,
"Comments": 11,
"Links": {
"AniDB": "https://anidb.net/anime/219",
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=353",
"Wikipedia": "https://en.wikipedia.org/wiki/Cowboy_Bebop:_The_Movie",
"MAL": "https://myanimelist.net/anime/5"
},
"Votes": 572,
"AvgVote": 8.3,
"Associations": null,
"Description": "Mars is under siege! Just before Halloween 2071, a terrorist bomb destroys a tanker truck on Highway One, close to the densely-populated crater city. There are casualties up to half a mile from the blast — 500 killed or injured by what appears to be a biochemical weapon. The reward for the bomber's capture is a massive 300,000,000 woolongs... and there are four humans and a dog who really need the money. Down on their luck as usual, the crew of the Bebop get on the case.\r\n\r\n[i]Note: The movie takes place between (in the time period of) the Cowboy Bebop episodes 22 and 23.[/i]",
"DescriptionHTML": "Mars is under siege! Just before Halloween 2071, a terrorist bomb destroys a tanker truck on Highway One, close to the densely-populated crater city. There are casualties up to half a mile from the blast — 500 killed or injured by what appears to be a biochemical weapon. The reward for the bomber&#039;s capture is a massive 300,000,000 woolongs... and there are four humans and a dog who really need the money. Down on their luck as usual, the crew of the Bebop get on the case.<br />\r\n<br />\r\n<em>Note: The movie takes place between (in the time period of) the Cowboy Bebop episodes 22 and 23.</em>",
"EpCount": 0,
"StudioList": "Sunrise///28|BONES///35",
"PastWeek": 2,
"Incomplete": false,
"Ongoing": false,
"Tags": [
"adventure",
"comedy",
"drama",
"scifi",
"seinen",
"action"
],
"Torrents": [
{
"ID": 959397,
"EditionData": {
"EditionTitle": ""
},
"RawDownMultiplier": 0,
"RawUpMultiplier": 1,
"Link": "https://animebytes.tv/torrent/959397/download/somepass",
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | Opus 5.1 | Softsubs (Polarwindz) | Freeleech",
"Snatched": 16,
"Seeders": 5,
"Leechers": 1,
"Status": 0,
"Size": 13090646841,
"FileCount": 1,
"FileList": [
{
"filename": "[Polarwindz] Cowboy Bebop The Movie - Knockin' on Heaven's Door [BD 1080p x265 10bit Opus 5.1].mkv",
"size": "13090646841"
}
],
"UploadTime": "2023-04-02 05:00:43"
},
{
"ID": 909565,
"EditionData": {
"EditionTitle": ""
},
"RawDownMultiplier": 0,
"RawUpMultiplier": 1,
"Link": "https://animebytes.tv/torrent/909565/download/somepass",
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | Opus 5.1 | Dual Audio | Softsubs (Yūrei) | Freeleech",
"Snatched": 29,
"Seeders": 6,
"Leechers": 0,
"Status": 0,
"Size": 15717521349,
"FileCount": 1,
"FileList": [
{
"filename": "Cowboy Bebop - Tengoku no Tobira.mkv",
"size": "15717521349"
}
],
"UploadTime": "2020-09-03 18:04:38"
}
]
},
{
"ID": 2709,
"CategoryName": "Anime",
"FullName": "BLEACH - TV Series&nbsp;&nbsp;[2004]",
"GroupName": "TV Series",
"SeriesID": "191",
"SeriesName": "BLEACH",
"Artists": null,
"Year": "2004",
"Image": "https://mei.animebytes.tv/997c8ec3ca0e70254b182b0a176f0161.jpg",
"Synonymns": [
"ブリーチ"
],
"SynonymnsV2": {
"Japanese": "ブリーチ",
"Romaji": "",
"Alternative": ""
},
"Snatched": 22653,
"Comments": 51,
"Links": {
"AniDB": "https://anidb.net/anime/2369",
"ANN": "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=4240",
"Wikipedia": "https://en.wikipedia.org/wiki/Bleach_(anime)",
"MAL": "https://myanimelist.net/anime/269"
},
"Votes": 530,
"AvgVote": 7.3,
"Associations": null,
"Description": "Kurosaki Ichigo is a teenager gifted with the ability to see spirits. His life is drastically changed by the sudden appearance of a Shinigami (literally, Death god) - one who governs the flow of souls between the human world and the afterlife - named Kuchiki Rukia, who arrives in search of a Hollow, a dangerous lost soul. When Rukia is severely wounded while trying to defeat the Hollow, she attempts to transfer half of her Reiatsu (literally, Spiritual pressure) energy to Ichigo so that he can defeat the Hollow. However, Ichigo takes almost all of her energy, transforming into a Shinigami and allowing him to defeat the Hollow with ease. With her powers diminished, Rukia is left stranded in the human world until she can recover her strength. In the meantime, Ichigo must take over Rukia's role as a Shinigami, battling Hollows and guiding souls to the afterlife realm known as the Soul Society.",
"DescriptionHTML": "Kurosaki Ichigo is a teenager gifted with the ability to see spirits. His life is drastically changed by the sudden appearance of a Shinigami (literally, Death god) - one who governs the flow of souls between the human world and the afterlife - named Kuchiki Rukia, who arrives in search of a Hollow, a dangerous lost soul. When Rukia is severely wounded while trying to defeat the Hollow, she attempts to transfer half of her Reiatsu (literally, Spiritual pressure) energy to Ichigo so that he can defeat the Hollow. However, Ichigo takes almost all of her energy, transforming into a Shinigami and allowing him to defeat the Hollow with ease. With her powers diminished, Rukia is left stranded in the human world until she can recover her strength. In the meantime, Ichigo must take over Rukia&#039;s role as a Shinigami, battling Hollows and guiding souls to the afterlife realm known as the Soul Society.",
"EpCount": 366,
"StudioList": "Studio Pierrot///45",
"PastWeek": 26,
"Incomplete": false,
"Ongoing": false,
"Tags": [
"adventure",
"comedy",
"fantasy",
"martial.arts",
"school.life",
"shounen",
"super.power",
"contemporary.fantasy",
"swordplay",
"action",
"supernatural"
],
"Torrents": [
{
"ID": 1031199,
"EditionData": {
"EditionTitle": "Season 02: The Entry (021-041)"
},
"RawDownMultiplier": 0,
"RawUpMultiplier": 1,
"Link": "https://animebytes.tv/torrent/1031199/download/somepass",
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | AAC 2.0 | Dual Audio | Softsubs (GHOST) | Freeleech",
"Snatched": 20,
"Seeders": 24,
"Leechers": 0,
"Status": 0,
"Size": 19584943785,
"FileCount": 21,
"FileList": [
{
"filename": "[GHOST][1080p] Bleach - 021 [BD HEVC 10bit Dual Audio AC3][035452C3].mkv",
"size": "880693454"
},
{
"filename": "[GHOST][1080p] Bleach - 022 [BD HEVC 10bit Dual Audio AC3][0E923AAD].mkv",
"size": "851531918"
},
{
"filename": "[GHOST][1080p] Bleach - 023 [BD HEVC 10bit Dual Audio AC3][604A0EC6].mkv",
"size": "882518038"
},
{
"filename": "[GHOST][1080p] Bleach - 024 [BD HEVC 10bit Dual Audio AC3][ABABE9B3].mkv",
"size": "837335522"
},
{
"filename": "[GHOST][1080p] Bleach - 025 [BD HEVC 10bit Dual Audio AC3][17AEB0C1].mkv",
"size": "933034706"
},
{
"filename": "[GHOST][1080p] Bleach - 026 [BD HEVC 10bit Dual Audio AC3][E6F0017C].mkv",
"size": "891916996"
},
{
"filename": "[GHOST][1080p] Bleach - 027 [BD HEVC 10bit Dual Audio AC3][317791C7].mkv",
"size": "896856044"
},
{
"filename": "[GHOST][1080p] Bleach - 028 [BD HEVC 10bit Dual Audio AC3][5CA4DF06].mkv",
"size": "1010510386"
},
{
"filename": "[GHOST][1080p] Bleach - 029 [BD HEVC 10bit Dual Audio AC3][549CF25A].mkv",
"size": "995648398"
},
{
"filename": "[GHOST][1080p] Bleach - 030 [BD HEVC 10bit Dual Audio AC3][CED479F9].mkv",
"size": "991633973"
},
{
"filename": "[GHOST][1080p] Bleach - 031 [BD HEVC 10bit Dual Audio AC3][1EEFAB0E].mkv",
"size": "950195341"
},
{
"filename": "[GHOST][1080p] Bleach - 032 [BD HEVC 10bit Dual Audio AC3][C6F3C39A].mkv",
"size": "859218784"
},
{
"filename": "[GHOST][1080p] Bleach - 033 [BD HEVC 10bit Dual Audio AC3][A8424897].mkv",
"size": "933230823"
},
{
"filename": "[GHOST][1080p] Bleach - 034 [BD HEVC 10bit Dual Audio AC3][96C94DB8].mkv",
"size": "818179634"
},
{
"filename": "[GHOST][1080p] Bleach - 035 [BD HEVC 10bit Dual Audio AC3][A07831AE].mkv",
"size": "882457138"
},
{
"filename": "[GHOST][1080p] Bleach - 036 [BD HEVC 10bit Dual Audio AC3][D984C169].mkv",
"size": "895683290"
},
{
"filename": "[GHOST][1080p] Bleach - 037 [BD HEVC 10bit Dual Audio AC3][32C05A7E].mkv",
"size": "970033716"
},
{
"filename": "[GHOST][1080p] Bleach - 038 [BD HEVC 10bit Dual Audio AC3][824B3EEC].mkv",
"size": "956573899"
},
{
"filename": "[GHOST][1080p] Bleach - 039 [BD HEVC 10bit Dual Audio AC3][A45233DF].mkv",
"size": "1238240707"
},
{
"filename": "[GHOST][1080p] Bleach - 040 [BD HEVC 10bit Dual Audio AC3][5334A7F1].mkv",
"size": "894491562"
},
{
"filename": "[GHOST][1080p] Bleach - 041 [BD HEVC 10bit Dual Audio AC3][A4F17363].mkv",
"size": "1014959456"
}
],
"UploadTime": "2023-03-03 03:03:17"
},
{
"ID": 1031203,
"EditionData": {
"EditionTitle": "Season 03: The Rescue (042-063)"
},
"RawDownMultiplier": 0,
"RawUpMultiplier": 1,
"Link": "https://animebytes.tv/torrent/1031203/download/somepass",
"Property": "Blu-ray | MKV | h265 10-bit | 1080p | AC3 2.0 | Dual Audio | Softsubs (GHOST) | Freeleech",
"Snatched": 6,
"Seeders": 12,
"Leechers": 2,
"Status": 0,
"Size": 24498538059,
"FileCount": 22,
"FileList": [
{
"filename": "[GHOST][1080p] Bleach - 042 [BD HEVC 10bit Dual Audio AC3][55763BF6].mkv",
"size": "899825828"
},
{
"filename": "[GHOST][1080p] Bleach - 043 [BD HEVC 10bit Dual Audio AC3][70B71ECC].mkv",
"size": "902256094"
},
{
"filename": "[GHOST][1080p] Bleach - 044 [BD HEVC 10bit Dual Audio AC3][35F5526B].mkv",
"size": "885323344"
},
{
"filename": "[GHOST][1080p] Bleach - 045 [BD HEVC 10bit Dual Audio AC3][9C1DAE4E].mkv",
"size": "1082465042"
},
{
"filename": "[GHOST][1080p] Bleach - 046 [BD HEVC 10bit Dual Audio AC3][869EF5B6].mkv",
"size": "957433930"
},
{
"filename": "[GHOST][1080p] Bleach - 047 [BD HEVC 10bit Dual Audio AC3][890DA7CE].mkv",
"size": "997124540"
},
{
"filename": "[GHOST][1080p] Bleach - 048 [BD HEVC 10bit Dual Audio AC3][39064E08].mkv",
"size": "1026751383"
},
{
"filename": "[GHOST][1080p] Bleach - 049 [BD HEVC 10bit Dual Audio AC3][C536D3DB].mkv",
"size": "994918391"
},
{
"filename": "[GHOST][1080p] Bleach - 050 [BD HEVC 10bit Dual Audio AC3][A7A0CB24].mkv",
"size": "1141146920"
},
{
"filename": "[GHOST][1080p] Bleach - 051 [BD HEVC 10bit Dual Audio AC3][25C06D9D].mkv",
"size": "951751791"
},
{
"filename": "[GHOST][1080p] Bleach - 052 [BD HEVC 10bit Dual Audio AC3][FB506194].mkv",
"size": "1131756065"
},
{
"filename": "[GHOST][1080p] Bleach - 053 [BD HEVC 10bit Dual Audio AC3][4A76C66D].mkv",
"size": "1076952986"
},
{
"filename": "[GHOST][1080p] Bleach - 054 [BD HEVC 10bit Dual Audio AC3][51D8E5F8].mkv",
"size": "1369454462"
},
{
"filename": "[GHOST][1080p] Bleach - 055 [BD HEVC 10bit Dual Audio AC3][DCF20007].mkv",
"size": "1428073116"
},
{
"filename": "[GHOST][1080p] Bleach - 056 [BD HEVC 10bit Dual Audio AC3][34A28687].mkv",
"size": "1304804717"
},
{
"filename": "[GHOST][1080p] Bleach - 057 [BD HEVC 10bit Dual Audio AC3][D1D5FE29].mkv",
"size": "1056220730"
},
{
"filename": "[GHOST][1080p] Bleach - 058 [BD HEVC 10bit Dual Audio AC3][C6EAC278].mkv",
"size": "1551455953"
},
{
"filename": "[GHOST][1080p] Bleach - 059 [BD HEVC 10bit Dual Audio AC3][E7B25869].mkv",
"size": "1026910923"
},
{
"filename": "[GHOST][1080p] Bleach - 060 [BD HEVC 10bit Dual Audio AC3][C9D257D4].mkv",
"size": "1059631177"
},
{
"filename": "[GHOST][1080p] Bleach - 061 [BD HEVC 10bit Dual Audio AC3][0521C8D3].mkv",
"size": "1457893287"
},
{
"filename": "[GHOST][1080p] Bleach - 062 [BD HEVC 10bit Dual Audio AC3][65CBA616].mkv",
"size": "1262863946"
},
{
"filename": "[GHOST][1080p] Bleach - 063 [BD HEVC 10bit Dual Audio AC3][CF63E244].mkv",
"size": "933523434"
}
],
"UploadTime": "2023-04-03 03:14:35"
}
]
}
]
}

@ -0,0 +1,88 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Definitions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.IndexerTests.AnimeBytesTests
{
[TestFixture]
public class AnimeBytesFixture : CoreTest<AnimeBytes>
{
[SetUp]
public void Setup()
{
Subject.Definition = new IndexerDefinition
{
Name = "AnimeBytes",
Settings = new AnimeBytesSettings
{
BaseUrl = "https://animebytes.tv/",
Username = "someuser",
Passkey = "somepass"
}
};
}
[Test]
public async Task should_parse_recent_feed_from_animebytes()
{
var recentFeed = ReadAllText(@"Files/Indexers/AnimeBytes/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new BasicSearchCriteria { Categories = new[] { 2000, 5000 } })).Releases;
releases.Should().HaveCount(10);
releases.First().Should().BeOfType<TorrentInfo>();
var firstTorrentInfo = releases.First() as TorrentInfo;
firstTorrentInfo.Title.Should().Be("[GHOST] BLEACH S03 [Blu-ray][MKV][h265 10-bit][1080p][AC3 2.0][Dual Audio][Softsubs (GHOST)]");
firstTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
firstTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/1031203/download/somepass");
firstTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/1031203/group");
firstTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/1031203/group?nh=F7C73EF631FE269D3A7F10BD12EC99A1");
firstTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
firstTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
firstTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-03 03:14:35", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
firstTorrentInfo.Size.Should().Be(24498538059);
firstTorrentInfo.InfoHash.Should().Be(null);
firstTorrentInfo.MagnetUrl.Should().Be(null);
firstTorrentInfo.Peers.Should().Be(2 + 12);
firstTorrentInfo.Seeders.Should().Be(12);
firstTorrentInfo.Files.Should().Be(22);
firstTorrentInfo.MinimumSeedTime.Should().Be(655200);
var secondTorrentInfo = releases.Skip(2).First() as TorrentInfo;
secondTorrentInfo.Title.Should().Be("Cowboy Bebop: Tengoku no Tobira 2001 [Polarwindz] [Blu-ray][MKV][h265 10-bit][1080p][Opus 5.1][Softsubs (Polarwindz)]");
secondTorrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
secondTorrentInfo.DownloadUrl.Should().Be("https://animebytes.tv/torrent/959397/download/somepass");
secondTorrentInfo.InfoUrl.Should().Be("https://animebytes.tv/torrent/959397/group");
secondTorrentInfo.Guid.Should().Be("https://animebytes.tv/torrent/959397/group?nh=D63895DA87A25239C11F9823F46000E1");
secondTorrentInfo.CommentUrl.Should().BeNullOrEmpty();
secondTorrentInfo.Indexer.Should().Be(Subject.Definition.Name);
secondTorrentInfo.PublishDate.Should().Be(DateTime.Parse("2023-04-02 05:00:43", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
secondTorrentInfo.Size.Should().Be(13090646841);
secondTorrentInfo.InfoHash.Should().Be(null);
secondTorrentInfo.MagnetUrl.Should().Be(null);
secondTorrentInfo.Peers.Should().Be(1 + 5);
secondTorrentInfo.Seeders.Should().Be(5);
secondTorrentInfo.Files.Should().Be(1);
secondTorrentInfo.MinimumSeedTime.Should().Be(475200);
}
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
@ -47,7 +48,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override IParseIndexerResponse GetParser()
{
return new AnimeBytesParser(Settings, Capabilities.Categories);
return new AnimeBytesParser(Settings);
}
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
@ -140,16 +141,20 @@ namespace NzbDrone.Core.Indexers.Definitions
var searchUrl = $"{_settings.BaseUrl.TrimEnd('/')}/scrape.php";
var term = searchCriteria.SanitizedSearchTerm.Trim();
var searchTerm = StripEpisodeNumber(term);
var parameters = new NameValueCollection
{
{ "username", _settings.Username },
{ "torrent_pass", _settings.Passkey },
{ "sort", "grouptime" },
{ "way", "desc" },
{ "type", searchType },
{ "searchstr", StripEpisodeNumber(term) }
{ "searchstr", searchTerm },
{ "limit", searchTerm.IsNotNullOrWhiteSpace() ? "50" : "20" }
};
if (_settings.SearchByYear)
if (_settings.SearchByYear && searchType == "anime")
{
var searchYear = ParseYearFromSearchTerm(term);
@ -166,6 +171,16 @@ namespace NzbDrone.Core.Indexers.Definitions
queryCats.ForEach(cat => parameters.Set(cat, "1"));
}
if (_settings.FreeleechOnly)
{
parameters.Set("freeleech", "1");
}
if (_settings.ExcludeHentai && searchType == "anime")
{
parameters.Set("hentai", "0");
}
searchUrl += "?" + parameters.GetQueryString();
var request = new IndexerRequest(searchUrl, HttpAccept.Json);
@ -207,17 +222,20 @@ namespace NzbDrone.Core.Indexers.Definitions
public class AnimeBytesParser : IParseIndexerResponse
{
private readonly AnimeBytesSettings _settings;
private readonly IndexerCapabilitiesCategories _categories;
public AnimeBytesParser(AnimeBytesSettings settings, IndexerCapabilitiesCategories categories)
private readonly HashSet<string> _excludedProperties = new (StringComparer.OrdinalIgnoreCase)
{
"Freeleech"
};
public AnimeBytesParser(AnimeBytesSettings settings)
{
_settings = settings;
_categories = categories;
}
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
var releaseInfos = new List<ReleaseInfo>();
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
@ -231,316 +249,306 @@ namespace NzbDrone.Core.Indexers.Definitions
var response = JsonConvert.DeserializeObject<AnimeBytesResponse>(indexerResponse.Content);
if (response.Matches > 0)
if (response.Matches == 0)
{
return releaseInfos.ToArray();
}
foreach (var group in response.Groups)
{
foreach (var group in response.Groups)
var categoryName = group.CategoryName;
var description = group.Description;
var year = group.Year;
var groupName = group.GroupName;
var seriesName = group.SeriesName;
var mainTitle = WebUtility.HtmlDecode(group.FullName);
if (seriesName.IsNotNullOrWhiteSpace())
{
var synonyms = new List<string>();
var year = group.Year;
var groupName = group.GroupName;
var seriesName = group.SeriesName;
var mainTitle = WebUtility.HtmlDecode(group.FullName);
if (seriesName != null)
{
mainTitle = seriesName;
}
mainTitle = seriesName;
}
synonyms.Add(mainTitle);
var synonyms = new HashSet<string>
{
mainTitle
};
if (group.Synonymns != null)
if (group.Synonymns != null && group.Synonymns.Any())
{
if (_settings.AddJapaneseTitle && group.Synonymns.TryGetValue("Japanese", out var japaneseTitle) && japaneseTitle.IsNotNullOrWhiteSpace())
{
var syn = (Synonymns)group.Synonymns;
synonyms.Add(japaneseTitle.Trim());
}
if (syn.StringArray != null)
{
if (_settings.AddJapaneseTitle && syn.StringArray.Count >= 1)
{
synonyms.Add(syn.StringArray[0]);
}
if (_settings.AddRomajiTitle && group.Synonymns.TryGetValue("Romaji", out var romajiTitle) && romajiTitle.IsNotNullOrWhiteSpace())
{
synonyms.Add(romajiTitle.Trim());
}
if (_settings.AddRomajiTitle && syn.StringArray.Count >= 2)
{
synonyms.Add(syn.StringArray[1]);
}
if (_settings.AddAlternativeTitle && group.Synonymns.TryGetValue("Alternative", out var alternativeTitle) && alternativeTitle.IsNotNullOrWhiteSpace())
{
synonyms.UnionWith(alternativeTitle.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries));
}
}
if (_settings.AddAlternativeTitle && syn.StringArray.Count == 3)
{
synonyms.AddRange(syn.StringArray[2].Split(',').Select(t => t.Trim()));
}
}
else
{
if (_settings.AddJapaneseTitle && syn.StringMap.ContainsKey("0"))
{
synonyms.Add(syn.StringMap["0"]);
}
List<IndexerCategory> categories = null;
if (_settings.AddRomajiTitle && syn.StringMap.ContainsKey("1"))
{
synonyms.Add(syn.StringMap["1"]);
}
foreach (var torrent in group.Torrents)
{
// Skip non-freeleech results when freeleech only is set
if (_settings.FreeleechOnly && torrent.RawDownMultiplier != 0)
{
continue;
}
if (_settings.AddAlternativeTitle && syn.StringMap.ContainsKey("2"))
{
synonyms.AddRange(syn.StringMap["2"].Split(',').Select(t => t.Trim()));
}
}
var torrentId = torrent.Id;
var link = torrent.Link;
var publishDate = DateTime.ParseExact(torrent.UploadTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
var details = new Uri(_settings.BaseUrl + "torrent/" + torrentId + "/group");
var size = torrent.Size;
var snatched = torrent.Snatched;
var seeders = torrent.Seeders;
var leechers = torrent.Leechers;
var fileCount = torrent.FileCount;
var peers = seeders + leechers;
var rawDownMultiplier = torrent.RawDownMultiplier;
var rawUpMultiplier = torrent.RawUpMultiplier;
// MST with additional 5 hours per GB
var minimumSeedTime = 259200 + (int)(size / (int)Math.Pow(1024, 3) * 18000);
var properties = WebUtility.HtmlDecode(torrent.Property)
.Split('|', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.ToList();
properties.RemoveAll(p => _excludedProperties.Any(p.Contains));
if (_settings.ExcludeRaw && properties.ContainsIgnoreCase("RAW"))
{
continue;
}
List<IndexerCategory> category = null;
var categoryName = group.CategoryName;
var releaseInfo = _settings.EnableSonarrCompatibility && categoryName == "Anime" ? "S01" : "";
var editionTitle = torrent.EditionData.EditionTitle;
var description = group.Description;
int? episode = null;
int? season = null;
foreach (var torrent in group.Torrents)
if (editionTitle.IsNotNullOrWhiteSpace())
{
var releaseInfo = _settings.EnableSonarrCompatibility ? "S01" : "";
int? episode = null;
int? season = null;
var editionTitle = torrent.EditionData.EditionTitle;
if (!string.IsNullOrWhiteSpace(editionTitle))
{
releaseInfo = WebUtility.HtmlDecode(editionTitle);
if (_settings.EnableSonarrCompatibility)
{
var simpleSeasonRegEx = new Regex(@"Season (\d+)", RegexOptions.Compiled);
var simpleSeasonRegExMatch = simpleSeasonRegEx.Match(releaseInfo);
if (simpleSeasonRegExMatch.Success)
{
season = ParseUtil.CoerceInt(simpleSeasonRegExMatch.Groups[1].Value);
}
}
var episodeRegEx = new Regex(@"Episode (\d+)", RegexOptions.Compiled);
var episodeRegExMatch = episodeRegEx.Match(releaseInfo);
if (episodeRegExMatch.Success)
{
episode = ParseUtil.CoerceInt(episodeRegExMatch.Groups[1].Value);
}
}
releaseInfo = WebUtility.HtmlDecode(editionTitle);
if (_settings.EnableSonarrCompatibility)
{
var advancedSeasonRegEx = new Regex(@"(\d+)(st|nd|rd|th) Season", RegexOptions.Compiled | RegexOptions.IgnoreCase);
var advancedSeasonRegExMatch = advancedSeasonRegEx.Match(mainTitle);
if (advancedSeasonRegExMatch.Success)
{
season = ParseUtil.CoerceInt(advancedSeasonRegExMatch.Groups[1].Value);
}
var seasonCharactersRegEx = new Regex(@"(I{2,})$", RegexOptions.Compiled);
var seasonCharactersRegExMatch = seasonCharactersRegEx.Match(mainTitle);
if (seasonCharactersRegExMatch.Success)
var simpleSeasonRegEx = new Regex(@"Season (\d+)", RegexOptions.Compiled);
var simpleSeasonRegExMatch = simpleSeasonRegEx.Match(releaseInfo);
if (simpleSeasonRegExMatch.Success)
{
season = seasonCharactersRegExMatch.Groups[1].Value.Length;
}
var seasonNumberRegEx = new Regex(@"([2-9])$", RegexOptions.Compiled);
var seasonNumberRegExMatch = seasonNumberRegEx.Match(mainTitle);
if (seasonNumberRegExMatch.Success)
{
season = ParseUtil.CoerceInt(seasonNumberRegExMatch.Groups[1].Value);
season = ParseUtil.CoerceInt(simpleSeasonRegExMatch.Groups[1].Value);
}
}
if (episode != null)
var episodeRegEx = new Regex(@"Episode (\d+)", RegexOptions.Compiled);
var episodeRegExMatch = episodeRegEx.Match(releaseInfo);
if (episodeRegExMatch.Success)
{
var episodeString = episode is > 0 and < 10
? "0" + episode
: episode.ToString();
releaseInfo = $" - {episodeString}";
episode = ParseUtil.CoerceInt(episodeRegExMatch.Groups[1].Value);
}
else
}
if (_settings.EnableSonarrCompatibility)
{
var advancedSeasonRegEx = new Regex(@"(\d+)(st|nd|rd|th) Season", RegexOptions.Compiled | RegexOptions.IgnoreCase);
var advancedSeasonRegExMatch = advancedSeasonRegEx.Match(mainTitle);
if (advancedSeasonRegExMatch.Success)
{
if (season != null && _settings.EnableSonarrCompatibility)
{
releaseInfo = $"S{season}";
}
season = ParseUtil.CoerceInt(advancedSeasonRegExMatch.Groups[1].Value);
}
releaseInfo = releaseInfo.Trim();
var torrentId = torrent.Id;
var property = torrent.Property.Replace(" | Freeleech", string.Empty);
var link = torrent.Link;
var uploadTime = torrent.UploadTime;
var publishDate = DateTime.SpecifyKind(uploadTime.DateTime, DateTimeKind.Utc).ToLocalTime();
var details = new Uri(_settings.BaseUrl + "torrent/" + torrentId + "/group");
var size = torrent.Size;
var snatched = torrent.Snatched;
var seeders = torrent.Seeders;
var leechers = torrent.Leechers;
var fileCount = torrent.FileCount;
var peers = seeders + leechers;
var rawDownMultiplier = torrent.RawDownMultiplier;
var rawUpMultiplier = torrent.RawUpMultiplier;
// Ignore these categories as they'll cause hell with the matcher
// TV Special, ONA, DVD Special, BD Special
if (groupName == "TV Series" || groupName == "OVA")
var seasonCharactersRegEx = new Regex(@"(I{2,})$", RegexOptions.Compiled);
var seasonCharactersRegExMatch = seasonCharactersRegEx.Match(mainTitle);
if (seasonCharactersRegExMatch.Success)
{
category = new List<IndexerCategory> { NewznabStandardCategory.TVAnime };
season = seasonCharactersRegExMatch.Groups[1].Value.Length;
}
if (groupName == "Movie" || groupName == "Live Action Movie")
var seasonNumberRegEx = new Regex(@"([2-9])$", RegexOptions.Compiled);
var seasonNumberRegExMatch = seasonNumberRegEx.Match(mainTitle);
if (seasonNumberRegExMatch.Success)
{
category = new List<IndexerCategory> { NewznabStandardCategory.Movies };
season = ParseUtil.CoerceInt(seasonNumberRegExMatch.Groups[1].Value);
}
}
if (episode is > 0)
{
releaseInfo = $" - {episode:00}";
}
else if (_settings.EnableSonarrCompatibility && season is > 0)
{
releaseInfo = $"S{season:00}";
}
releaseInfo = releaseInfo.Trim();
// Ignore these categories as they'll cause hell with the matcher
// TV Special, DVD Special, BD Special
if (groupName is "TV Series" or "OVA" or "ONA")
{
categories = new List<IndexerCategory> { NewznabStandardCategory.TVAnime };
}
if (groupName is "Movie" or "Live Action Movie")
{
categories = new List<IndexerCategory> { NewznabStandardCategory.Movies };
}
if (categoryName is "Manga" or "Oneshot" or "Anthology" or "Manhwa" or "Manhua" or "Light Novel")
{
categories = new List<IndexerCategory> { NewznabStandardCategory.BooksComics };
}
if (categoryName is "Novel" or "Artbook")
{
categories = new List<IndexerCategory> { NewznabStandardCategory.BooksComics };
}
if (categoryName == "Manga" || categoryName == "Oneshot" || categoryName == "Anthology" || categoryName == "Manhwa" || categoryName == "Manhua" || categoryName == "Light Novel")
if (categoryName is "Game" or "Visual Novel")
{
if (properties.Contains("PSP"))
{
category = new List<IndexerCategory> { NewznabStandardCategory.BooksComics };
categories = new List<IndexerCategory> { NewznabStandardCategory.ConsolePSP };
}
if (categoryName == "Novel" || categoryName == "Artbook")
if (properties.Contains("PS3"))
{
category = new List<IndexerCategory> { NewznabStandardCategory.BooksComics };
categories = new List<IndexerCategory> { NewznabStandardCategory.ConsolePS3 };
}
if (categoryName == "Game" || categoryName == "Visual Novel")
if (properties.Contains("PS Vita"))
{
if (property.Contains(" PSP "))
{
category = new List<IndexerCategory> { NewznabStandardCategory.ConsolePSP };
}
if (property.Contains("PSX"))
{
category = new List<IndexerCategory> { NewznabStandardCategory.ConsoleOther };
}
categories = new List<IndexerCategory> { NewznabStandardCategory.ConsolePSVita };
}
if (property.Contains(" NES "))
{
category = new List<IndexerCategory> { NewznabStandardCategory.ConsoleOther };
}
if (properties.Contains("3DS"))
{
categories = new List<IndexerCategory> { NewznabStandardCategory.Console3DS };
}
if (property.Contains(" PC "))
{
category = new List<IndexerCategory> { NewznabStandardCategory.PCGames };
}
if (properties.Contains("NDS"))
{
categories = new List<IndexerCategory> { NewznabStandardCategory.ConsoleNDS };
}
if (categoryName == "Single" || categoryName == "EP" || categoryName == "Album" || categoryName == "Compilation" || categoryName == "Soundtrack" || categoryName == "Remix CD" || categoryName == "PV" || categoryName == "Live Album" || categoryName == "Image CD" || categoryName == "Drama CD" || categoryName == "Vocal CD")
if (properties.Contains("PSX") || properties.Contains("PS2") || properties.Contains("SNES") || properties.Contains("NES") || properties.Contains("GBA") || properties.Contains("Switch"))
{
if (property.Contains(" Lossless "))
{
category = new List<IndexerCategory> { NewznabStandardCategory.AudioLossless };
}
else if (property.Contains("MP3"))
{
category = new List<IndexerCategory> { NewznabStandardCategory.AudioMP3 };
}
else
{
category = new List<IndexerCategory> { NewznabStandardCategory.AudioOther };
}
categories = new List<IndexerCategory> { NewznabStandardCategory.ConsoleOther };
}
// We don't actually have a release name >.> so try to create one
var releaseTags = property.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
for (var i = releaseTags.Count - 1; i >= 0; i--)
if (properties.Contains("PC"))
{
releaseTags[i] = releaseTags[i].Trim();
if (string.IsNullOrWhiteSpace(releaseTags[i]))
{
releaseTags.RemoveAt(i);
}
categories = new List<IndexerCategory> { NewznabStandardCategory.PCGames };
}
}
var releaseGroup = releaseTags.LastOrDefault();
if (releaseGroup != null && releaseGroup.Contains('(') && releaseGroup.Contains(')'))
if (categoryName is "Single" or "EP" or "Album" or "Compilation" or "Soundtrack" or "Remix CD" or "PV" or "Live Album" or "Image CD" or "Drama CD" or "Vocal CD")
{
if (properties.Any(p => p.Contains("Lossless")))
{
categories = new List<IndexerCategory> { NewznabStandardCategory.AudioLossless };
}
else if (properties.Any(p => p.Contains("MP3")))
{
//// Skip raws if set
//if (releaseGroup.ToLowerInvariant().StartsWith("raw") && !AllowRaws)
//{
// continue;
//}
var start = releaseGroup.IndexOf("(", StringComparison.Ordinal);
releaseGroup = "[" + releaseGroup.Substring(start + 1, (releaseGroup.IndexOf(")", StringComparison.Ordinal) - 1) - start) + "] ";
categories = new List<IndexerCategory> { NewznabStandardCategory.AudioMP3 };
}
else
{
releaseGroup = string.Empty;
categories = new List<IndexerCategory> { NewznabStandardCategory.AudioOther };
}
}
//if (!AllowRaws && releaseTags.Contains("raw", StringComparer.InvariantCultureIgnoreCase))
//{
// continue;
//}
var infoString = releaseTags.Aggregate(string.Empty, (prev, cur) => prev + "[" + cur + "]");
var minimumSeedTime = 259200;
// We don't actually have a release name >.> so try to create one
var releaseGroup = properties.LastOrDefault(p => !p.ContainsIgnoreCase("Hentai"));
// Additional 5 hours per GB
minimumSeedTime += (int)((size / 1000000000) * 18000);
if (releaseGroup.IsNotNullOrWhiteSpace() && releaseGroup.Contains('(') && releaseGroup.Contains(')'))
{
var start = releaseGroup.IndexOf("(", StringComparison.Ordinal);
releaseGroup = "[" + releaseGroup.Substring(start + 1, releaseGroup.IndexOf(")", StringComparison.Ordinal) - 1 - start) + "] ";
}
else
{
releaseGroup = string.Empty;
}
if (_settings.UseFilenameForSingleEpisodes && torrent.FileCount == 1)
{
var fileName = torrent.Files.First().FileName;
var infoString = properties.Select(p => "[" + p + "]").Join(string.Empty);
var guid = new Uri(details + "&nh=" + HashUtil.CalculateMd5(fileName));
if (_settings.UseFilenameForSingleEpisodes && torrent.FileCount == 1)
{
var fileName = torrent.Files.First().FileName;
var release = new TorrentInfo
{
MinimumRatio = 1,
MinimumSeedTime = minimumSeedTime,
Title = fileName,
InfoUrl = details.AbsoluteUri,
Guid = guid.AbsoluteUri,
DownloadUrl = link.AbsoluteUri,
PublishDate = publishDate,
Categories = category,
Description = description,
Size = size,
Seeders = seeders,
Peers = peers,
Grabs = snatched,
Files = fileCount,
DownloadVolumeFactor = rawDownMultiplier,
UploadVolumeFactor = rawUpMultiplier,
};
torrentInfos.Add(release);
continue;
}
var guid = new Uri(details + "?nh=" + HashUtil.CalculateMd5(fileName));
foreach (var title in synonyms)
var release = new TorrentInfo
{
var releaseTitle = groupName == "Movie" ?
$"{title} {year} {releaseGroup}{infoString}" :
$"{releaseGroup}{title} {releaseInfo} {infoString}";
MinimumRatio = 1,
MinimumSeedTime = minimumSeedTime,
Title = fileName,
InfoUrl = details.AbsoluteUri,
Guid = guid.AbsoluteUri,
DownloadUrl = link.AbsoluteUri,
PublishDate = publishDate,
Categories = categories,
Description = description,
Size = size,
Seeders = seeders,
Peers = peers,
Grabs = snatched,
Files = fileCount,
DownloadVolumeFactor = rawDownMultiplier,
UploadVolumeFactor = rawUpMultiplier,
};
releaseInfos.Add(release);
continue;
}
var guid = new Uri(details + "&nh=" + HashUtil.CalculateMd5(title));
foreach (var title in synonyms)
{
var releaseTitle = groupName is "Movie" or "Live Action Movie" ?
$"{title} {year} {releaseGroup}{infoString}" :
$"{releaseGroup}{title} {releaseInfo} {infoString}";
var release = new TorrentInfo
{
MinimumRatio = 1,
MinimumSeedTime = minimumSeedTime,
Title = releaseTitle,
InfoUrl = details.AbsoluteUri,
Guid = guid.AbsoluteUri,
DownloadUrl = link.AbsoluteUri,
PublishDate = publishDate,
Categories = category,
Description = description,
Size = size,
Seeders = seeders,
Peers = peers,
Grabs = snatched,
Files = fileCount,
DownloadVolumeFactor = rawDownMultiplier,
UploadVolumeFactor = rawUpMultiplier,
};
torrentInfos.Add(release);
}
var guid = new Uri(details + "?nh=" + HashUtil.CalculateMd5(title));
var release = new TorrentInfo
{
MinimumRatio = 1,
MinimumSeedTime = minimumSeedTime,
Title = releaseTitle.Trim(),
InfoUrl = details.AbsoluteUri,
Guid = guid.AbsoluteUri,
DownloadUrl = link.AbsoluteUri,
PublishDate = publishDate,
Categories = categories,
Description = description,
Size = size,
Seeders = seeders,
Peers = peers,
Grabs = snatched,
Files = fileCount,
DownloadVolumeFactor = rawDownMultiplier,
UploadVolumeFactor = rawUpMultiplier,
};
releaseInfos.Add(release);
}
}
}
return torrentInfos.ToArray();
return releaseInfos
.OrderByDescending(o => o.PublishDate)
.ToArray();
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
@ -566,6 +574,9 @@ namespace NzbDrone.Core.Indexers.Definitions
{
Username = "";
Passkey = "";
FreeleechOnly = false;
ExcludeRaw = false;
ExcludeHentai = false;
SearchByYear = false;
EnableSonarrCompatibility = true;
UseFilenameForSingleEpisodes = false;
@ -580,22 +591,31 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Passkey", HelpText = "Site Passkey", Privacy = PrivacyLevel.Password, Type = FieldType.Password)]
public string Passkey { get; set; }
[FieldDefinition(5, Label = "Search By Year", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr to search by year as a different argument in the request.")]
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
public bool FreeleechOnly { get; set; }
[FieldDefinition(5, Label = "Exclude RAW", Type = FieldType.Checkbox, HelpText = "Exclude RAW torrents from results")]
public bool ExcludeRaw { get; set; }
[FieldDefinition(6, Label = "Exclude Hentai", Type = FieldType.Checkbox, HelpText = "Exclude Hentai torrents from results")]
public bool ExcludeHentai { get; set; }
[FieldDefinition(7, Label = "Search By Year", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr to search by year as a different argument in the request.")]
public bool SearchByYear { get; set; }
[FieldDefinition(5, Label = "Enable Sonarr Compatibility", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr try to add Season information into Release names, without this Sonarr can't match any Seasons, but it has a lot of false positives as well")]
[FieldDefinition(8, Label = "Enable Sonarr Compatibility", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr try to add Season information into Release names, without this Sonarr can't match any Seasons, but it has a lot of false positives as well")]
public bool EnableSonarrCompatibility { get; set; }
[FieldDefinition(6, Label = "Use Filenames for Single Episodes", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr replace AnimeBytes release names with the actual filename, this currently only works for single episode releases")]
[FieldDefinition(9, Label = "Use Filenames for Single Episodes", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr replace AnimeBytes release names with the actual filename, this currently only works for single episode releases")]
public bool UseFilenameForSingleEpisodes { get; set; }
[FieldDefinition(7, Label = "Add Japanese title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add Japanese titles as synonyms, i.e kanji/hiragana/katakana.")]
[FieldDefinition(10, Label = "Add Japanese title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add Japanese titles as synonyms, i.e kanji/hiragana/katakana.")]
public bool AddJapaneseTitle { get; set; }
[FieldDefinition(8, Label = "Add Romaji title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add Romaji title as a synonym, i.e \"Shingeki no Kyojin\" with Attack on Titan")]
[FieldDefinition(11, Label = "Add Romaji title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add Romaji title as a synonym, i.e \"Shingeki no Kyojin\" with Attack on Titan")]
public bool AddRomajiTitle { get; set; }
[FieldDefinition(9, Label = "Add alternative title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add alternative title as a synonym, i.e \"AoT\" with Attack on Titan, but also \"Attack on Titan Season 4\" Instead of \"Attack on Titan: The Final Season\"")]
[FieldDefinition(12, Label = "Add alternative title as a synonym", Type = FieldType.Checkbox, HelpText = "Makes Prowlarr add alternative title as a synonym, i.e \"AoT\" with Attack on Titan, but also \"Attack on Titan Season 4\" Instead of \"Attack on Titan: The Final Season\"")]
public bool AddAlternativeTitle { get; set; }
public override NzbDroneValidationResult Validate()
@ -607,20 +627,13 @@ namespace NzbDrone.Core.Indexers.Definitions
public class AnimeBytesResponse
{
[JsonProperty("Matches")]
public long Matches { get; set; }
[JsonProperty("Limit")]
public long Limit { get; set; }
[JsonProperty("Results")]
[JsonConverter(typeof(ParseStringConverter))]
public long Results { get; set; }
public int Matches { get; set; }
[JsonProperty("Groups")]
public Group[] Groups { get; set; }
public AnimeBytesGroup[] Groups { get; set; }
}
public class Group
public class AnimeBytesGroup
{
[JsonProperty("ID")]
public long Id { get; set; }
@ -635,44 +648,19 @@ namespace NzbDrone.Core.Indexers.Definitions
public string GroupName { get; set; }
[JsonProperty("SeriesID")]
[JsonConverter(typeof(ParseStringConverter))]
public long SeriesId { get; set; }
public long? SeriesId { get; set; }
[JsonProperty("SeriesName")]
public string SeriesName { get; set; }
[JsonProperty("Artists")]
public object Artists { get; set; }
[JsonProperty("Year")]
[JsonConverter(typeof(ParseStringConverter))]
public long Year { get; set; }
public int? Year { get; set; }
[JsonProperty("Image")]
public Uri Image { get; set; }
[JsonProperty("Synonymns")]
[JsonConverter(typeof(SynonymnsConverter))]
public Synonymns? Synonymns { get; set; }
[JsonProperty("Snatched")]
public long Snatched { get; set; }
[JsonProperty("Comments")]
public long Comments { get; set; }
[JsonProperty("Links")]
[JsonConverter(typeof(LinksUnionConverter))]
public LinksUnion? Links { get; set; }
[JsonProperty("Votes")]
public long Votes { get; set; }
public string Image { get; set; }
[JsonProperty("AvgVote")]
public double AvgVote { get; set; }
[JsonProperty("Associations")]
public object Associations { get; set; }
[JsonProperty("SynonymnsV2", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<string, string> Synonymns { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
@ -680,59 +668,26 @@ namespace NzbDrone.Core.Indexers.Definitions
[JsonProperty("DescriptionHTML")]
public string DescriptionHtml { get; set; }
[JsonProperty("EpCount")]
public long EpCount { get; set; }
[JsonProperty("StudioList")]
public string StudioList { get; set; }
[JsonProperty("PastWeek")]
public long PastWeek { get; set; }
[JsonProperty("Incomplete")]
public bool Incomplete { get; set; }
[JsonProperty("Ongoing")]
public bool Ongoing { get; set; }
[JsonProperty("Tags")]
public List<string> Tags { get; set; }
[JsonProperty("Torrents")]
public List<Torrent> Torrents { get; set; }
}
public class LinksClass
{
[JsonProperty("ANN", NullValueHandling = NullValueHandling.Ignore)]
public Uri Ann { get; set; }
[JsonProperty("Manga-Updates", NullValueHandling = NullValueHandling.Ignore)]
public Uri MangaUpdates { get; set; }
[JsonProperty("Wikipedia", NullValueHandling = NullValueHandling.Ignore)]
public Uri Wikipedia { get; set; }
[JsonProperty("MAL", NullValueHandling = NullValueHandling.Ignore)]
public Uri Mal { get; set; }
[JsonProperty("AniDB", NullValueHandling = NullValueHandling.Ignore)]
public Uri AniDb { get; set; }
public List<AnimeBytesTorrent> Torrents { get; set; }
}
public class Torrent
public class AnimeBytesTorrent
{
[JsonProperty("ID")]
public long Id { get; set; }
[JsonProperty("EditionData")]
public EditionData EditionData { get; set; }
public AnimeBytesEditionData EditionData { get; set; }
[JsonProperty("RawDownMultiplier")]
public double? RawDownMultiplier { get; set; }
public double RawDownMultiplier { get; set; }
[JsonProperty("RawUpMultiplier")]
public double? RawUpMultiplier { get; set; }
public double RawUpMultiplier { get; set; }
[JsonProperty("Link")]
public Uri Link { get; set; }
@ -756,13 +711,13 @@ namespace NzbDrone.Core.Indexers.Definitions
public int FileCount { get; set; }
[JsonProperty("FileList")]
public List<File> Files { get; set; }
public List<AnimeBytesFile> Files { get; set; }
[JsonProperty("UploadTime")]
public DateTimeOffset UploadTime { get; set; }
public string UploadTime { get; set; }
}
public class File
public class AnimeBytesFile
{
[JsonProperty("filename")]
public string FileName { get; set; }
@ -771,146 +726,9 @@ namespace NzbDrone.Core.Indexers.Definitions
public string FileSize { get; set; }
}
public class EditionData
public class AnimeBytesEditionData
{
[JsonProperty("EditionTitle")]
public string EditionTitle { get; set; }
}
public struct LinksUnion
{
public List<object> AnythingArray;
public LinksClass LinksClass;
public static implicit operator LinksUnion(List<object> anythingArray) => new LinksUnion { AnythingArray = anythingArray };
public static implicit operator LinksUnion(LinksClass linksClass) => new LinksUnion { LinksClass = linksClass };
}
public struct Synonymns
{
public List<string> StringArray;
public Dictionary<string, string> StringMap;
public static implicit operator Synonymns(List<string> stringArray) => new Synonymns { StringArray = stringArray };
public static implicit operator Synonymns(Dictionary<string, string> stringMap) => new Synonymns { StringMap = stringMap };
}
internal class LinksUnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(LinksUnion) || t == typeof(LinksUnion?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<LinksClass>(reader);
return new LinksUnion { LinksClass = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<List<object>>(reader);
return new LinksUnion { AnythingArray = arrayValue };
case JsonToken.Null:
return null;
}
throw new Exception("Cannot unmarshal type LinksUnion");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (LinksUnion)untypedValue;
if (value.AnythingArray != null)
{
serializer.Serialize(writer, value.AnythingArray);
return;
}
if (value.LinksClass != null)
{
serializer.Serialize(writer, value.LinksClass);
}
serializer.Serialize(writer, null);
}
public static readonly LinksUnionConverter Singleton = new LinksUnionConverter();
}
internal class ParseStringConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<string>(reader);
if (long.TryParse(value, out var l))
{
return l;
}
throw new Exception("Cannot unmarshal type long");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (long)untypedValue;
serializer.Serialize(writer, value.ToString());
}
public static readonly ParseStringConverter Singleton = new ParseStringConverter();
}
internal class SynonymnsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Synonymns) || t == typeof(Synonymns?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<Dictionary<string, string>>(reader);
return new Synonymns { StringMap = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<List<string>>(reader);
return new Synonymns { StringArray = arrayValue };
case JsonToken.Null:
return null;
}
throw new Exception("Cannot unmarshal type Synonymns");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (Synonymns)untypedValue;
if (value.StringArray != null)
{
serializer.Serialize(writer, value.StringArray);
return;
}
if (value.StringMap != null)
{
serializer.Serialize(writer, value.StringMap);
}
serializer.Serialize(writer, null);
}
public static readonly SynonymnsConverter Singleton = new SynonymnsConverter();
}
}

Loading…
Cancel
Save