diff --git a/src/Jackett.Common/Indexers/Feeds/BaseNewznabIndexer.cs b/src/Jackett.Common/Indexers/Feeds/BaseNewznabIndexer.cs index c0a8060b4..27444cc12 100644 --- a/src/Jackett.Common/Indexers/Feeds/BaseNewznabIndexer.cs +++ b/src/Jackett.Common/Indexers/Feeds/BaseNewznabIndexer.cs @@ -46,10 +46,32 @@ namespace Jackett.Common.Indexers.Feeds protected virtual ReleaseInfo ResultFromFeedItem(XElement item) { var attributes = item.Descendants().Where(e => e.Name.LocalName == "attr"); - var size = long.TryParse(ReadAttribute(attributes, "size"), out var longVal) ? (long?)longVal : null; - var files = long.TryParse(ReadAttribute(attributes, "files"), out longVal) ? (long?)longVal : null; + long? size = null; + if (long.TryParse(ReadAttribute(attributes, "size"), out var longVal)) + size = (long?)longVal; + else if (long.TryParse(item.FirstValue("size"), out longVal)) + size = (long?)longVal; + long? files = null; + if (long.TryParse(ReadAttribute(attributes, "files"), out longVal)) + files = (long?)longVal; + else if (long.TryParse(item.FirstValue("files"), out longVal)) + files = (long?)longVal; + long? grabs = null; + if (item.Descendants("grabs").Any()) + grabs = long.TryParse(item.FirstValue("grabs"), out longVal) ? (long?)longVal : null; var seeders = int.TryParse(ReadAttribute(attributes, "seeders"), out var intVal) ? (int?)intVal : null; var peers = int.TryParse(ReadAttribute(attributes, "peers"), out intVal) ? (int?)intVal : null; + double? downloadvolumefactor = double.TryParse(ReadAttribute(attributes, "downloadvolumefactor"), out var doubleVal) ? (double?)doubleVal : null; + double? uploadvolumefactor = double.TryParse(ReadAttribute(attributes, "uploadvolumefactor"), out doubleVal) ? (double?)doubleVal : null; + var magnet = ReadAttribute(attributes, "magneturl"); + var magneturi = !string.IsNullOrEmpty(magnet) ? new Uri(magnet) : null; + var categories = item.Descendants().Where(e => e.Name == "category" && int.TryParse(e.Value, out var categoryid)); + List categoryids = null; + if (categories.Any()) + categoryids = new List { int.Parse(categories.Last(e => !string.IsNullOrEmpty(e.Value)).Value) }; + else + categoryids = new List { int.Parse(attributes.First(e => e.Attribute("name").Value == "category").Attribute("value").Value) }; + var release = new ReleaseInfo { Title = item.FirstValue("title"), @@ -57,15 +79,19 @@ namespace Jackett.Common.Indexers.Feeds Link = new Uri(item.FirstValue("link")), Details = new Uri(item.FirstValue("comments")), PublishDate = DateTime.Parse(item.FirstValue("pubDate")), - Category = new List { int.Parse(attributes.First(e => e.Attribute("name").Value == "category").Attribute("value").Value) }, + Category = categoryids, Size = size, Files = files, Description = item.FirstValue("description"), + Grabs = grabs, Seeders = seeders, Peers = peers, InfoHash = attributes.First(e => e.Attribute("name").Value == "infohash").Attribute("value").Value, - MagnetUri = new Uri(attributes.First(e => e.Attribute("name").Value == "magneturl").Attribute("value").Value) + DownloadVolumeFactor = downloadvolumefactor, + UploadVolumeFactor = uploadvolumefactor }; + if (magneturi != null) + release.MagnetUri = magneturi; return release; } diff --git a/src/Jackett.Common/Indexers/Feeds/MoreThanTVAPI.cs b/src/Jackett.Common/Indexers/Feeds/MoreThanTVAPI.cs new file mode 100644 index 000000000..f6096bc1b --- /dev/null +++ b/src/Jackett.Common/Indexers/Feeds/MoreThanTVAPI.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Jackett.Common.Models; +using Jackett.Common.Models.IndexerConfig; +using Jackett.Common.Services.Interfaces; +using Jackett.Common.Utils; +using Jackett.Common.Utils.Clients; +using Newtonsoft.Json.Linq; +using NLog; +using static Jackett.Common.Models.IndexerConfig.ConfigurationData; + +namespace Jackett.Common.Indexers.Feeds +{ + [ExcludeFromCodeCoverage] + public class MoreThanTVAPI : BaseNewznabIndexer + { + private new ConfigurationDataAPIKey configData => (ConfigurationDataAPIKey)base.configData; + + public MoreThanTVAPI(IIndexerConfigurationService configService, WebClient client, Logger logger, + IProtectionService ps, ICacheService cs) + : base(id: "morethantv-api", + name: "MoreThanTV (API)", + description: "Private torrent tracker for TV / MOVIES", + link: "https://www.morethantv.me/", + caps: new TorznabCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId + }, + MovieSearchParams = new List + { + MovieSearchParam.Q, MovieSearchParam.ImdbId + } + }, + configService: configService, + client: client, + logger: logger, + p: ps, + cs: cs, + configData: new ConfigurationDataAPIKey()) + { + Encoding = Encoding.UTF8; + Language = "en-US"; + Type = "private"; + + AddCategoryMapping(TorznabCatType.TVSD.ID, TorznabCatType.TVSD); + AddCategoryMapping(TorznabCatType.TVHD.ID, TorznabCatType.TVHD); + AddCategoryMapping(TorznabCatType.TVUHD.ID, TorznabCatType.TVUHD); + AddCategoryMapping(TorznabCatType.TVSport.ID, TorznabCatType.TVSport); + AddCategoryMapping(TorznabCatType.MoviesSD.ID, TorznabCatType.MoviesSD); + AddCategoryMapping(TorznabCatType.MoviesHD.ID, TorznabCatType.MoviesHD); + AddCategoryMapping(TorznabCatType.MoviesUHD.ID, TorznabCatType.MoviesUHD); + AddCategoryMapping(TorznabCatType.MoviesBluRay.ID, TorznabCatType.MoviesBluRay); + + configData.AddDynamic("keyInfo", new DisplayInfoConfigurationItem(String.Empty, "Find or Generate a new API Key by accessing your MoreThanTV account User Security page and scrolling to the API Keys section.")); + } + + public override Task ApplyConfiguration(JToken configJson) + { + LoadValuesFromJson(configJson); + + if (configData.Key.Value.Length != 32) + throw new Exception("Invalid API Key configured. Expected length: 32"); + + IsConfigured = true; + SaveConfig(); + + return Task.FromResult(IndexerConfigurationStatus.RequiresTesting); + } + + protected override async Task> PerformQuery(TorznabQuery query) + { + var requestUri = FeedUri.ToString(); + var qc = new NameValueCollection + { + {"apikey", configData.Key.Value}, + {"limit", "100"}, + {"extended", "1"}, + }; + if (query.IsTVSearch) + qc.Add("t", "tvsearch"); + else if (query.IsMovieSearch) + qc.Add("t", "movie"); + else + qc.Add("t", "search"); + if (!string.IsNullOrWhiteSpace(query.SearchTerm)) + qc.Add("q", query.SearchTerm); + + var queryCats = new List(); + foreach (var queryCategory in query.Categories) + { + queryCats.Add(queryCategory); + } + if (queryCats.Any()) + qc.Add("cat", string.Join(",", queryCats)); + if (!string.IsNullOrWhiteSpace(query.ImdbID)) + qc.Add("imdbid", query.ImdbID); + if (query.TvdbID != null) + qc.Add("tvdbid", query.TvdbID.ToString()); + if (!string.IsNullOrWhiteSpace(query.Episode)) + qc.Add("ep", query.Episode); + if (query.Season > 0) + qc.Add("season", query.Season.ToString()); + requestUri = requestUri + "?" + qc.GetQueryString(); + var request = new WebRequest + { + Url = requestUri, + Type = RequestType.GET, + Encoding = Encoding + }; + var result = await webclient.GetResultAsync(request); + + var results = ParseFeedForResults(result.ContentString); + + return results; + } + + protected override ReleaseInfo ResultFromFeedItem(XElement item) + { + var release = base.ResultFromFeedItem(item); + var enclosures = item.Descendants("enclosure").Where(e => e.Attribute("type").Value == "application/x-bittorrent"); + if (enclosures.Any()) + { + var enclosure = enclosures.First().Attribute("url").Value; + release.Link = new Uri(enclosure); + } + // add some default values if none returned by feed + release.Seeders = release.Seeders > 0 ? release.Seeders : 0; + release.Peers = release.Peers > 0 ? release.Peers : 0; + release.DownloadVolumeFactor = release.DownloadVolumeFactor > 0 ? release.DownloadVolumeFactor : 0; + release.UploadVolumeFactor = release.UploadVolumeFactor > 0 ? release.UploadVolumeFactor : 1; + return release; + } + + protected override Uri FeedUri => new Uri(SiteLink + "api/torznab"); + } +}