Skip to content

Commit

Permalink
New: Add UniOtaku
Browse files Browse the repository at this point in the history
  • Loading branch information
mynameisbogdan committed Feb 17, 2023
1 parent c923982 commit 76bfd29
Showing 1 changed file with 312 additions and 0 deletions.
312 changes: 312 additions & 0 deletions src/NzbDrone.Core/Indexers/Definitions/Uniotaku.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using Newtonsoft.Json.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Settings;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;

namespace NzbDrone.Core.Indexers.Definitions;

public class Uniotaku : TorrentIndexerBase<UniotakuSettings>
{
public override string Name => "UniOtaku";
public override string[] IndexerUrls => new[] { "https://tracker.uniotaku.com/" };
public override string Description => "UniOtaku is a BRAZILIAN Semi-Private Torrent Tracker for ANIME";
public override string Language => "pt-BR";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPrivate;
public override IndexerCapabilities Capabilities => SetCapabilities();

public Uniotaku(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}

public override IIndexerRequestGenerator GetRequestGenerator()
{
return new UniotakuRequestGenerator(Settings, Capabilities);
}

public override IParseIndexerResponse GetParser()
{
return new UniotakuParser(Settings, Capabilities.Categories);
}

protected override async Task DoLogin()
{
var loginUrl = Settings.BaseUrl + "account-login.php";
var requestBuilder = new HttpRequestBuilder(loginUrl);

var cookies = Cookies;
Cookies = null;

var authLoginRequest = requestBuilder
.Post()
.AddFormParameter("username", Settings.Username)
.AddFormParameter("password", Settings.Password)
.AddFormParameter("manter", "1")
.SetHeader("Content-Type", "application/x-www-form-urlencoded")
.SetHeader("Referer", Settings.BaseUrl)
.Build();

var response = await ExecuteAuth(authLoginRequest);

if (CheckIfLoginNeeded(response))
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(response.Content);
var errorMessage = dom.QuerySelector(".login-content span.text-red")?.TextContent.Trim();

throw new IndexerAuthException(errorMessage ?? "Unknown error message, please report.");
}

cookies = response.GetCookies();
UpdateCookies(cookies, DateTime.Now.AddDays(30));

_logger.Debug("Authentication succeeded");
}

protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
{
return !httpResponse.GetCookies().ContainsKey("uid") || !httpResponse.GetCookies().ContainsKey("pass");
}

public override async Task<byte[]> Download(Uri link)
{
var request = new HttpRequestBuilder(link.ToString())
.SetCookies(GetCookies() ?? new Dictionary<string, string>())
.Accept(HttpAccept.Html)
.Build();

var response = await _httpClient.ExecuteProxiedAsync(request, Definition);

var parser = new HtmlParser();
var dom = parser.ParseDocument(response.Content);
var downloadLink = dom.QuerySelector("a[href^=\"download.php?id=\"]")?.GetAttribute("href")?.Trim();

if (downloadLink == null)
{
throw new Exception($"Failed to fetch download link from {link}");
}

return await base.Download(new Uri(Settings.BaseUrl + downloadLink));
}

private IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};

caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.TVAnime, "Anime");
caps.Categories.AddCategoryMapping(47, NewznabStandardCategory.MoviesOther, "Filme");
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.TVAnime, "OVA");
caps.Categories.AddCategoryMapping(49, NewznabStandardCategory.BooksComics, "Mangá");
caps.Categories.AddCategoryMapping(50, NewznabStandardCategory.TVOther, "Dorama");
caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.Audio, "OST");
caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.TVAnime, "Anime Completo");
caps.Categories.AddCategoryMapping(53, NewznabStandardCategory.BooksComics, "Mangá Completo");
caps.Categories.AddCategoryMapping(54, NewznabStandardCategory.TVOther, "Dorama Completo");
caps.Categories.AddCategoryMapping(55, NewznabStandardCategory.XXX, "Hentai");
caps.Categories.AddCategoryMapping(56, NewznabStandardCategory.XXXOther, "H Doujinshi");
caps.Categories.AddCategoryMapping(57, NewznabStandardCategory.TVOther, "Tokusatsu");

return caps;
}
}

