From f0d8c88505bc6a199f2704ee10c7c1d963bb0ff7 Mon Sep 17 00:00:00 2001 From: Diego Heras Date: Sun, 8 Nov 2020 02:45:03 +0100 Subject: [PATCH] magnetico: add new local dht tracker. #2684 (#10136) https://github.com/boramalper/magnetico --- README.md | 1 + src/Jackett.Common/Indexers/Magnetico.cs | 168 +++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 src/Jackett.Common/Indexers/Magnetico.cs diff --git a/README.md b/README.md index 89c1e2285..fa8eca52f 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/ * Kinozal * Korsar * LostFilm.tv + * Magnetico (Local DHT) [[site](https://github.com/boramalper/magnetico)] * MVGroup Forum * MVGroup Main * Marine Tracker diff --git a/src/Jackett.Common/Indexers/Magnetico.cs b/src/Jackett.Common/Indexers/Magnetico.cs new file mode 100644 index 000000000..1692db7b4 --- /dev/null +++ b/src/Jackett.Common/Indexers/Magnetico.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Jackett.Common.Models; +using Jackett.Common.Models.IndexerConfig; +using Jackett.Common.Services.Interfaces; +using Jackett.Common.Utils; +using Newtonsoft.Json.Linq; +using NLog; +using WebClient = Jackett.Common.Utils.Clients.WebClient; + +namespace Jackett.Common.Indexers +{ + [ExcludeFromCodeCoverage] + public class Magnetico : BaseWebIndexer + { + private string SearchURl => SiteLink + "api/v0.1/torrents"; + private string TorrentsUrl => SiteLink + "torrents"; + + private new ConfigurationDataBasicLogin configData => (ConfigurationDataBasicLogin)base.configData; + + public Magnetico(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps) + : base(id: "magnetico", + name: "Magnetico (Local DHT)", + description: "Magnetico is a self-hosted BitTorrent DHT search engine", + link: "http://127.0.0.1:8080/", + caps: new TorznabCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q + }, + MusicSearchParams = new List + { + MusicSearchParam.Q + }, + BookSearchParams = new List + { + BookSearchParam.Q + } + }, + configService: configService, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationDataBasicLogin("Configure the URL, username and password from your local magneticow.
Default credentials are: username=username, password=password.")) + + { + Encoding = Encoding.UTF8; + Language = "en-us"; + Type = "semi-private"; + + var sort = new ConfigurationData.SelectItem(new Dictionary + { + {"DISCOVERED_ON", "discovered"}, + {"TOTAL_SIZE", "size"}, + {"N_FILES", "files"}, + {"RELEVANCE", "relevance"} + }) + { Name = "Sort requested from site", Value = "discovered" }; + configData.AddDynamic("sort", sort); + + var order = new ConfigurationData.SelectItem(new Dictionary + { + {"false", "desc"}, + {"true", "asc"} + }) + { Name = "Order requested from site", Value = "false" }; + configData.AddDynamic("order", order); + + AddCategoryMapping("1", TorznabCatType.Other); + } + + public override async Task ApplyConfiguration(JToken configJson) + { + LoadValuesFromJson(configJson); + + await PerformQuery(new TorznabQuery()); // throws exception if there is an error + + IsConfigured = true; + SaveConfig(); + return IndexerConfigurationStatus.Completed; + } + + protected override async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + + var qc = new NameValueCollection + { + {"query", query.GetQueryString()}, + {"orderBy", ((ConfigurationData.SelectItem)configData.GetDynamic("sort")).Value}, + {"ascending", ((ConfigurationData.SelectItem)configData.GetDynamic("order")).Value}, + {"limit", "100"} + }; + var searchUrl = SearchURl + "?" + qc.GetQueryString(); + + var results = await RequestWithCookiesAsync(searchUrl, headers: GetAuthorizationHeaders()); + if (results.Status != HttpStatusCode.OK) + throw new Exception($"Error code: {results.Status}"); + + try + { + var torrents = JArray.Parse(results.ContentString).ToObject>(); + foreach (var torrent in torrents) + { + var details = new Uri($"{TorrentsUrl}/{torrent.infoHash}"); + var publishDate = DateTimeUtil.UnixTimestampToDateTime(torrent.discoveredOn); + + var release = new ReleaseInfo + { + Title = torrent.name, + Comments = new Uri($"{TorrentsUrl}/{torrent.infoHash}"), + Guid = details, + InfoHash = torrent.infoHash, + PublishDate = publishDate, + Category = new List{ TorznabCatType.Other.ID }, + Size = torrent.size, + Files = torrent.nFiles, + Seeders = 1, + Peers = 1, + DownloadVolumeFactor = 0, + UploadVolumeFactor = 1 + }; + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results.ContentString, ex); + } + + return releases; + } + + private Dictionary GetAuthorizationHeaders() + { + var username = configData.Username.Value; + var password = configData.Password.Value; + var encoded = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); + var headers = new Dictionary + { + {"Authorization", "Basic " + encoded} + }; + return headers; + } + + private class Torrent + { + public string infoHash { get; set; } + public long id { get; set; } + public string name { get; set; } + public long size { get; set; } + public long discoveredOn { get; set; } + public long nFiles { get; set; } + public long relevance { get; set; } + } + } +}