parent
d532a69edc
commit
580113d6ce
@ -0,0 +1,373 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using Newtonsoft.Json.Linq;
|
||||
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 TorrentSyndikat : TorrentIndexerBase<TorrentSyndikatSettings>
|
||||
{
|
||||
public override string Name => "TorrentSyndikat";
|
||||
public override string[] IndexerUrls => new[] { "https://torrent-syndikat.org/" };
|
||||
public override string Description => "A German general tracker";
|
||||
public override string Language => "de-de";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public TorrentSyndikat(IHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TorrentSyndikatRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new TorrentSyndikatParser(Settings, Capabilities.Categories);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.PC, "Apps / Windows");
|
||||
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.PC, "Apps / Linux");
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCMac, "Apps / MacOS");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PC, "Apps / Misc");
|
||||
|
||||
caps.Categories.AddCategoryMapping(50, NewznabStandardCategory.PCGames, "Spiele / Windows");
|
||||
caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.PCGames, "Spiele / MacOS");
|
||||
caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PCGames, "Spiele / Linux");
|
||||
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.ConsoleOther, "Spiele / Playstation");
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.ConsoleOther, "Spiele / Nintendo");
|
||||
caps.Categories.AddCategoryMapping(32, NewznabStandardCategory.ConsoleOther, "Spiele / XBOX");
|
||||
|
||||
caps.Categories.AddCategoryMapping(42, NewznabStandardCategory.MoviesUHD, "Filme / 2160p");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.MoviesHD, "Filme / 1080p");
|
||||
caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.MoviesHD, "Filme / 720p");
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.MoviesSD, "Filme / SD");
|
||||
|
||||
caps.Categories.AddCategoryMapping(43, NewznabStandardCategory.TVUHD, "Serien / 2160p");
|
||||
caps.Categories.AddCategoryMapping(53, NewznabStandardCategory.TVHD, "Serien / 1080p");
|
||||
caps.Categories.AddCategoryMapping(54, NewznabStandardCategory.TVHD, "Serien / 720p");
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.TVSD, "Serien / SD");
|
||||
caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.TVSport, "Serien / Sport");
|
||||
|
||||
caps.Categories.AddCategoryMapping(44, NewznabStandardCategory.TVUHD, "Serienpacks / 2160p");
|
||||
caps.Categories.AddCategoryMapping(55, NewznabStandardCategory.TVHD, "Serienpacks / 1080p");
|
||||
caps.Categories.AddCategoryMapping(56, NewznabStandardCategory.TVHD, "Serienpacks / 720p");
|
||||
caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.TVSD, "Serienpacks / SD");
|
||||
|
||||
caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.AudioLossless, "Audio / Musik / FLAC");
|
||||
caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.AudioMP3, "Audio / Musik / MP3");
|
||||
caps.Categories.AddCategoryMapping(35, NewznabStandardCategory.AudioOther, "Audio / Other");
|
||||
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.AudioAudiobook, "Audio / aBooks");
|
||||
caps.Categories.AddCategoryMapping(33, NewznabStandardCategory.AudioVideo, "Audio / Videos");
|
||||
|
||||
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.Books, "Misc / eBooks");
|
||||
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.PCMobileOther, "Misc / Mobile");
|
||||
caps.Categories.AddCategoryMapping(39, NewznabStandardCategory.Other, "Misc / Bildung");
|
||||
|
||||
caps.Categories.AddCategoryMapping(36, NewznabStandardCategory.TVForeign, "Englisch / Serien");
|
||||
caps.Categories.AddCategoryMapping(57, NewznabStandardCategory.TVForeign, "Englisch / Serienpacks");
|
||||
caps.Categories.AddCategoryMapping(37, NewznabStandardCategory.MoviesForeign, "Englisch / Filme");
|
||||
caps.Categories.AddCategoryMapping(47, NewznabStandardCategory.Books, "Englisch / eBooks");
|
||||
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.Other, "Englisch / Bildung");
|
||||
caps.Categories.AddCategoryMapping(49, NewznabStandardCategory.TVSport, "Englisch / Sport");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentSyndikatRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public TorrentSyndikatSettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
|
||||
public TorrentSyndikatRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchString = term;
|
||||
var queryCollection = new NameValueCollection { { "apikey", Settings.ApiKey } };
|
||||
|
||||
queryCollection.Add("limit", "50"); // Default 30
|
||||
queryCollection.Add("ponly", Settings.ProductsOnly ? "true" : "false");
|
||||
foreach (var releaseType in Settings.ReleaseTypes)
|
||||
{
|
||||
queryCollection.Add("release_type", ((TorrentSyndikatReleaseTypes)releaseType).ToString().Replace("_", "-"));
|
||||
}
|
||||
|
||||
if (imdbId != null)
|
||||
{
|
||||
queryCollection.Add("imdbId", imdbId);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
// Suffix the first occurence of `s01` surrounded by whitespace with *
|
||||
// That way we also search for single episodes in a whole season search
|
||||
var regex = new Regex(@"(^|\s)(s\d{2})(\s|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
queryCollection.Add("searchstring", regex.Replace(searchString.Trim(), @"$1$2*$3"));
|
||||
}
|
||||
|
||||
var cats = string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories));
|
||||
if (!string.IsNullOrEmpty(cats))
|
||||
{
|
||||
queryCollection.Add("cats", cats);
|
||||
}
|
||||
|
||||
var searchUrl = string.Format("{0}/api_9djWe8Tb2NE3p6opyqnh/v1/browse.php?{1}", Settings.BaseUrl.TrimEnd('/'), queryCollection.GetQueryString());
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria 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, searchCriteria.ImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), 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;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class TorrentSyndikatParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly TorrentSyndikatSettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
|
||||
public TorrentSyndikatParser(TorrentSyndikatSettings settings, IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
// TODO Deserialize to TorrentSyndikatResponse Type
|
||||
var jsonContent = JObject.Parse(indexerResponse.Content);
|
||||
|
||||
foreach (var row in jsonContent.Value<JArray>("rows"))
|
||||
{
|
||||
var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
var id = row.Value<string>("id");
|
||||
var details = _settings.BaseUrl + "details.php?id=" + id;
|
||||
var seeders = row.Value<int>("seeders");
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 96 * 60 * 60,
|
||||
DownloadVolumeFactor = 1,
|
||||
UploadVolumeFactor = 1,
|
||||
Guid = details,
|
||||
InfoUrl = details,
|
||||
DownloadUrl = _settings.BaseUrl + "download.php?id=" + id,
|
||||
Title = row.Value<string>("name"),
|
||||
Categories = _categories.MapTrackerCatToNewznab(row.Value<int>("category").ToString()),
|
||||
PublishDate = dateTime.AddSeconds(row.Value<long>("added")).ToLocalTime(),
|
||||
Size = row.Value<long>("size"),
|
||||
Files = row.Value<int>("numfiles"),
|
||||
Seeders = seeders,
|
||||
Peers = seeders + row.Value<int>("leechers"),
|
||||
Grabs = row.Value<int>("snatched"),
|
||||
ImdbId = row.Value<int?>("imdbId").GetValueOrDefault(),
|
||||
TvdbId = row.Value<int?>("tvdbId").GetValueOrDefault(),
|
||||
TmdbId = row.Value<int?>("tmdbId").GetValueOrDefault()
|
||||
};
|
||||
|
||||
var poster = row.Value<string>("poster");
|
||||
if (!string.IsNullOrWhiteSpace(poster))
|
||||
{
|
||||
release.PosterUrl = _settings.BaseUrl + poster.Substring(1);
|
||||
}
|
||||
|
||||
var descriptions = new List<string>();
|
||||
var title = row.Value<string>("title");
|
||||
var titleOrigin = row.Value<string>("title_origin");
|
||||
var year = row.Value<int?>("year");
|
||||
var pid = row.Value<int?>("pid");
|
||||
var releaseType = row.Value<string>("release_type");
|
||||
var tags = row.Value<JArray>("tags");
|
||||
var genres = row.Value<JArray>("genres");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
descriptions.Add("Title: " + title);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(titleOrigin))
|
||||
{
|
||||
descriptions.Add("Original Title: " + titleOrigin);
|
||||
}
|
||||
|
||||
if (year > 0)
|
||||
{
|
||||
descriptions.Add("Year: " + year);
|
||||
}
|
||||
|
||||
if (pid > 0)
|
||||
{
|
||||
descriptions.Add("Product-Link: " + _settings.BaseUrl + "product.php?pid=" + pid);
|
||||
}
|
||||
|
||||
if (genres != null && genres.Any())
|
||||
{
|
||||
descriptions.Add("Genres: " + string.Join(", ", genres));
|
||||
}
|
||||
|
||||
if (tags != null && tags.Any())
|
||||
{
|
||||
descriptions.Add("Tags: " + string.Join(", ", tags));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(releaseType))
|
||||
{
|
||||
descriptions.Add("Release Type: " + releaseType);
|
||||
}
|
||||
|
||||
if (descriptions.Any())
|
||||
{
|
||||
release.Description = string.Join(Environment.NewLine, descriptions);
|
||||
}
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class TorrentSyndikatSettingsValidator : AbstractValidator<TorrentSyndikatSettings>
|
||||
{
|
||||
public TorrentSyndikatSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.ApiKey).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentSyndikatSettings : IIndexerSettings
|
||||
{
|
||||
private static readonly TorrentSyndikatSettingsValidator Validator = new TorrentSyndikatSettingsValidator();
|
||||
|
||||
public TorrentSyndikatSettings()
|
||||
{
|
||||
ApiKey = "";
|
||||
ReleaseTypes = new List<int>();
|
||||
}
|
||||
|
||||
[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, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpText = "Site API Key")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Products Only", Type = FieldType.Checkbox, HelpText = "Limit search to torrents linked to a product")]
|
||||
public bool ProductsOnly { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Release Types", Type = FieldType.Select, SelectOptions = typeof(TorrentSyndikatReleaseTypes))]
|
||||
public IEnumerable<int> ReleaseTypes { get; set; }
|
||||
|
||||
[FieldDefinition(5)]
|
||||
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
|
||||
public enum TorrentSyndikatReleaseTypes
|
||||
{
|
||||
[FieldOption]
|
||||
P2P = 1,
|
||||
[FieldOption]
|
||||
Scene = 2,
|
||||
[FieldOption]
|
||||
O_Scene = 3
|
||||
}
|
||||
}
|
Loading…
Reference in new issue