From aec1b16612482d30984a3cc30654ce2efead1af1 Mon Sep 17 00:00:00 2001 From: Dmitry Chepurovskiy Date: Sun, 18 Oct 2020 23:33:47 +0300 Subject: [PATCH] Added ShizaProject semi-private tracker (#9878) --- README.md | 1 + src/Jackett.Common/Indexers/ShizaProject.cs | 204 ++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/Jackett.Common/Indexers/ShizaProject.cs diff --git a/README.md b/README.md index b6fc06b62..3d81a05cd 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/ * RuTracker.RU * seleZen * Sexy-Pics + * ShizaProject * shokweb * ShowRSS * SkyTorrentsClone diff --git a/src/Jackett.Common/Indexers/ShizaProject.cs b/src/Jackett.Common/Indexers/ShizaProject.cs new file mode 100644 index 000000000..acc4e611a --- /dev/null +++ b/src/Jackett.Common/Indexers/ShizaProject.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading.Tasks; +using AngleSharp.Dom; +using AngleSharp.Html.Parser; +using Jackett.Common.Models; +using Jackett.Common.Models.IndexerConfig; +using Jackett.Common.Services.Interfaces; +using Jackett.Common.Utils; +using Jackett.Common.Utils.Clients; +using Newtonsoft.Json.Linq; +using NLog; + +namespace Jackett.Common.Indexers +{ + [ExcludeFromCodeCoverage] + internal class ShizaProject : BaseWebIndexer + { + public ShizaProject(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps) + : base(id: "ShizaProject", + name: "ShizaProject", + description: "ShizaProject Tracker is a semi-private russian tracker and release group for anime", + link: "http://shiza-project.com/", + caps: new TorznabCapabilities(), + configService: configService, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationDataBasicLoginWithEmail()) + { + Encoding = Encoding.UTF8; + Language = "ru-ru"; + Type = "semi-private"; + + AddCategoryMapping(1, TorznabCatType.TVAnime, "Anime"); + } + + private ConfigurationDataBasicLoginWithEmail Configuration => (ConfigurationDataBasicLoginWithEmail)configData; + + /// + /// http://shiza-project.com/accounts/login + /// + private string LoginUrl => SiteLink + "accounts/login"; + + /// + /// http://shiza-project.com/releases/search + /// + private string SearchUrl => SiteLink + "releases/search"; + + public override async Task ApplyConfiguration(JToken configJson) + { + LoadValuesFromJson(configJson); + + var data = new Dictionary + { + { "field-email", Configuration.Email.Value }, + { "field-password", Configuration.Password.Value } + }; + + var result = await RequestLoginAndFollowRedirect( + LoginUrl, + data, + CookieHeader, + returnCookiesFromFirstCall: true + ); + + var parser = new HtmlParser(); + var document = await parser.ParseDocumentAsync(result.ContentString); + + await ConfigureIfOK(result.Cookies, IsAuthorized(result), () => + { + const string ErrorSelector = "div.card-content > div.alert-error"; + var errorMessage = document.QuerySelector(ErrorSelector).Text().Trim(); + throw new ExceptionWithConfigData(errorMessage, Configuration); + }); + + return IndexerConfigurationStatus.Completed; + } + + public override async Task Download(Uri link) + { + await EnsureAuthorized(); + return await base.Download(link); + } + + // If the search string is empty use the latest releases + protected override async Task> PerformQuery(TorznabQuery query) { + WebResult result; + if (query.IsTest || string.IsNullOrWhiteSpace(query.SearchTerm)) { + result = await RequestWithCookiesAndRetryAsync(SiteLink); + } else { + // Prepare the search query + var queryParameters = new NameValueCollection + { + { "q", query.SearchTerm}, + }; + result = await RequestWithCookiesAndRetryAsync(SearchUrl + "?" + queryParameters.GetQueryString()); + } + + const string ReleaseLinksSelector = "article.grid-card > a.card-box"; + + var releases = new List(); + + try + { + var parser = new HtmlParser(); + var document = await parser.ParseDocumentAsync(result.ContentString); + + foreach (var linkNode in document.QuerySelectorAll(ReleaseLinksSelector)) + { + var url = linkNode.GetAttribute("href"); + releases.AddRange(await FetchShowReleases(url)); + } + } + catch (Exception ex) + { + OnParseError(result.ContentString, ex); + } + + return releases; + } + + private async Task EnsureAuthorized() + { + var result = await RequestWithCookiesAndRetryAsync(SiteLink); + + if (!IsAuthorized(result)) + { + await ApplyConfiguration(null); + } + } + + private async Task> FetchShowReleases(string url) + { + var releases = new List(); + var uri = new Uri(url); + var result = await RequestWithCookiesAndRetryAsync(url); + + try + { + var parser = new HtmlParser(); + var document = await parser.ParseDocumentAsync(result.ContentString); + var r = document.QuerySelector("div.release > div.wrapper-release"); + + var baseRelease = new ReleaseInfo(){ + Title = composeBaseTitle(r), + BannerUrl = new Uri(SiteLink + r.QuerySelector("a[data-fancybox]").Attributes["href"].Value), + Comments = uri, + DownloadVolumeFactor = 0, + UploadVolumeFactor = 1, + Category = new int[]{ TorznabCatType.TVAnime.ID }, + }; + + foreach (var t in r.QuerySelectorAll("a[data-toggle]")) + { + var release = (ReleaseInfo)baseRelease.Clone(); + release.Title += " " + t.Text().Trim(); + var tr_id = t.Attributes["href"].Value; + var tr = r.QuerySelector("div" + tr_id); + release.Link = new Uri(tr.QuerySelector("a.button--success").Attributes["href"].Value); + release.Seeders = long.Parse(tr.QuerySelector("div.torrent-counter > div:nth-of-type(1)").Text().Trim().Split(' ')[0]); + release.Peers = release.Seeders + long.Parse(tr.QuerySelector("div.torrent-counter > div:nth-of-type(2)").Text().Trim().Split(' ')[0]); + release.Grabs = long.Parse(tr.QuerySelector("div.torrent-counter > div:nth-of-type(3)").Text().Trim().Split(' ')[0]); + release.PublishDate = DateTime.Parse(tr.QuerySelector("time.torrent-time").Text()); + release.Size = getReleaseSize(tr); + release.Guid = new Uri(uri.ToString() + tr_id); + logger.Info(release.Guid); + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(result.ContentString, ex); + } + + return releases; + } + + private string composeBaseTitle(IElement release) { + var titleDiv = release.QuerySelector("section:nth-of-type(2) > div.card > article:nth-of-type(1) > div.card-header"); + return titleDiv.QuerySelector("h3").Text() + " " + titleDiv.QuerySelector("p").Text(); + } + + // Appending id to differentiate between different quality versions + private bool IsAuthorized(WebResult result) { + var parser = new HtmlParser(); + var document = parser.ParseDocument(result.ContentString); + return document.QuerySelector("div.header-content > div.profile-menu") != null; + } + + private static long getReleaseSize(IElement tr) + { + var size = tr.QuerySelector("a.torrent-size").Text().Trim(); + size = size.Replace("КБ", "KB"); + size = size.Replace("МБ", "MB"); + size = size.Replace("ГБ", "GB"); + size = size.Replace("ТБ", "TB"); + return ReleaseInfo.GetBytes(size); + } + } +}