mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-22 22:34:53 -04:00
Cleanup Search UI, Newznab Caps API
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcorn : HttpIndexerBase<PassThePopcornSettings>
|
||||
{
|
||||
public override string Name => "PassThePopcorn";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override bool SupportsRss => true;
|
||||
public override bool SupportsSearch => true;
|
||||
|
||||
public override IndexerCapabilities Capabilities => new IndexerCapabilities
|
||||
{
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
}
|
||||
};
|
||||
|
||||
public override int PageSize => 50;
|
||||
|
||||
public PassThePopcorn(IHttpClient httpClient,
|
||||
ICacheManager cacheManager,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
: base(httpClient, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new PassThePopcornRequestGenerator()
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
Logger = _logger,
|
||||
};
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new PassThePopcornParser(Settings, _logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class Director
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class Torrent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string Codec { get; set; }
|
||||
public string Resolution { get; set; }
|
||||
public bool Scene { get; set; }
|
||||
public string Size { get; set; }
|
||||
public DateTime UploadTime { get; set; }
|
||||
public string RemasterTitle { get; set; }
|
||||
public string Snatched { get; set; }
|
||||
public string Seeders { get; set; }
|
||||
public string Leechers { get; set; }
|
||||
public string ReleaseName { get; set; }
|
||||
public bool Checked { get; set; }
|
||||
public bool GoldenPopcorn { get; set; }
|
||||
public string FreeleechType { get; set; }
|
||||
}
|
||||
|
||||
public class Movie
|
||||
{
|
||||
public string GroupId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Year { get; set; }
|
||||
public string Cover { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public List<Director> Directors { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public int TotalLeechers { get; set; }
|
||||
public int TotalSeeders { get; set; }
|
||||
public int TotalSnatched { get; set; }
|
||||
public long MaxSize { get; set; }
|
||||
public string LastUploadTime { get; set; }
|
||||
public List<Torrent> Torrents { get; set; }
|
||||
}
|
||||
|
||||
public class PassThePopcornResponse
|
||||
{
|
||||
public string TotalResults { get; set; }
|
||||
public List<Movie> Movies { get; set; }
|
||||
public string Page { get; set; }
|
||||
public string AuthKey { get; set; }
|
||||
public string PassKey { get; set; }
|
||||
}
|
||||
|
||||
public class PassThePopcornAuthResponse
|
||||
{
|
||||
public string Result { get; set; }
|
||||
public string Popcron { get; set; }
|
||||
public string AntiCsrfToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcornInfo : TorrentInfo
|
||||
{
|
||||
public bool? Golden { get; set; }
|
||||
public bool? Scene { get; set; }
|
||||
public bool? Approved { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcornParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly PassThePopcornSettings _settings;
|
||||
private readonly Logger _logger;
|
||||
public PassThePopcornParser(PassThePopcornSettings settings, Logger logger)
|
||||
{
|
||||
_settings = settings;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<ReleaseInfo>();
|
||||
|
||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
// Remove cookie cache
|
||||
if (indexerResponse.HttpResponse.HasHttpRedirect && indexerResponse.HttpResponse.Headers["Location"]
|
||||
.ContainsIgnoreCase("login.php"))
|
||||
{
|
||||
CookiesUpdater(null, null);
|
||||
throw new IndexerException(indexerResponse, "We are being redirected to the PTP login page. Most likely your session expired or was killed. Try testing the indexer in the settings.");
|
||||
}
|
||||
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
|
||||
}
|
||||
|
||||
if (indexerResponse.HttpResponse.Headers.ContentType != HttpAccept.Json.Value)
|
||||
{
|
||||
if (indexerResponse.HttpResponse.Request.Url.Path.ContainsIgnoreCase("login.php"))
|
||||
{
|
||||
CookiesUpdater(null, null);
|
||||
throw new IndexerException(indexerResponse, "We are currently on the login page. Most likely your session expired or was killed. Try testing the indexer in the settings.");
|
||||
}
|
||||
|
||||
// Remove cookie cache
|
||||
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
|
||||
}
|
||||
|
||||
var jsonResponse = JsonConvert.DeserializeObject<PassThePopcornResponse>(indexerResponse.Content);
|
||||
if (jsonResponse.TotalResults == "0" ||
|
||||
jsonResponse.TotalResults.IsNullOrWhiteSpace() ||
|
||||
jsonResponse.Movies == null)
|
||||
{
|
||||
return torrentInfos;
|
||||
}
|
||||
|
||||
foreach (var result in jsonResponse.Movies)
|
||||
{
|
||||
foreach (var torrent in result.Torrents)
|
||||
{
|
||||
var id = torrent.Id;
|
||||
var title = torrent.ReleaseName;
|
||||
IndexerFlags flags = 0;
|
||||
|
||||
if (torrent.GoldenPopcorn)
|
||||
{
|
||||
flags |= IndexerFlags.PTP_Golden; //title = $"{title} 🍿";
|
||||
}
|
||||
|
||||
if (torrent.Checked)
|
||||
{
|
||||
flags |= IndexerFlags.PTP_Approved; //title = $"{title} ✔";
|
||||
}
|
||||
|
||||
if (torrent.FreeleechType == "Freeleech")
|
||||
{
|
||||
flags |= IndexerFlags.G_Freeleech;
|
||||
}
|
||||
|
||||
if (torrent.Scene)
|
||||
{
|
||||
flags |= IndexerFlags.G_Scene;
|
||||
}
|
||||
|
||||
// Only add approved torrents
|
||||
try
|
||||
{
|
||||
torrentInfos.Add(new PassThePopcornInfo()
|
||||
{
|
||||
Guid = string.Format("PassThePopcorn-{0}", id),
|
||||
Title = title,
|
||||
Size = long.Parse(torrent.Size),
|
||||
DownloadUrl = GetDownloadUrl(id, jsonResponse.AuthKey, jsonResponse.PassKey),
|
||||
InfoUrl = GetInfoUrl(result.GroupId, id),
|
||||
Seeders = int.Parse(torrent.Seeders),
|
||||
Peers = int.Parse(torrent.Leechers) + int.Parse(torrent.Seeders),
|
||||
PublishDate = torrent.UploadTime.ToUniversalTime(),
|
||||
Golden = torrent.GoldenPopcorn,
|
||||
Scene = torrent.Scene,
|
||||
Approved = torrent.Checked,
|
||||
ImdbId = result.ImdbId.IsNotNullOrWhiteSpace() ? int.Parse(result.ImdbId) : 0,
|
||||
IndexerFlags = flags
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Encountered exception parsing PTP torrent: {" +
|
||||
$"Size: {torrent.Size}" +
|
||||
$"UploadTime: {torrent.UploadTime}" +
|
||||
$"Seeders: {torrent.Seeders}" +
|
||||
$"Leechers: {torrent.Leechers}" +
|
||||
$"ReleaseName: {torrent.ReleaseName}" +
|
||||
$"ID: {torrent.Id}" +
|
||||
"}. Please immediately report this info on https://github.com/Prowlarr/Prowlarr/issues/1584.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
torrentInfos;
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
private string GetDownloadUrl(int 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, int torrentId)
|
||||
{
|
||||
var url = new HttpUri(_settings.BaseUrl)
|
||||
.CombinePath("/torrents.php")
|
||||
.AddQueryParam("id", groupId)
|
||||
.AddQueryParam("torrentid", torrentId);
|
||||
|
||||
return url.FullUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcornRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public PassThePopcornSettings Settings { get; set; }
|
||||
|
||||
public IDictionary<string, string> Cookies { get; set; }
|
||||
|
||||
public IHttpClient HttpClient { get; set; }
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
public virtual IndexerPageableRequestChain GetRecentRequests()
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetRequest(null));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pageableRequests.Add(GetRequest(searchCriteria.ImdbId));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var queryTitle in searchCriteria.QueryTitles)
|
||||
{
|
||||
pageableRequests.Add(GetRequest(string.Format("{0}", queryTitle)));
|
||||
}
|
||||
}
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||
{
|
||||
var request =
|
||||
new IndexerRequest(
|
||||
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/torrents.php?action=advanced&json=noredirect&searchstr={searchParameters}",
|
||||
HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers["ApiUser"] = Settings.APIUser;
|
||||
request.HttpRequest.Headers["ApiKey"] = Settings.APIKey;
|
||||
|
||||
if (Settings.APIKey.IsNullOrWhiteSpace())
|
||||
{
|
||||
foreach (var cookie in Cookies)
|
||||
{
|
||||
request.HttpRequest.Cookies[cookie.Key] = cookie.Value;
|
||||
}
|
||||
|
||||
CookiesUpdater(Cookies, DateTime.Now + TimeSpan.FromDays(30));
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.PassThePopcorn
|
||||
{
|
||||
public class PassThePopcornSettingsValidator : AbstractValidator<PassThePopcornSettings>
|
||||
{
|
||||
public PassThePopcornSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
RuleFor(c => c.APIUser).NotEmpty();
|
||||
RuleFor(c => c.APIKey).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class PassThePopcornSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly PassThePopcornSettingsValidator Validator = new PassThePopcornSettingsValidator();
|
||||
|
||||
public PassThePopcornSettings()
|
||||
{
|
||||
BaseUrl = "https://passthepopcorn.me";
|
||||
MinimumSeeders = 0;
|
||||
MultiLanguages = new List<int>();
|
||||
RequiredFlags = new List<int>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your cookie will be sent to that host.")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "APIUser", HelpText = "These settings are found in your PassThePopcorn security settings (Edit Profile > Security).", Privacy = PrivacyLevel.UserName)]
|
||||
public string APIUser { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "APIKey", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string APIKey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter), Label = "Multi Languages", HelpText = "What languages are normally in a multi release on this indexer?", Advanced = true)]
|
||||
public IEnumerable<int> MultiLanguages { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.TagSelect, SelectOptions = typeof(IndexerFlags), Label = "Required Flags", HelpText = "What indexer flags are required?", HelpLink = "https://github.com/Prowlarr/Prowlarr/wiki/Indexer-Flags#1-required-flags", Advanced = true)]
|
||||
public IEnumerable<int> RequiredFlags { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user