From b93a9719feea6e50feb7a84a8df83af2c072461b Mon Sep 17 00:00:00 2001 From: Devin Buhl Date: Thu, 12 Jan 2017 23:06:26 -0500 Subject: [PATCH] initial awesomeHD support --- .../Indexers/AwesomeHD/AwesomeHD.cs | 30 ++++++ .../Indexers/AwesomeHD/AwesomeHDApi.cs | 80 ++++++++++++++++ .../AwesomeHD/AwesomeHDRequestGenerator.cs | 61 ++++++++++++ .../Indexers/AwesomeHD/AwesomeHDRssParser.cs | 92 +++++++++++++++++++ .../Indexers/AwesomeHD/AwesomeHDSettings.cs | 37 ++++++++ src/NzbDrone.Core/NzbDrone.Core.csproj | 5 + 6 files changed, 305 insertions(+) create mode 100644 src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs create mode 100644 src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs create mode 100644 src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRequestGenerator.cs create mode 100644 src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs create mode 100644 src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs diff --git a/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs new file mode 100644 index 000000000..2ecb61b84 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHD.cs @@ -0,0 +1,30 @@ +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.Indexers.AwesomeHD +{ + public class AwesomeHD : HttpIndexerBase + { + public override string Name => "AwesomeHD"; + public override DownloadProtocol Protocol => DownloadProtocol.Torrent; + public override bool SupportsRss => false; + public override bool SupportsSearch => true; + public override int PageSize => 50; + + public AwesomeHD(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger) + : base(httpClient, indexerStatusService, configService, parsingService, logger) + { } + + public override IIndexerRequestGenerator GetRequestGenerator() + { + return new AwesomeHDRequestGenerator() { Settings = Settings }; + } + + public override IParseIndexerResponse GetParser() + { + return new AwesomeHDRssParser(Settings); + } + } +} diff --git a/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs new file mode 100644 index 000000000..77744b4ac --- /dev/null +++ b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDApi.cs @@ -0,0 +1,80 @@ +using System; +using Newtonsoft.Json; + +using System.Xml.Serialization; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.AwesomeHD +{ + public class Torrent + { + [XmlElement(ElementName = "id")] + public string Id { get; set; } + [XmlElement(ElementName = "groupid")] + public string GroupId { get; set; } + [XmlElement(ElementName = "time")] + public DateTime Time { get; set; } + [XmlElement(ElementName = "userid")] + public string Userid { get; set; } + [XmlElement(ElementName = "size")] + public long Size { get; set; } + [XmlElement(ElementName = "snatched")] + public string Snatched { get; set; } + [XmlElement(ElementName = "seeders")] + public string Seeders { get; set; } + [XmlElement(ElementName = "leechers")] + public string Leechers { get; set; } + [XmlElement(ElementName = "releasegroup")] + public string Releasegroup { get; set; } + [XmlElement(ElementName = "resolution")] + public string Resolution { get; set; } + [XmlElement(ElementName = "media")] + public string Media { get; set; } + [XmlElement(ElementName = "format")] + public string Format { get; set; } + [XmlElement(ElementName = "encoding")] + public string Encoding { get; set; } + [XmlElement(ElementName = "audioformat")] + public string Audioformat { get; set; } + [XmlElement(ElementName = "audiobitrate")] + public string Audiobitrate { get; set; } + [XmlElement(ElementName = "audiochannels")] + public string Audiochannels { get; set; } + [XmlElement(ElementName = "subtitles")] + public string Subtitles { get; set; } + [XmlElement(ElementName = "encodestatus")] + public string Encodestatus { get; set; } + [XmlElement(ElementName = "freeleech")] + public string Freeleech { get; set; } + [XmlElement(ElementName = "cover")] + public string Cover { get; set; } + [XmlElement(ElementName = "smallcover")] + public string Smallcover { get; set; } + [XmlElement(ElementName = "year")] + public string Year { get; set; } + [XmlElement(ElementName = "name")] + public string Name { get; set; } + [XmlElement(ElementName = "imdb")] + public string Imdb { get; set; } + [XmlElement(ElementName = "type")] + public string Type { get; set; } + [XmlElement(ElementName = "plotoutline")] + public string Plotoutline { get; set; } + } + + public class SearchResults + { + [XmlElement(ElementName = "authkey")] + public string AuthKey { get; set; } + [XmlElement(ElementName = "torrent")] + public List Torrent { get; set; } + } + + public class AwesomeHDSearchResponse + { + [XmlElement(ElementName = "?xml")] + public string Xml { get; set; } + [XmlElement(ElementName = "searchresults")] + public SearchResults SearchResults { get; set; } + } +} diff --git a/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRequestGenerator.cs b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRequestGenerator.cs new file mode 100644 index 000000000..7e42f9ad6 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRequestGenerator.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Http; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.IndexerSearch.Definitions; + +namespace NzbDrone.Core.Indexers.AwesomeHD +{ + public class AwesomeHDRequestGenerator : IIndexerRequestGenerator + { + public AwesomeHDSettings Settings { get; set; } + + public virtual IndexerPageableRequestChain GetRecentRequests() + { + var pageableRequests = new IndexerPageableRequestChain(); + + pageableRequests.Add(GetRequest("tt2488496")); + + return pageableRequests; + } + + public virtual IndexerPageableRequestChain GetSearchRequests(SingleEpisodeSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public virtual IndexerPageableRequestChain GetSearchRequests(AnimeEpisodeSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public virtual IndexerPageableRequestChain GetSearchRequests(DailyEpisodeSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public virtual IndexerPageableRequestChain GetSearchRequests(SeasonSearchCriteria searchCriteria) + { + return new IndexerPageableRequestChain(); + } + + public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria) + { + var pageableRequests = new IndexerPageableRequestChain(); + pageableRequests.Add(GetRequest(searchCriteria.Movie.ImdbId)); + return pageableRequests; + } + + private IEnumerable GetRequest(string searchParameters) + { + var request = new IndexerRequest(string.Format("{0}/searchapi.php?action=imdbsearch&passkey={1}&imdb={2}", Settings.BaseUrl.Trim().TrimEnd('/'), Settings.Passkey.Trim(), searchParameters), HttpAccept.Rss); + yield return request; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs new file mode 100644 index 000000000..e7a2632a0 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDRssParser.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Net; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NzbDrone.Common.Http; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Parser.Model; +using System; +using System.Linq; +using System.Xml; + +namespace NzbDrone.Core.Indexers.AwesomeHD +{ + public class AwesomeHDRssParser : IParseIndexerResponse + { + private readonly AwesomeHDSettings _settings; + + public AwesomeHDRssParser(AwesomeHDSettings settings) + { + _settings = settings; + } + + public IList ParseResponse(IndexerResponse indexerResponse) + { + var torrentInfos = new List(); + + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new IndexerException(indexerResponse, + "Unexpected response status {0} code from API request", + indexerResponse.HttpResponse.StatusCode); + } + + // Hacky ¯\_(ツ)_/¯ + XmlDocument doc = new XmlDocument(); + doc.LoadXml(indexerResponse.Content); + + var json = JsonConvert.SerializeXmlNode(doc); + + Console.WriteLine(json); + + var jsonResponse = JsonConvert.DeserializeObject(json); + + if (jsonResponse == null) + { + throw new IndexerException(indexerResponse, "Unexpected response from request"); + } + + foreach (var torrent in jsonResponse.SearchResults.Torrent) + { + var id = torrent.Id; + var title = $"{torrent.Name}.{torrent.Year}.{torrent.Resolution}.{torrent.Media}.{torrent.Encoding}.{torrent.Audioformat}-{torrent.Releasegroup}"; + + torrentInfos.Add(new TorrentInfo() + { + Guid = string.Format("AwesomeHD-{0}", id), + Title = title, + Size = torrent.Size, + DownloadUrl = GetDownloadUrl(id, jsonResponse.SearchResults.AuthKey, _settings.Passkey), + InfoUrl = GetInfoUrl(torrent.GroupId, id), + Seeders = int.Parse(torrent.Seeders), + Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders), + PublishDate = torrent.Time.ToUniversalTime() + }); + } + + return torrentInfos.OrderByDescending(o => ((dynamic)o).Seeders).ToArray(); + } + + private string GetDownloadUrl(string torrentId, string authKey, string passKey) + { + var url = new HttpUri(_settings.BaseUrl) + .CombinePath("/torrents.php") + .AddQueryParam("action", "download") + .AddQueryParam("id", torrentId) + .AddQueryParam("authkey", authKey) + .AddQueryParam("torrent_pass", passKey); + + return url.FullUri; + } + + private string GetInfoUrl(string groupId, string torrentId) + { + var url = new HttpUri(_settings.BaseUrl) + .CombinePath("/torrents.php") + .AddQueryParam("id", groupId) + .AddQueryParam("torrentid", torrentId); + + return url.FullUri; + } + } +} diff --git a/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs new file mode 100644 index 000000000..3c6f525c4 --- /dev/null +++ b/src/NzbDrone.Core/Indexers/AwesomeHD/AwesomeHDSettings.cs @@ -0,0 +1,37 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.Indexers.AwesomeHD +{ + public class AwesomeHDSettingsValidator : AbstractValidator + { + public AwesomeHDSettingsValidator() + { + RuleFor(c => c.BaseUrl).ValidRootUrl(); + RuleFor(c => c.Passkey).NotEmpty(); + } + } + + public class AwesomeHDSettings : IProviderConfig + { + private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator(); + + public AwesomeHDSettings() + { + BaseUrl = "https://awesome-hd.me"; + } + + [FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")] + public string BaseUrl { get; set; } + + [FieldDefinition(1, Label = "Passkey")] + public string Passkey { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index cd482bd90..9dfb29ec2 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -578,6 +578,7 @@ + @@ -585,6 +586,10 @@ + + + +