From da60543c72ed0f99390ed501af6ca9fa72102d36 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 14 Feb 2021 22:58:00 -0500 Subject: [PATCH] Fixup Gazelle Parsing, Rework Auth to allow Indexer Override --- .../Indexers/Definitions/Gazelle/Gazelle.cs | 47 +++++++++++ .../Definitions/Gazelle/GazelleApi.cs | 13 ++- .../Definitions/Gazelle/GazelleParser.cs | 35 +++++++- .../Gazelle/GazelleRequestGenerator.cs | 83 +------------------ src/NzbDrone.Core/Indexers/HttpIndexerBase.cs | 56 ++++++++++++- 5 files changed, 148 insertions(+), 86 deletions(-) diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs index 1417ce836..1cb997d20 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/Gazelle.cs @@ -1,3 +1,4 @@ +using System; using NLog; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -8,6 +9,7 @@ namespace NzbDrone.Core.Indexers.Gazelle { public override DownloadProtocol Protocol => DownloadProtocol.Torrent; public override string BaseUrl => ""; + protected virtual string LoginUrl => BaseUrl + "login.php"; public override bool SupportsRss => true; public override bool SupportsSearch => true; public override int PageSize => 50; @@ -44,5 +46,50 @@ namespace NzbDrone.Core.Indexers.Gazelle return caps; } + + protected override void DoLogin() + { + var requestBuilder = new HttpRequestBuilder(LoginUrl) + { + LogResponseContent = true + }; + + requestBuilder.Method = HttpMethod.POST; + requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); + + var cookies = Cookies; + + Cookies = null; + 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(); + + Cookies = cookies; + + UpdateCookies(cookies, DateTime.Now + TimeSpan.FromDays(30)); + + _logger.Debug("Gazelle authentication succeeded."); + + //Settings.AuthKey = index.Response.Authkey; + //Settings.PassKey = index.Response.Passkey; + } + + protected override bool CheckIfLoginNeeded(HttpResponse response) + { + if (response.HasHttpRedirect || (response.Content != null && response.Content.Contains("\"bad credentials\""))) + { + return true; + } + + return false; + } } } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs index 7da0595a0..18a26dcce 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleApi.cs @@ -43,6 +43,13 @@ namespace NzbDrone.Core.Indexers.Gazelle { public string GroupId { get; set; } public string GroupName { get; set; } + public int TorrentId { get; set; } + public string Size { get; set; } + public int FileCount { get; set; } + public string Snatches { get; set; } + public string Seeders { get; set; } + public string Leechers { get; set; } + public string Category { get; set; } public string Artist { get; set; } public string GroupYear { get; set; } public string Cover { get; set; } @@ -52,8 +59,12 @@ namespace NzbDrone.Core.Indexers.Gazelle public int TotalSeeders { get; set; } public int TotalSnatched { get; set; } public long MaxSize { get; set; } - public string GroupTime { get; set; } + public long GroupTime { get; set; } public List Torrents { get; set; } + public bool IsFreeLeech { get; set; } + public bool IsNeutralLeech { get; set; } + public bool IsPersonalFreeLeech { get; set; } + public bool CanUseToken { get; set; } } public class GazelleResponse diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs index 86a632ab9..bd2ab041a 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleParser.cs @@ -73,6 +73,7 @@ namespace NzbDrone.Core.Indexers.Gazelle Guid = string.Format("Gazelle-{0}", id), Title = WebUtility.HtmlDecode(title), Container = torrent.Encoding, + Files = torrent.FileCount, Codec = torrent.Format, Size = long.Parse(torrent.Size), DownloadUrl = GetDownloadUrl(id), @@ -96,6 +97,36 @@ namespace NzbDrone.Core.Indexers.Gazelle torrentInfos.Add(release); } } + else + { + var id = result.TorrentId; + var groupName = WebUtility.HtmlDecode(result.GroupName); + + var release = new GazelleInfo() + { + Guid = string.Format("Gazelle-{0}", id), + Title = groupName, + Size = long.Parse(result.Size), + DownloadUrl = GetDownloadUrl(id), + InfoUrl = GetInfoUrl(result.GroupId, id), + Seeders = int.Parse(result.Seeders), + Peers = int.Parse(result.Leechers) + int.Parse(result.Seeders), + Files = result.FileCount, + PublishDate = DateTimeOffset.FromUnixTimeSeconds(result.GroupTime).UtcDateTime, + }; + + var category = result.Category; + if (category == null || category.Contains("Select Category")) + { + release.Category = _capabilities.Categories.MapTrackerCatToNewznab("1"); + } + else + { + release.Category = _capabilities.Categories.MapTrackerCatDescToNewznab(category); + } + + torrentInfos.Add(release); + } } // order by date @@ -110,9 +141,7 @@ namespace NzbDrone.Core.Indexers.Gazelle var url = new HttpUri(_baseUrl) .CombinePath("/torrents.php") .AddQueryParam("action", "download") - .AddQueryParam("id", torrentId) - .AddQueryParam("authkey", _settings.AuthKey) - .AddQueryParam("torrent_pass", _settings.PassKey); + .AddQueryParam("id", torrentId); return url.FullUri; } diff --git a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs index d74cb7152..ffec972bd 100644 --- a/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs +++ b/src/NzbDrone.Core/Indexers/Definitions/Gazelle/GazelleRequestGenerator.cs @@ -20,7 +20,6 @@ namespace NzbDrone.Core.Indexers.Gazelle public IndexerCapabilities Capabilities { get; set; } public Logger Logger { get; set; } - protected virtual string LoginUrl => BaseUrl + "login.php"; protected virtual string APIUrl => BaseUrl + "ajax.php"; protected virtual string DownloadUrl => BaseUrl + "torrents.php?action=download&usetoken=" + (Settings.UseFreeleechToken ? "1" : "0") + "&id="; protected virtual string DetailsUrl => BaseUrl + "torrents.php?torrentid="; @@ -40,10 +39,6 @@ namespace NzbDrone.Core.Indexers.Gazelle private IEnumerable GetRequest(string searchParameters) { - AuthCookieCache = GetCookies(); - - Authenticate(); - var filter = ""; if (searchParameters == null) { @@ -51,91 +46,17 @@ namespace NzbDrone.Core.Indexers.Gazelle var request = new IndexerRequest( - $"{APIUrl}?action=browse{searchParameters}{filter}", + $"{APIUrl}?{searchParameters}{filter}", HttpAccept.Json); - var cookies = AuthCookieCache; - foreach (var cookie in cookies) - { - request.HttpRequest.Cookies[cookie.Key] = cookie.Value; - } - yield return request; } - private GazelleAuthResponse GetIndex(IDictionary cookies) - { - var indexRequestBuilder = new HttpRequestBuilder($"{APIUrl}?action=index") - { - LogResponseContent = true - }; - - indexRequestBuilder.SetCookies(cookies); - indexRequestBuilder.Method = HttpMethod.POST; - - var authIndexRequest = indexRequestBuilder - .Accept(HttpAccept.Json) - .Build(); - - var indexResponse = HttpClient.Execute(authIndexRequest); - - var result = Json.Deserialize(indexResponse.Content); - - return result; - } - - private void Authenticate() - { - var requestBuilder = new HttpRequestBuilder(LoginUrl) - { - LogResponseContent = true - }; - - requestBuilder.Method = HttpMethod.POST; - requestBuilder.PostProcess += r => r.RequestTimeout = TimeSpan.FromSeconds(15); - - var cookies = AuthCookieCache; - - if (cookies == null) - { - AuthCookieCache = null; - 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 = cookies; - CookiesUpdater(cookies, DateTime.Now + TimeSpan.FromDays(30)); - } - - var index = GetIndex(cookies); - - if (index == null || index.Status.IsNullOrWhiteSpace() || index.Status != "success") - { - Logger.Debug("Gazelle authentication failed."); - AuthCookieCache = null; - CookiesUpdater(null, null); - throw new Exception("Failed to authenticate with Gazelle."); - } - - Logger.Debug("Gazelle authentication succeeded."); - - Settings.AuthKey = index.Response.Authkey; - Settings.PassKey = index.Response.Passkey; - } - private string GetBasicSearchParameters(string searchTerm, int[] categories) { var searchString = GetSearchTerm(searchTerm); - var parameters = "&action=browse&order_by=time&order_way=desc"; + var parameters = "action=browse&order_by=time&order_way=desc"; if (!string.IsNullOrWhiteSpace(searchString)) { diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index f84c508cb..e45694263 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -22,6 +22,7 @@ namespace NzbDrone.Core.Indexers protected const int MaxNumResultsPerQuery = 1000; protected readonly IHttpClient _httpClient; + public IDictionary Cookies { get; set; } public override bool SupportsRss => true; public override bool SupportsSearch => true; @@ -111,6 +112,23 @@ namespace NzbDrone.Core.Indexers return generator; } + protected IDictionary GetCookies() + { + var cookies = _indexerStatusService.GetIndexerCookies(Definition.Id); + var expiration = _indexerStatusService.GetIndexerCookiesExpirationDate(Definition.Id); + if (expiration < DateTime.Now) + { + cookies = null; + } + + return cookies; + } + + protected void UpdateCookies(IDictionary cookies, DateTime? expiration) + { + _indexerStatusService.UpdateCookies(Definition.Id, cookies, expiration); + } + protected virtual IndexerPageableQueryResult FetchReleases(Func pageableRequestChainSelector, bool isRecent = false) { var releases = new List(); @@ -333,6 +351,15 @@ namespace NzbDrone.Core.Indexers } } + protected virtual bool CheckIfLoginNeeded(HttpResponse httpResponse) + { + return false; + } + + protected virtual void DoLogin() + { + } + protected virtual IndexerResponse FetchIndexerResponse(IndexerRequest request) { _logger.Debug("Downloading Feed " + request.HttpRequest.ToString(false)); @@ -342,7 +369,17 @@ namespace NzbDrone.Core.Indexers request.HttpRequest.RateLimit = RateLimit; } - request.HttpRequest.AllowAutoRedirect = true; + request.HttpRequest.AllowAutoRedirect = false; + + Cookies = GetCookies(); + + if (Cookies != null) + { + foreach (var cookie in Cookies) + { + request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value); + } + } var stopWatch = Stopwatch.StartNew(); @@ -350,6 +387,23 @@ namespace NzbDrone.Core.Indexers stopWatch.Stop(); + // Check reponse to see if auth is needed, if needed try again + if (CheckIfLoginNeeded(response)) + { + DoLogin(); + request.HttpRequest.Cookies.Clear(); + + if (Cookies != null) + { + foreach (var cookie in Cookies) + { + request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value); + } + } + + response = _httpClient.Execute(request.HttpRequest); + } + return new IndexerResponse(request, response, stopWatch.ElapsedMilliseconds); }