parent
7c3446baab
commit
1322633d0d
@ -0,0 +1,81 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.Gazelle
|
||||||
|
{
|
||||||
|
public class Gazelle : HttpIndexerBase<GazelleSettings>
|
||||||
|
{
|
||||||
|
public override string Name => "Gazelle API";
|
||||||
|
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||||
|
public override bool SupportsRss => true;
|
||||||
|
public override bool SupportsSearch => true;
|
||||||
|
public override int PageSize => 50;
|
||||||
|
|
||||||
|
private readonly ICached<Dictionary<string, string>> _authCookieCache;
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public Gazelle(IHttpClient httpClient, ICacheManager cacheManager, IIndexerStatusService indexerStatusService,
|
||||||
|
IConfigService configService, IParsingService parsingService, Logger logger)
|
||||||
|
: base(httpClient, indexerStatusService, configService, parsingService, logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_authCookieCache = cacheManager.GetCache<Dictionary<string, string>>(GetType(), "authCookies");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new GazelleRequestGenerator()
|
||||||
|
{
|
||||||
|
Settings = Settings,
|
||||||
|
HttpClient = _httpClient,
|
||||||
|
Logger = _logger,
|
||||||
|
AuthCookieCache = _authCookieCache
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IParseIndexerResponse GetParser()
|
||||||
|
{
|
||||||
|
return new GazelleParser(Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return GetDefinition("Apollo.Rip", GetSettings("https://apollo.rip"));
|
||||||
|
yield return GetDefinition("REDacted", GetSettings("https://redacted.ch"));
|
||||||
|
yield return GetDefinition("Not What CD", GetSettings("https://notwhat.cd"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexerDefinition GetDefinition(string name, GazelleSettings settings)
|
||||||
|
{
|
||||||
|
return new IndexerDefinition
|
||||||
|
{
|
||||||
|
EnableRss = false,
|
||||||
|
EnableSearch = false,
|
||||||
|
Name = name,
|
||||||
|
Implementation = GetType().Name,
|
||||||
|
Settings = settings,
|
||||||
|
Protocol = DownloadProtocol.Torrent,
|
||||||
|
SupportsRss = SupportsRss,
|
||||||
|
SupportsSearch = SupportsSearch
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private GazelleSettings GetSettings(string url)
|
||||||
|
{
|
||||||
|
var settings = new GazelleSettings { BaseUrl = url };
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.Gazelle
|
||||||
|
{
|
||||||
|
public class GazelleRequestGenerator : IIndexerRequestGenerator
|
||||||
|
{
|
||||||
|
|
||||||
|
public GazelleSettings Settings { get; set; }
|
||||||
|
|
||||||
|
public ICached<Dictionary<string, string>> AuthCookieCache { 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(AlbumSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
pageableRequests.Add(GetRequest(string.Format("&artistname={0}&groupname={1}", searchCriteria.Artist.Name, searchCriteria.AlbumTitle)));
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexerPageableRequestChain GetSearchRequests(ArtistSearchCriteria searchCriteria)
|
||||||
|
{
|
||||||
|
var pageableRequests = new IndexerPageableRequestChain();
|
||||||
|
pageableRequests.Add(GetRequest(string.Format("&artistname={0}",searchCriteria.Artist.Name)));
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IndexerRequest> GetRequest(string searchParameters)
|
||||||
|
{
|
||||||
|
Authenticate();
|
||||||
|
|
||||||
|
var filter = "";
|
||||||
|
if (searchParameters == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var request =
|
||||||
|
new IndexerRequest(
|
||||||
|
$"{Settings.BaseUrl.Trim().TrimEnd('/')}/ajax.php?action=browse&searchstr={searchParameters}{filter}",
|
||||||
|
HttpAccept.Json);
|
||||||
|
|
||||||
|
var cookies = AuthCookieCache.Find(Settings.BaseUrl.Trim().TrimEnd('/'));
|
||||||
|
foreach (var cookie in cookies)
|
||||||
|
{
|
||||||
|
request.HttpRequest.Cookies[cookie.Key] = cookie.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GazelleAuthResponse GetIndex(Dictionary<string,string> cookies)
|
||||||
|
{
|
||||||
|
var indexRequestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
|
||||||
|
{
|
||||||
|
LogResponseContent = true
|
||||||
|
};
|
||||||
|
|
||||||
|
indexRequestBuilder.SetCookies(cookies);
|
||||||
|
indexRequestBuilder.Method = HttpMethod.GET;
|
||||||
|
indexRequestBuilder.Resource("ajax.php?action=index");
|
||||||
|
|
||||||
|
var authIndexRequest = indexRequestBuilder
|
||||||
|
.Accept(HttpAccept.Json)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var indexResponse = HttpClient.Execute(authIndexRequest);
|
||||||
|
|
||||||
|
var result = Json.Deserialize<GazelleAuthResponse>(indexResponse.Content);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Authenticate()
|
||||||
|
{
|
||||||
|
|
||||||
|
var requestBuilder = new HttpRequestBuilder($"{Settings.BaseUrl.Trim().TrimEnd('/')}")
|
||||||
|
{
|
||||||
|
LogResponseContent = true
|
||||||
|
};
|
||||||
|
|
||||||
|
requestBuilder.Method = HttpMethod.POST;
|
||||||
|
requestBuilder.Resource("login.php");
|
||||||
|
requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15);
|
||||||
|
|
||||||
|
var authKey = Settings.BaseUrl.Trim().TrimEnd('/');
|
||||||
|
var cookies = AuthCookieCache.Find(authKey);
|
||||||
|
|
||||||
|
if (cookies == null)
|
||||||
|
{
|
||||||
|
AuthCookieCache.Remove(authKey);
|
||||||
|
var authLoginRequest = requestBuilder
|
||||||
|
.AddFormParameter("username", Settings.Username)
|
||||||
|
.AddFormParameter("password", Settings.Password)
|
||||||
|
.AddFormParameter("keeplogged", "1")
|
||||||
|
.SetHeader("Content-Type", "multipart/form-data")
|
||||||
|
.Accept(HttpAccept.Json)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var response = HttpClient.Execute(authLoginRequest);
|
||||||
|
|
||||||
|
cookies = response.GetCookies();
|
||||||
|
AuthCookieCache.Set(authKey, cookies, new TimeSpan(7, 0, 0, 0, 0)); // re-auth every 7 days
|
||||||
|
requestBuilder.SetCookies(cookies);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBuilder.SetCookies(cookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = GetIndex(cookies);
|
||||||
|
|
||||||
|
if (index.Status != "success" || string.IsNullOrWhiteSpace(index.Status))
|
||||||
|
{
|
||||||
|
Logger.Debug("Gazelle authentication failed.");
|
||||||
|
throw new Exception("Failed to authenticate with Gazelle.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Gazelle authentication succeeded.");
|
||||||
|
|
||||||
|
Settings.AuthKey = index.Response.Authkey;
|
||||||
|
Settings.PassKey = index.Response.Passkey;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Indexers.Gazelle
|
||||||
|
{
|
||||||
|
public class GazelleSettingsValidator : AbstractValidator<GazelleSettings>
|
||||||
|
{
|
||||||
|
public GazelleSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||||
|
RuleFor(c => c.Username).NotEmpty();
|
||||||
|
RuleFor(c => c.Password).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GazelleSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly GazelleSettingsValidator Validator = new GazelleSettingsValidator();
|
||||||
|
|
||||||
|
public GazelleSettings()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string AuthKey;
|
||||||
|
public string PassKey;
|
||||||
|
|
||||||
|
[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 = "Username", HelpText = "Username")]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Password")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue