2020-04-07 19:58:36 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2020-05-03 23:35:52 +00:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2020-04-07 19:58:36 +00:00
|
|
|
using System.Linq;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using AngleSharp.Html.Parser;
|
|
|
|
using Jackett.Common.Helpers;
|
|
|
|
using Jackett.Common.Models;
|
|
|
|
using Jackett.Common.Models.IndexerConfig;
|
|
|
|
using Jackett.Common.Services.Interfaces;
|
|
|
|
using Jackett.Common.Utils.Clients;
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
using NLog;
|
2023-05-29 18:50:44 +00:00
|
|
|
using static System.Net.WebRequestMethods;
|
2020-04-07 19:58:36 +00:00
|
|
|
using WebClient = Jackett.Common.Utils.Clients.WebClient;
|
|
|
|
|
|
|
|
namespace Jackett.Common.Indexers
|
|
|
|
{
|
2020-05-03 23:35:52 +00:00
|
|
|
[ExcludeFromCodeCoverage]
|
2023-03-09 14:06:12 +00:00
|
|
|
public class Cinecalidad : IndexerBase
|
2020-04-07 19:58:36 +00:00
|
|
|
{
|
2023-03-09 14:06:12 +00:00
|
|
|
public override string Id => "cinecalidad";
|
|
|
|
public override string Name => "Cinecalidad";
|
|
|
|
public override string Description => "Películas Full HD en Latino Dual.";
|
2023-08-25 00:42:05 +00:00
|
|
|
public override string SiteLink { get; protected set; } = "https://vww.cinecalidad.foo/";
|
2023-03-09 14:06:12 +00:00
|
|
|
public override string[] LegacySiteLinks => new[]
|
|
|
|
{
|
2021-03-21 01:41:52 +00:00
|
|
|
"https://cinecalidad.u4m.club/",
|
2021-11-13 05:29:34 +00:00
|
|
|
"https://cinecalidad.mrunblock.icu/",
|
2022-04-10 04:12:04 +00:00
|
|
|
"https://cinecalidad3.com/",
|
2022-04-25 00:04:14 +00:00
|
|
|
"https://www5.cine-calidad.com/",
|
2022-05-14 03:16:17 +00:00
|
|
|
"https://v3.cine-calidad.com/",
|
2022-06-30 04:09:58 +00:00
|
|
|
"https://www.cine-calidad.com/",
|
2022-11-12 02:12:22 +00:00
|
|
|
"https://www.cinecalidad.lat/",
|
2023-03-28 01:43:21 +00:00
|
|
|
"https://cinecalidad.dev/",
|
2023-05-29 18:50:44 +00:00
|
|
|
"https://cinecalidad.ms/",
|
2023-05-31 23:44:16 +00:00
|
|
|
"https://www3.cinecalidad.ms/",
|
2023-06-05 21:23:56 +00:00
|
|
|
"https://ww1.cinecalidad.ms/",
|
2023-08-05 16:51:39 +00:00
|
|
|
"https://www.cinecalidad.gs/",
|
2023-08-08 18:27:19 +00:00
|
|
|
"https://www.cinecalidad.tf/",
|
|
|
|
"https://wvw.cinecalidad.tf/",
|
2023-08-10 18:22:20 +00:00
|
|
|
"https://vww.cinecalidad.tf/",
|
2023-08-17 18:27:42 +00:00
|
|
|
"https://wwv.cinecalidad.tf/",
|
2023-08-25 00:42:05 +00:00
|
|
|
"https://www.cinecalidad.foo/"
|
2020-12-15 19:16:09 +00:00
|
|
|
};
|
2023-03-09 14:06:12 +00:00
|
|
|
public override string Language => "es-419";
|
|
|
|
public override string Type => "public";
|
|
|
|
|
2023-03-10 11:20:29 +00:00
|
|
|
public override TorznabCapabilities TorznabCaps => SetCapabilities();
|
|
|
|
|
2023-03-09 14:06:12 +00:00
|
|
|
private const int MaxLatestPageLimit = 3; // 12 items per page * 3 pages = 36
|
|
|
|
private const int MaxSearchPageLimit = 6;
|
2020-12-15 19:16:09 +00:00
|
|
|
|
2020-12-11 22:14:21 +00:00
|
|
|
public Cinecalidad(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
|
2023-03-09 14:06:12 +00:00
|
|
|
ICacheService cs)
|
2023-03-10 11:20:29 +00:00
|
|
|
: base(configService: configService,
|
2020-04-07 19:58:36 +00:00
|
|
|
client: wc,
|
|
|
|
logger: l,
|
|
|
|
p: ps,
|
2020-12-11 22:14:21 +00:00
|
|
|
cacheService: cs,
|
2020-04-07 19:58:36 +00:00
|
|
|
configData: new ConfigurationData())
|
|
|
|
{
|
2023-03-10 11:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private TorznabCapabilities SetCapabilities()
|
|
|
|
{
|
|
|
|
var caps = new TorznabCapabilities
|
|
|
|
{
|
|
|
|
MovieSearchParams = new List<MovieSearchParam>
|
|
|
|
{
|
|
|
|
MovieSearchParam.Q
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
caps.Categories.AddCategoryMapping(1, TorznabCatType.MoviesHD);
|
|
|
|
|
|
|
|
return caps;
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
|
|
|
{
|
|
|
|
LoadValuesFromJson(configJson);
|
|
|
|
var releases = await PerformQuery(new TorznabQuery());
|
|
|
|
|
|
|
|
await ConfigureIfOK(string.Empty, releases.Any(), () =>
|
|
|
|
throw new Exception("Could not find release from this URL."));
|
|
|
|
|
|
|
|
return IndexerConfigurationStatus.Completed;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
|
|
|
{
|
|
|
|
var releases = new List<ReleaseInfo>();
|
|
|
|
|
|
|
|
var templateUrl = SiteLink;
|
2021-10-09 18:36:22 +00:00
|
|
|
templateUrl += "{0}?s="; // placeholder for page
|
2020-04-07 19:58:36 +00:00
|
|
|
|
2022-03-08 20:13:47 +00:00
|
|
|
var maxPages = MaxLatestPageLimit; // we scrape only 3 pages for recent torrents
|
|
|
|
var recent = !string.IsNullOrWhiteSpace(query.GetQueryString());
|
|
|
|
if (recent)
|
2020-04-07 19:58:36 +00:00
|
|
|
{
|
2021-10-09 18:36:22 +00:00
|
|
|
templateUrl += WebUtilityHelpers.UrlEncode(query.GetQueryString(), Encoding.UTF8);
|
2020-04-07 19:58:36 +00:00
|
|
|
maxPages = MaxSearchPageLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastPublishDate = DateTime.Now;
|
|
|
|
for (var page = 1; page <= maxPages; page++)
|
|
|
|
{
|
|
|
|
var pageParam = page > 1 ? $"page/{page}/" : "";
|
|
|
|
var searchUrl = string.Format(templateUrl, pageParam);
|
2020-06-11 15:09:27 +00:00
|
|
|
var response = await RequestWithCookiesAndRetryAsync(searchUrl);
|
2020-04-07 19:58:36 +00:00
|
|
|
var pageReleases = ParseReleases(response, query);
|
|
|
|
|
|
|
|
// publish date is not available in the torrent list, but we add a relative date so we can sort
|
2020-10-02 12:29:14 +00:00
|
|
|
foreach (var release in pageReleases)
|
2020-04-07 19:58:36 +00:00
|
|
|
{
|
|
|
|
release.PublishDate = lastPublishDate;
|
|
|
|
lastPublishDate = lastPublishDate.AddMinutes(-1);
|
|
|
|
}
|
|
|
|
releases.AddRange(pageReleases);
|
|
|
|
|
2022-03-08 20:13:47 +00:00
|
|
|
if (pageReleases.Count < 1 && recent)
|
2020-04-07 19:58:36 +00:00
|
|
|
break; // this is the last page
|
|
|
|
}
|
|
|
|
|
|
|
|
return releases;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override async Task<byte[]> Download(Uri link)
|
|
|
|
{
|
2020-09-21 16:39:47 +00:00
|
|
|
var results = await RequestWithCookiesAsync(link.ToString());
|
2020-04-07 19:58:36 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var parser = new HtmlParser();
|
2020-06-09 17:36:57 +00:00
|
|
|
var dom = parser.ParseDocument(results.ContentString);
|
2022-01-16 10:30:26 +00:00
|
|
|
var protectedLink = dom.QuerySelector("a:contains('Torrent')").GetAttribute("data-url");
|
|
|
|
protectedLink = Base64Decode(protectedLink);
|
2022-09-07 08:41:57 +00:00
|
|
|
// turn
|
|
|
|
// link=https://cinecalidad.dev/pelicula/la-chica-salvaje/
|
2022-11-12 02:12:22 +00:00
|
|
|
// and
|
2022-09-07 08:41:57 +00:00
|
|
|
// protectedlink=https://cinecalidad.dev/links/MS8xMDA5NTIvMQ==
|
|
|
|
// into
|
2022-11-12 02:12:22 +00:00
|
|
|
// https://cinecalidad.dev/pelicula/la-chica-salvaje/?link=MS8xMDA5NTIvMQ==
|
2022-09-07 08:41:57 +00:00
|
|
|
var protectedLinkSplit = protectedLink.Split('/');
|
|
|
|
var key = protectedLinkSplit.Last();
|
|
|
|
protectedLink = link.ToString() + "?link=" + key;
|
2020-12-22 17:21:00 +00:00
|
|
|
protectedLink = GetAbsoluteUrl(protectedLink);
|
2020-04-07 19:58:36 +00:00
|
|
|
|
2020-11-28 20:40:10 +00:00
|
|
|
results = await RequestWithCookiesAsync(protectedLink);
|
2020-06-09 17:36:57 +00:00
|
|
|
dom = parser.ParseDocument(results.ContentString);
|
2020-04-07 19:58:36 +00:00
|
|
|
var magnetUrl = dom.QuerySelector("a[href^=magnet]").GetAttribute("href");
|
|
|
|
return await base.Download(new Uri(magnetUrl));
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-06-09 17:36:57 +00:00
|
|
|
OnParseError(results.ContentString, ex);
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-06-10 21:22:29 +00:00
|
|
|
private List<ReleaseInfo> ParseReleases(WebResult response, TorznabQuery query)
|
2020-04-07 19:58:36 +00:00
|
|
|
{
|
|
|
|
var releases = new List<ReleaseInfo>();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var parser = new HtmlParser();
|
2020-06-09 17:36:57 +00:00
|
|
|
var dom = parser.ParseDocument(response.ContentString);
|
2020-04-07 19:58:36 +00:00
|
|
|
|
2022-01-15 18:15:26 +00:00
|
|
|
var rows = dom.QuerySelectorAll("article");
|
2020-04-07 19:58:36 +00:00
|
|
|
foreach (var row in rows)
|
|
|
|
{
|
2022-01-16 10:30:26 +00:00
|
|
|
if (row.QuerySelector("div.selt") != null)
|
|
|
|
continue; // we only support movies
|
|
|
|
|
|
|
|
var qLink = row.QuerySelector("a.absolute");
|
2022-03-08 20:13:47 +00:00
|
|
|
var qImg = row.QuerySelector("img.rounded");
|
2022-01-16 10:30:26 +00:00
|
|
|
if (qLink == null || qImg == null)
|
2020-05-01 16:32:23 +00:00
|
|
|
continue; // skip results without image
|
2022-01-16 10:30:26 +00:00
|
|
|
|
|
|
|
var title = qLink.TextContent.Trim();
|
2020-04-07 19:58:36 +00:00
|
|
|
if (!CheckTitleMatchWords(query.GetQueryString(), title))
|
|
|
|
continue; // skip if it doesn't contain all words
|
2022-01-16 10:30:26 +00:00
|
|
|
title += " MULTi/LATiN SPANiSH 1080p BDRip x264";
|
2022-01-15 18:15:26 +00:00
|
|
|
var poster = new Uri(GetAbsoluteUrl(qImg.GetAttribute("src")));
|
2022-01-16 10:30:26 +00:00
|
|
|
var link = new Uri(qLink.GetAttribute("href"));
|
2020-04-07 19:58:36 +00:00
|
|
|
|
|
|
|
var release = new ReleaseInfo
|
|
|
|
{
|
|
|
|
Title = title,
|
|
|
|
Link = link,
|
2020-11-08 02:11:27 +00:00
|
|
|
Details = link,
|
2020-04-07 19:58:36 +00:00
|
|
|
Guid = link,
|
2020-10-02 12:29:14 +00:00
|
|
|
Category = new List<int> { TorznabCatType.MoviesHD.ID },
|
2020-11-07 23:43:33 +00:00
|
|
|
Poster = poster,
|
2020-04-07 19:58:36 +00:00
|
|
|
Size = 2147483648, // 2 GB
|
|
|
|
Files = 1,
|
|
|
|
Seeders = 1,
|
|
|
|
Peers = 2,
|
|
|
|
DownloadVolumeFactor = 0,
|
|
|
|
UploadVolumeFactor = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
releases.Add(release);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-06-09 17:36:57 +00:00
|
|
|
OnParseError(response.ContentString, ex);
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return releases;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: merge this method with query.MatchQueryStringAND
|
|
|
|
private static bool CheckTitleMatchWords(string queryStr, string title)
|
|
|
|
{
|
|
|
|
// this code split the words, remove words with 2 letters or less, remove accents and lowercase
|
|
|
|
var queryMatches = Regex.Matches(queryStr, @"\b[\w']*\b");
|
|
|
|
var queryWords = from m in queryMatches.Cast<Match>()
|
|
|
|
where !string.IsNullOrEmpty(m.Value) && m.Value.Length > 2
|
|
|
|
select Encoding.UTF8.GetString(Encoding.GetEncoding("ISO-8859-8").GetBytes(m.Value.ToLower()));
|
|
|
|
|
|
|
|
var titleMatches = Regex.Matches(title, @"\b[\w']*\b");
|
|
|
|
var titleWords = from m in titleMatches.Cast<Match>()
|
|
|
|
where !string.IsNullOrEmpty(m.Value) && m.Value.Length > 2
|
|
|
|
select Encoding.UTF8.GetString(Encoding.GetEncoding("ISO-8859-8").GetBytes(m.Value.ToLower()));
|
|
|
|
titleWords = titleWords.ToArray();
|
|
|
|
|
|
|
|
return queryWords.All(word => titleWords.Contains(word));
|
|
|
|
}
|
2020-12-22 17:21:00 +00:00
|
|
|
|
|
|
|
private string GetAbsoluteUrl(string url)
|
|
|
|
{
|
|
|
|
url = url.Trim();
|
|
|
|
if (!url.StartsWith("http"))
|
|
|
|
return SiteLink + url.TrimStart('/');
|
|
|
|
return url;
|
|
|
|
}
|
2022-01-16 10:30:26 +00:00
|
|
|
|
|
|
|
private string Base64Decode(string base64EncodedData)
|
|
|
|
{
|
|
|
|
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
|
|
|
|
return Encoding.UTF8.GetString(base64EncodedBytes);
|
|
|
|
}
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|