2020-02-09 02:35:16 +00:00
|
|
|
using System;
|
2015-07-19 02:19:09 +00:00
|
|
|
using System.Collections.Generic;
|
2020-05-03 23:35:52 +00:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2015-07-19 02:19:09 +00:00
|
|
|
using System.Globalization;
|
2020-04-26 18:09:11 +00:00
|
|
|
using System.Linq;
|
2020-04-30 11:10:03 +00:00
|
|
|
using System.Text.RegularExpressions;
|
2015-07-19 02:19:09 +00:00
|
|
|
using System.Threading.Tasks;
|
2020-02-29 20:29:51 +00:00
|
|
|
using AngleSharp.Html.Parser;
|
2020-04-28 20:36:42 +00:00
|
|
|
using Jackett.Common.Helpers;
|
2018-03-10 08:05:56 +00:00
|
|
|
using Jackett.Common.Models;
|
2023-02-01 23:39:54 +00:00
|
|
|
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
2018-03-10 08:05:56 +00:00
|
|
|
using Jackett.Common.Services.Interfaces;
|
|
|
|
using Jackett.Common.Utils;
|
|
|
|
using Jackett.Common.Utils.Clients;
|
2017-10-29 06:50:47 +00:00
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
using NLog;
|
2015-07-19 02:19:09 +00:00
|
|
|
|
2018-03-10 08:05:56 +00:00
|
|
|
namespace Jackett.Common.Indexers
|
2015-07-19 02:19:09 +00:00
|
|
|
{
|
2020-05-03 23:35:52 +00:00
|
|
|
[ExcludeFromCodeCoverage]
|
2023-03-09 14:06:12 +00:00
|
|
|
public class SpeedCD : IndexerBase
|
2015-07-19 02:19:09 +00:00
|
|
|
{
|
2023-03-09 14:06:12 +00:00
|
|
|
public override string Id => "speedcd";
|
|
|
|
public override string Name => "Speed.cd";
|
|
|
|
public override string Description => "Your home now!";
|
|
|
|
public override string SiteLink { get; protected set; } = "https://speed.cd/";
|
|
|
|
public override string[] AlternativeSiteLinks => new[]
|
|
|
|
{
|
|
|
|
"https://speed.cd/",
|
|
|
|
"https://speed.click/",
|
|
|
|
"https://speeders.me/"
|
|
|
|
};
|
|
|
|
public override string Language => "en-US";
|
|
|
|
public override string Type => "private";
|
|
|
|
|
2023-03-05 12:30:40 +00:00
|
|
|
public override bool SupportsPagination => true;
|
|
|
|
|
2023-03-10 11:20:29 +00:00
|
|
|
public override TorznabCapabilities TorznabCaps => SetCapabilities();
|
|
|
|
|
2020-04-28 20:36:42 +00:00
|
|
|
private string SearchUrl => SiteLink + "browse/";
|
2015-08-03 21:38:45 +00:00
|
|
|
|
2023-02-01 23:39:54 +00:00
|
|
|
private new ConfigurationDataSpeedCD configData => (ConfigurationDataSpeedCD)base.configData;
|
2015-08-03 21:38:45 +00:00
|
|
|
|
2020-12-11 22:14:21 +00:00
|
|
|
public SpeedCD(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
|
|
|
|
ICacheService cs)
|
2023-03-10 11:20:29 +00:00
|
|
|
: base(configService: configService,
|
2020-04-26 18:09:11 +00:00
|
|
|
client: wc,
|
|
|
|
logger: l,
|
|
|
|
p: ps,
|
2020-12-11 22:14:21 +00:00
|
|
|
cacheService: cs,
|
2023-02-01 23:39:54 +00:00
|
|
|
configData: new ConfigurationDataSpeedCD(
|
2024-03-27 23:20:46 +00:00
|
|
|
@"Speed.Cd have increased their security. If you are having problems please check the security tab in your
|
2024-03-19 06:43:25 +00:00
|
|
|
Speed.Cd profile. Eg. Geo Locking, your seedbox may be in a different country to the one where you login via your
|
|
|
|
web browser.<br><br>For best results, change the 'Torrents per page' setting to 100 in<br>'Profile Settings > Torrents'.
|
2024-03-27 23:20:46 +00:00
|
|
|
<br><br>This site may use Cloudflare DDoS Protection, therefore Jackett requires <a
|
2024-03-19 06:43:25 +00:00
|
|
|
href='https://github.com/Jackett/Jackett#configuring-flaresolverr' target='_blank'>FlareSolverr</a> to access it."))
|
2015-07-19 02:19:09 +00:00
|
|
|
{
|
2023-03-10 11:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private TorznabCapabilities SetCapabilities()
|
|
|
|
{
|
|
|
|
var caps = new TorznabCapabilities
|
|
|
|
{
|
|
|
|
TvSearchParams = new List<TvSearchParam>
|
|
|
|
{
|
|
|
|
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
|
|
|
},
|
|
|
|
MovieSearchParams = new List<MovieSearchParam>
|
|
|
|
{
|
|
|
|
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
|
|
|
},
|
|
|
|
MusicSearchParams = new List<MusicSearchParam>
|
|
|
|
{
|
|
|
|
MusicSearchParam.Q
|
|
|
|
},
|
|
|
|
BookSearchParams = new List<BookSearchParam>
|
|
|
|
{
|
|
|
|
BookSearchParam.Q
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
caps.Categories.AddCategoryMapping(1, TorznabCatType.MoviesOther, "Movies/XviD");
|
|
|
|
caps.Categories.AddCategoryMapping(42, TorznabCatType.Movies, "Movies/Packs");
|
|
|
|
caps.Categories.AddCategoryMapping(32, TorznabCatType.Movies, "Movies/Kids");
|
|
|
|
caps.Categories.AddCategoryMapping(43, TorznabCatType.MoviesHD, "Movies/HD");
|
|
|
|
caps.Categories.AddCategoryMapping(47, TorznabCatType.Movies, "Movies/DiVERSiTY");
|
|
|
|
caps.Categories.AddCategoryMapping(28, TorznabCatType.MoviesBluRay, "Movies/B-Ray");
|
|
|
|
caps.Categories.AddCategoryMapping(48, TorznabCatType.Movies3D, "Movies/3D");
|
|
|
|
caps.Categories.AddCategoryMapping(40, TorznabCatType.MoviesDVD, "Movies/DVD-R");
|
|
|
|
caps.Categories.AddCategoryMapping(56, TorznabCatType.Movies, "Movies/Anime");
|
|
|
|
caps.Categories.AddCategoryMapping(50, TorznabCatType.TVSport, "TV/Sports");
|
|
|
|
caps.Categories.AddCategoryMapping(52, TorznabCatType.TVHD, "TV/B-Ray");
|
|
|
|
caps.Categories.AddCategoryMapping(53, TorznabCatType.TVSD, "TV/DVD-R");
|
|
|
|
caps.Categories.AddCategoryMapping(41, TorznabCatType.TV, "TV/Packs");
|
|
|
|
caps.Categories.AddCategoryMapping(55, TorznabCatType.TV, "TV/Kids");
|
|
|
|
caps.Categories.AddCategoryMapping(57, TorznabCatType.TV, "TV/DiVERSiTY");
|
|
|
|
caps.Categories.AddCategoryMapping(49, TorznabCatType.TVHD, "TV/HD");
|
|
|
|
caps.Categories.AddCategoryMapping(2, TorznabCatType.TVSD, "TV/Episodes");
|
|
|
|
caps.Categories.AddCategoryMapping(30, TorznabCatType.TVAnime, "TV/Anime");
|
|
|
|
caps.Categories.AddCategoryMapping(25, TorznabCatType.PCISO, "Games/PC ISO");
|
|
|
|
caps.Categories.AddCategoryMapping(39, TorznabCatType.ConsoleWii, "Games/Wii");
|
|
|
|
caps.Categories.AddCategoryMapping(45, TorznabCatType.ConsolePS3, "Games/PS3");
|
|
|
|
caps.Categories.AddCategoryMapping(35, TorznabCatType.Console, "Games/Nintendo");
|
|
|
|
caps.Categories.AddCategoryMapping(33, TorznabCatType.ConsoleXBox360, "Games/XboX360");
|
|
|
|
caps.Categories.AddCategoryMapping(46, TorznabCatType.PCMobileOther, "Mobile");
|
|
|
|
caps.Categories.AddCategoryMapping(24, TorznabCatType.PC0day, "Apps/0DAY");
|
|
|
|
caps.Categories.AddCategoryMapping(51, TorznabCatType.PCMac, "Mac");
|
|
|
|
caps.Categories.AddCategoryMapping(54, TorznabCatType.Books, "Educational");
|
|
|
|
caps.Categories.AddCategoryMapping(27, TorznabCatType.Books, "Books-Mags");
|
|
|
|
caps.Categories.AddCategoryMapping(26, TorznabCatType.Audio, "Music/Audio");
|
|
|
|
caps.Categories.AddCategoryMapping(3, TorznabCatType.Audio, "Music/Flac");
|
|
|
|
caps.Categories.AddCategoryMapping(44, TorznabCatType.Audio, "Music/Pack");
|
|
|
|
caps.Categories.AddCategoryMapping(29, TorznabCatType.AudioVideo, "Music/Video");
|
|
|
|
|
|
|
|
return caps;
|
2015-07-19 02:19:09 +00:00
|
|
|
}
|
|
|
|
|
2017-06-28 05:31:38 +00:00
|
|
|
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
2015-07-19 02:19:09 +00:00
|
|
|
{
|
2017-01-02 20:39:28 +00:00
|
|
|
LoadValuesFromJson(configJson);
|
2020-04-30 11:10:03 +00:00
|
|
|
await DoLogin();
|
|
|
|
return IndexerConfigurationStatus.RequiresTesting;
|
|
|
|
}
|
|
|
|
|
|
|
|
private async Task DoLogin()
|
|
|
|
{
|
|
|
|
// first request with username
|
2024-03-27 23:20:46 +00:00
|
|
|
var result = await RequestLoginAndFollowRedirect(
|
2024-03-27 23:43:15 +00:00
|
|
|
$"{SiteLink}checkpoint/API",
|
2024-03-27 23:20:46 +00:00
|
|
|
new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "username", configData.Username.Value }
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
true,
|
|
|
|
null,
|
|
|
|
SiteLink);
|
|
|
|
|
2020-04-30 11:10:03 +00:00
|
|
|
var tokenRegex = new Regex(@"name=\\""a\\"" value=\\""([^""]+)\\""");
|
2020-06-09 17:36:57 +00:00
|
|
|
var matches = tokenRegex.Match(result.ContentString);
|
2020-04-30 11:10:03 +00:00
|
|
|
if (!matches.Success)
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2020-04-30 11:10:03 +00:00
|
|
|
throw new Exception("Error parsing the login form");
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 11:10:03 +00:00
|
|
|
var token = matches.Groups[1].Value;
|
|
|
|
|
|
|
|
// second request with token and password
|
2024-03-27 23:20:46 +00:00
|
|
|
result = await RequestLoginAndFollowRedirect(
|
2024-03-27 23:43:15 +00:00
|
|
|
$"{SiteLink}checkpoint/",
|
2024-03-27 23:20:46 +00:00
|
|
|
new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "pwd", configData.Password.Value },
|
|
|
|
{ "a", token }
|
|
|
|
},
|
|
|
|
result.Cookies,
|
|
|
|
true,
|
|
|
|
null,
|
|
|
|
SiteLink);
|
2020-04-30 11:10:03 +00:00
|
|
|
|
2020-06-09 17:36:57 +00:00
|
|
|
await ConfigureIfOK(result.Cookies, result.ContentString?.Contains("/browse.php") == true, () =>
|
2020-04-26 18:09:11 +00:00
|
|
|
{
|
2020-04-30 11:10:03 +00:00
|
|
|
var parser = new HtmlParser();
|
2023-10-05 01:06:23 +00:00
|
|
|
using var dom = parser.ParseDocument(result.ContentString);
|
2020-04-30 11:10:03 +00:00
|
|
|
var errorMessage = dom.QuerySelector("h5")?.TextContent;
|
2023-02-01 23:39:54 +00:00
|
|
|
|
2020-06-09 17:36:57 +00:00
|
|
|
if (result.ContentString.Contains("Wrong Captcha!"))
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2020-04-30 11:10:03 +00:00
|
|
|
errorMessage = "Captcha required due to a failed login attempt. Login via a browser to whitelist your IP and then reconfigure Jackett.";
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
|
|
|
|
throw new Exception(errorMessage ?? "Login failed.");
|
2020-04-30 11:10:03 +00:00
|
|
|
});
|
2015-07-19 02:19:09 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 05:15:47 +00:00
|
|
|
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
2015-07-19 02:19:09 +00:00
|
|
|
{
|
2015-07-27 00:03:51 +00:00
|
|
|
var releases = new List<ReleaseInfo>();
|
2015-12-09 08:45:44 +00:00
|
|
|
|
2020-04-28 20:36:42 +00:00
|
|
|
// the order of the params is important!
|
|
|
|
var qc = new List<string>();
|
|
|
|
|
|
|
|
var catList = MapTorznabCapsToTrackers(query);
|
|
|
|
foreach (var cat in catList)
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2020-04-28 20:36:42 +00:00
|
|
|
qc.Add(cat);
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2020-04-28 20:36:42 +00:00
|
|
|
|
2023-02-01 23:39:54 +00:00
|
|
|
if (configData.Freeleech.Value)
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
qc.Add("freeleech");
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
|
|
|
|
if (configData.ExcludeArchives.Value)
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
qc.Add("norar");
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
|
2020-03-31 20:26:07 +00:00
|
|
|
if (query.IsImdbQuery)
|
2015-12-09 08:45:44 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
var term = query.ImdbID;
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(query.GetEpisodeSearchString()))
|
|
|
|
{
|
|
|
|
term += $" {query.GetEpisodeSearchString()}";
|
|
|
|
|
|
|
|
if (query.Season > 0 && string.IsNullOrEmpty(query.Episode))
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
term += "*";
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
}
|
|
|
|
|
2020-04-28 20:36:42 +00:00
|
|
|
qc.Add("deep");
|
|
|
|
qc.Add("q");
|
2023-02-01 23:39:54 +00:00
|
|
|
qc.Add(WebUtilityHelpers.UrlEncode(term.Trim(), Encoding));
|
2015-12-09 08:45:44 +00:00
|
|
|
}
|
2020-03-31 20:26:07 +00:00
|
|
|
else
|
2020-04-28 20:36:42 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
var term = query.GetQueryString();
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(query.GetEpisodeSearchString()) && query.Season > 0 && string.IsNullOrEmpty(query.Episode))
|
2024-03-27 23:20:46 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
term += "*";
|
2024-03-27 23:20:46 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
|
2020-04-28 20:36:42 +00:00
|
|
|
qc.Add("q");
|
2023-02-01 23:39:54 +00:00
|
|
|
qc.Add(WebUtilityHelpers.UrlEncode(term.Trim(), Encoding));
|
2020-04-28 20:36:42 +00:00
|
|
|
}
|
2016-01-19 13:08:00 +00:00
|
|
|
|
2023-03-05 12:30:40 +00:00
|
|
|
if (query.Limit > 0 && query.Offset > 0)
|
|
|
|
{
|
|
|
|
var page = query.Offset / query.Limit + 1;
|
|
|
|
|
|
|
|
qc.Add("p");
|
|
|
|
qc.Add(page.ToString());
|
|
|
|
}
|
|
|
|
|
2020-04-28 20:36:42 +00:00
|
|
|
var searchUrl = SearchUrl + string.Join("/", qc);
|
2020-06-11 15:09:27 +00:00
|
|
|
var response = await RequestWithCookiesAndRetryAsync(searchUrl);
|
2023-02-01 23:39:54 +00:00
|
|
|
|
2020-06-09 17:36:57 +00:00
|
|
|
if (!response.ContentString.Contains("/logout.php")) // re-login
|
2020-04-30 11:10:03 +00:00
|
|
|
{
|
|
|
|
await DoLogin();
|
2020-06-11 15:09:27 +00:00
|
|
|
response = await RequestWithCookiesAndRetryAsync(searchUrl);
|
2020-04-30 11:10:03 +00:00
|
|
|
}
|
2015-07-19 02:19:09 +00:00
|
|
|
|
2016-01-19 13:08:00 +00:00
|
|
|
try
|
|
|
|
{
|
2020-02-29 20:29:51 +00:00
|
|
|
var parser = new HtmlParser();
|
2023-10-05 01:06:23 +00:00
|
|
|
using var dom = parser.ParseDocument(response.ContentString);
|
2015-07-19 02:19:09 +00:00
|
|
|
|
2023-02-01 23:39:54 +00:00
|
|
|
var rows = dom.QuerySelectorAll("div.boxContent > table > tbody > tr");
|
2020-02-10 22:16:19 +00:00
|
|
|
foreach (var row in rows)
|
2016-01-19 13:08:00 +00:00
|
|
|
{
|
2023-02-01 23:39:54 +00:00
|
|
|
var title = CleanTitle(row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]")?.TextContent);
|
|
|
|
var link = new Uri(SiteLink + row.QuerySelector("td:nth-child(4) a[href^=\"/download/\"]")?.GetAttribute("href")?.TrimStart('/'));
|
|
|
|
var details = new Uri(SiteLink + row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]")?.GetAttribute("href")?.TrimStart('/'));
|
|
|
|
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)")?.TextContent);
|
|
|
|
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)")?.TextContent);
|
|
|
|
var pubDateStr = row.QuerySelector("td:nth-child(2) span[class^=\"elapsedDate\"]")?.GetAttribute("title")?.Replace(" at", "");
|
|
|
|
var cat = row.QuerySelector("td:nth-child(1) a")?.GetAttribute("href")?.Split('/').Last();
|
|
|
|
var downloadVolumeFactor = row.QuerySelector("td:nth-child(2) span:contains(\"[Freeleech]\")") != null ? 0 : 1;
|
2020-03-31 20:26:07 +00:00
|
|
|
|
2020-03-26 22:15:28 +00:00
|
|
|
var release = new ReleaseInfo
|
|
|
|
{
|
2020-03-31 20:26:07 +00:00
|
|
|
Guid = link,
|
2023-02-01 23:39:54 +00:00
|
|
|
Link = link,
|
2020-11-08 02:11:27 +00:00
|
|
|
Details = details,
|
2023-02-01 23:39:54 +00:00
|
|
|
Title = title,
|
2020-03-31 20:26:07 +00:00
|
|
|
Category = MapTrackerCatToNewznab(cat),
|
2023-02-01 23:39:54 +00:00
|
|
|
PublishDate = DateTime.ParseExact(pubDateStr, "dddd, MMMM d, yyyy h:mmtt", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal),
|
2023-02-24 23:22:13 +00:00
|
|
|
Size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(6)")?.TextContent),
|
2023-02-01 23:39:54 +00:00
|
|
|
Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)")?.TextContent),
|
2020-03-26 22:15:28 +00:00
|
|
|
Seeders = seeders,
|
|
|
|
Peers = seeders + leechers,
|
|
|
|
DownloadVolumeFactor = downloadVolumeFactor,
|
2023-02-01 23:39:54 +00:00
|
|
|
UploadVolumeFactor = 1,
|
|
|
|
MinimumRatio = 1,
|
|
|
|
MinimumSeedTime = 259200 // 72 hours
|
2020-03-26 22:15:28 +00:00
|
|
|
};
|
2020-03-31 20:26:07 +00:00
|
|
|
|
2015-07-19 02:19:09 +00:00
|
|
|
releases.Add(release);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-06-09 17:36:57 +00:00
|
|
|
OnParseError(response.ContentString, ex);
|
2015-07-19 02:19:09 +00:00
|
|
|
}
|
2015-07-28 23:10:04 +00:00
|
|
|
return releases;
|
2015-07-19 02:19:09 +00:00
|
|
|
}
|
2023-02-01 23:39:54 +00:00
|
|
|
|
|
|
|
private static string CleanTitle(string title)
|
|
|
|
{
|
|
|
|
title = Regex.Replace(title, @"\[REQ(UEST)?\]", string.Empty, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
|
|
|
|
return title.Trim(' ', '.');
|
|
|
|
}
|
2015-07-19 02:19:09 +00:00
|
|
|
}
|
2016-01-20 08:45:39 +00:00
|
|
|
}
|