public class UniotakuRequestGenerator : IIndexerRequestGenerator
{
private readonly UniotakuSettings _settings;
private readonly IndexerCapabilities _capabilities;

public UniotakuRequestGenerator(UniotakuSettings settings, IndexerCapabilities capabilities)
{
_settings = settings;
_capabilities = capabilities;
}

public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();

pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));

return pageableRequests;
}

public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();

pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));

return pageableRequests;
}

public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();

pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));

return pageableRequests;
}

public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();

pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));

return pageableRequests;
}

public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();

pageableRequests.Add(GetPagedRequests($"{searchCriteria.SanitizedSearchTerm}", searchCriteria.Categories));

return pageableRequests;
}

private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories)
{
if (!string.IsNullOrWhiteSpace(term))
{
term = "%" + Regex.Replace(term, @"[ -._]+", "%").Trim() + "%";
}

var categoryMapping = _capabilities.Categories.MapTorznabCapsToTrackers(categories);

var parameters = new NameValueCollection
{
{ "categoria", categoryMapping.FirstIfSingleOrDefault("0") },
{ "grupo", "0" },
{ "status", _settings.FreeleechOnly ? "1" : "0" },
{ "ordenar", "0" },
{ "start", "0" },
{ "length", "100" },
{ "search[value]", term ?? string.Empty },
{ "search[regex]", "false" },
};

var searchUrl = $"{_settings.BaseUrl}torrents_.php?{parameters.GetQueryString()}";

yield return new IndexerRequest(searchUrl, HttpAccept.Html);
}

public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

public class UniotakuParser : IParseIndexerResponse
{
private readonly UniotakuSettings _settings;
private readonly IndexerCapabilitiesCategories _categories;

public UniotakuParser(UniotakuSettings settings, IndexerCapabilitiesCategories categories)
{
_settings = settings;
_categories = categories;
}

public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var releaseInfos = new List<ReleaseInfo>();

var parser = new HtmlParser();

var jsonContent = JObject.Parse(indexerResponse.Content);

var publishDate = DateTime.Now;
foreach (var item in jsonContent.Value<JArray>("data"))
{
var detailsDom = parser.ParseDocument(item.SelectToken("[0]").Value<string>());
var categoryDom = parser.ParseDocument(item.SelectToken("[1]").Value<string>());
var groupDom = parser.ParseDocument(item.SelectToken("[7]").Value<string>());

var qTitleLink = detailsDom.QuerySelector("a[href^=\"torrents-details.php?id=\"]");
var title = qTitleLink?.TextContent.Trim();
var infoUrl = _settings.BaseUrl + qTitleLink?.GetAttribute("href");

var category = categoryDom.QuerySelector("img[alt]")?.GetAttribute("alt")?.Trim() ?? "Anime";

var releaseGroup = groupDom.QuerySelector("a[href*=\"teams-view.php?id=\"]")?.TextContent.Trim();
if (!string.IsNullOrWhiteSpace(releaseGroup))
{
title += $" [{releaseGroup}]";
}

var seeders = item.SelectToken("[3]")?.Value<int>();
var leechers = item.SelectToken("[4]")?.Value<int>();

publishDate = publishDate.AddMinutes(-1);

var release = new TorrentInfo
{
Guid = infoUrl,
InfoUrl = infoUrl,
DownloadUrl = infoUrl,
Title = title,
Categories = _categories.MapTrackerCatDescToNewznab(category),
Size = ParseUtil.GetBytes(item.SelectToken("[6]")?.Value<string>()),
Grabs = item.SelectToken("[5]")?.Value<int>(),
Seeders = seeders,
Peers = seeders + leechers,
PublishDate = publishDate,
DownloadVolumeFactor =
detailsDom.QuerySelector("img[src*=\"images/free.gif\"]") != null ? 0 :
detailsDom.QuerySelector("img[src*=\"images/silverdownload.gif\"]") != null ? 0.5 : 1,
UploadVolumeFactor = 1,
MinimumRatio = 0.7
};

releaseInfos.Add(release);
}

return releaseInfos;
}

public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}

public class UniotakuSettings : UserPassTorrentBaseSettings
{
public UniotakuSettings()
{
FreeleechOnly = false;
}

[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search Freeleech torrents only")]
public bool FreeleechOnly { get; set; }
}

0 comments on commit 76bfd29

Please sign in to comment.