mirror of
https://github.com/Jackett/Jackett
synced 2024-12-30 19:58:39 +00:00
Added ShizaProject semi-private tracker (#9878)
This commit is contained in:
parent
0c7f5f4f55
commit
aec1b16612
2 changed files with 205 additions and 0 deletions
|
@ -113,6 +113,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
|||
* RuTracker.RU
|
||||
* seleZen
|
||||
* Sexy-Pics
|
||||
* ShizaProject
|
||||
* shokweb
|
||||
* ShowRSS
|
||||
* SkyTorrentsClone
|
||||
|
|
204
src/Jackett.Common/Indexers/ShizaProject.cs
Normal file
204
src/Jackett.Common/Indexers/ShizaProject.cs
Normal file
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// http://shiza-project.com/accounts/login
|
||||
/// </summary>
|
||||
private string LoginUrl => SiteLink + "accounts/login";
|
||||
|
||||
/// <summary>
|
||||
/// http://shiza-project.com/releases/search
|
||||
/// </summary>
|
||||
private string SearchUrl => SiteLink + "releases/search";
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
{ "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<byte[]> Download(Uri link)
|
||||
{
|
||||
await EnsureAuthorized();
|
||||
return await base.Download(link);
|
||||
}
|
||||
|
||||
// If the search string is empty use the latest releases
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> 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<ReleaseInfo>();
|
||||
|
||||
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<List<ReleaseInfo>> FetchShowReleases(string url)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue