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;
|
2023-09-04 00:46:11 +00:00
|
|
|
using Jackett.Common.Extensions;
|
2020-04-07 19:58:36 +00:00
|
|
|
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;
|
|
|
|
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";
|
2023-09-04 05:16:16 +00:00
|
|
|
public override string Description => "Películas Full UHD/HD en Latino Dual.";
|
2023-11-12 05:52:24 +00:00
|
|
|
public override string SiteLink { get; protected set; } = "https://wvw.cinecalidad.so/";
|
2023-03-09 14:06:12 +00:00
|
|
|
public override string[] LegacySiteLinks => new[]
|
|
|
|
{
|
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-26 04:15:53 +00:00
|
|
|
"https://www.cinecalidad.foo/",
|
|
|
|
"https://vww.cinecalidad.foo/",
|
2023-09-02 04:42:13 +00:00
|
|
|
"https://vw.cinecalidad.foo/",
|
2023-09-21 18:09:45 +00:00
|
|
|
"https://ww.cinecalidad.foo/",
|
2023-09-22 01:45:21 +00:00
|
|
|
"https://v1.cinecalidad.foo/",
|
2023-09-23 17:07:04 +00:00
|
|
|
"https://w.cinecalidad.foo/",
|
2023-09-30 17:39:25 +00:00
|
|
|
"https://wwv.cinecalidad.foo/",
|
2023-10-06 03:10:29 +00:00
|
|
|
"https://wv.cinecalidad.foo/",
|
2023-10-10 18:02:09 +00:00
|
|
|
"https://vwv.cinecalidad.foo/",
|
2023-10-12 17:19:15 +00:00
|
|
|
"https://wzw.cinecalidad.foo/",
|
2023-10-29 16:56:11 +00:00
|
|
|
"https://v2.cinecalidad.foo/",
|
2023-11-12 05:52:24 +00:00
|
|
|
"https://www.cinecalidad.so/"
|
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);
|
2023-09-04 00:46:11 +00:00
|
|
|
caps.Categories.AddCategoryMapping(2, TorznabCatType.MoviesUHD);
|
2023-03-10 11:20:29 +00:00
|
|
|
|
|
|
|
return caps;
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
|
|
|
{
|
|
|
|
LoadValuesFromJson(configJson);
|
2023-09-04 00:46:11 +00:00
|
|
|
|
2020-04-07 19:58:36 +00:00
|
|
|
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
|
2023-09-04 00:46:11 +00:00
|
|
|
|
2022-03-08 20:13:47 +00:00
|
|
|
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)
|
2023-09-04 00:46:11 +00:00
|
|
|
{
|
|
|
|
// this is the last page
|
|
|
|
break;
|
|
|
|
}
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return releases;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override async Task<byte[]> Download(Uri link)
|
|
|
|
{
|
2023-09-04 00:46:11 +00:00
|
|
|
var parser = new HtmlParser();
|
|
|
|
|
2020-09-21 16:39:47 +00:00
|
|
|
var results = await RequestWithCookiesAsync(link.ToString());
|
2020-04-07 19:58:36 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2023-10-05 01:06:23 +00:00
|
|
|
using var dom = await parser.ParseDocumentAsync(results.ContentString);
|
2023-09-04 00:46:11 +00:00
|
|
|
|
|
|
|
var downloadLink = link.Query.Contains("type=4k")
|
|
|
|
? dom.QuerySelector("ul.links a:contains('Bittorrent 4K')")
|
|
|
|
: dom.QuerySelector("ul.links a:contains('Torrent')");
|
|
|
|
|
|
|
|
var protectedLink = downloadLink?.GetAttribute("data-url");
|
|
|
|
|
|
|
|
if (protectedLink.IsNullOrWhiteSpace())
|
|
|
|
{
|
|
|
|
throw new Exception($"Invalid download link for {link}");
|
|
|
|
}
|
|
|
|
|
2022-01-16 10:30:26 +00:00
|
|
|
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();
|
2023-09-04 00:46:11 +00:00
|
|
|
protectedLink = link.AddQueryParameter("link", key).ToString();
|
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);
|
2023-10-05 01:06:23 +00:00
|
|
|
|
|
|
|
using var document = parser.ParseDocument(results.ContentString);
|
|
|
|
var magnetUrl = document.QuerySelector("a[href^=magnet]").GetAttribute("href");
|
|
|
|
|
2020-04-07 19:58:36 +00:00
|
|
|
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();
|
2023-10-05 01:06:23 +00:00
|
|
|
using var dom = parser.ParseDocument(response.ContentString);
|
2020-04-07 19:58:36 +00:00
|
|
|
|
2023-09-04 00:46:11 +00:00
|
|
|
var rows = dom.QuerySelectorAll("article:has(a.absolute):has(img.rounded)");
|
|
|
|
|
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)
|
2023-09-04 00:46:11 +00:00
|
|
|
{
|
|
|
|
// we only support movies
|
|
|
|
continue;
|
|
|
|
}
|
2022-01-16 10:30:26 +00:00
|
|
|
|
|
|
|
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)
|
2023-09-04 00:46:11 +00:00
|
|
|
{
|
|
|
|
// skip results without image
|
|
|
|
continue;
|
|
|
|
}
|
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))
|
2023-09-04 00:46:11 +00:00
|
|
|
{
|
|
|
|
// skip if it doesn't contain all words
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var poster = new Uri(GetAbsoluteUrl(qImg.GetAttribute("data-src") ?? 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
|
|
|
|
2023-09-04 00:46:11 +00:00
|
|
|
releases.Add(new ReleaseInfo
|
2020-04-07 19:58:36 +00:00
|
|
|
{
|
|
|
|
Guid = link,
|
2023-09-04 00:46:11 +00:00
|
|
|
Details = link,
|
|
|
|
Link = link,
|
|
|
|
Title = $"{title} MULTi/LATiN SPANiSH 1080p BDRip x264",
|
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
|
2023-09-04 00:46:11 +00:00
|
|
|
});
|
2020-04-07 19:58:36 +00:00
|
|
|
|
2023-09-04 00:46:11 +00:00
|
|
|
if (row.QuerySelector("a[aria-label=\"4K\"]") != null)
|
|
|
|
{
|
|
|
|
var link4K = link.AddQueryParameter("type", "4k");
|
|
|
|
|
|
|
|
releases.Add(new ReleaseInfo
|
|
|
|
{
|
|
|
|
Guid = link4K,
|
|
|
|
Details = link,
|
|
|
|
Link = link4K,
|
2023-09-04 05:16:16 +00:00
|
|
|
Title = $"{title} MULTi/LATiN SPANiSH 2160p BDRip x265",
|
2023-09-04 00:46:11 +00:00
|
|
|
Category = new List<int> { TorznabCatType.MoviesUHD.ID },
|
|
|
|
Poster = poster,
|
2023-09-04 05:16:16 +00:00
|
|
|
Size = 10737418240, // 10 GB
|
2023-09-04 00:46:11 +00:00
|
|
|
Files = 1,
|
|
|
|
Seeders = 1,
|
|
|
|
Peers = 2,
|
|
|
|
DownloadVolumeFactor = 0,
|
|
|
|
UploadVolumeFactor = 1
|
|
|
|
});
|
|
|
|
}
|
2020-04-07 19:58:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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();
|
2023-09-04 00:46:11 +00:00
|
|
|
|
2020-12-22 17:21:00 +00:00
|
|
|
if (!url.StartsWith("http"))
|
2023-09-04 00:46:11 +00:00
|
|
|
{
|
2020-12-22 17:21:00 +00:00
|
|
|
return SiteLink + url.TrimStart('/');
|
2023-09-04 00:46:11 +00:00
|
|
|
}
|
|
|
|
|
2020-12-22 17:21:00 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
}
|