mirror of
https://github.com/Jackett/Jackett
synced 2025-02-24 15:21:06 +00:00
Add BlueTigers Indexer
Adds BlueTigers Indexer. Torznab Category mapping still to come.
This commit is contained in:
parent
02d0f9c5a1
commit
f999e6657b
5 changed files with 280 additions and 0 deletions
|
@ -25,6 +25,7 @@ We were previously focused on TV but are working on extending searches to allow
|
|||
* [BeyondHD](https://beyondhd.me/)
|
||||
* [BIT-HDTV](https://www.bit-hdtv.com)
|
||||
* [BitMeTV](http://www.bitmetv.org/)
|
||||
* [BlueTigers](https://www.bluetigers.ca/)
|
||||
* [BTN](http://broadcasthe.net)
|
||||
* [Demonoid](http://www.demonoid.pw/)
|
||||
* [EuTorrents](https://eutorrents.to/)
|
||||
|
|
BIN
src/Jackett/Content/logos/bluetigers.png
Normal file
BIN
src/Jackett/Content/logos/bluetigers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
211
src/Jackett/Indexers/BlueTigers.cs
Normal file
211
src/Jackett/Indexers/BlueTigers.cs
Normal file
|
@ -0,0 +1,211 @@
|
|||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BlueTigers : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl => SiteLink + "account-login.php";
|
||||
private string TorrentSearchUrl => SiteLink + "torrents-search.php";
|
||||
private string IndexUrl => SiteLink + "index.php";
|
||||
|
||||
private ConfigurationDataBlueTigers ConfigData
|
||||
{
|
||||
get { return (ConfigurationDataBlueTigers)configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BlueTigers(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BlueTigers",
|
||||
description: "BlueTigers - No Ratio - Private",
|
||||
link: "https://www.bluetigers.ca/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBlueTigers(@"BlueTigers can search for one or all languages.
|
||||
If you select 2 languages below, results will contain all 3 languages.
|
||||
<br> For best results change the torrents per page setting to 50 in your BlueTigers profile."))
|
||||
{
|
||||
AddCategoryMapping(19, TorznabCatType.TV);
|
||||
AddCategoryMapping(2, TorznabCatType.TV);
|
||||
AddCategoryMapping(17, TorznabCatType.TV);
|
||||
|
||||
AddCategoryMapping(52, TorznabCatType.ConsoleXbox);
|
||||
|
||||
AddCategoryMapping(29, TorznabCatType.PCMac);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
ConfigData.LoadValuesFromJson(configJson);
|
||||
|
||||
if (ConfigData.French.Value == false && ConfigData.English.Value == false && ConfigData.Spanish.Value == false)
|
||||
throw new ExceptionWithConfigData("Please select at least one language.", ConfigData);
|
||||
|
||||
await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value },
|
||||
{ "take_login", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, IndexUrl, SiteLink);
|
||||
Regex rgx = new Regex(@"uid=[0-9]{1,10}; pass=[a-z0-9]{1,40};");
|
||||
await ConfigureIfOK(result.Cookies, rgx.IsMatch(result.Cookies), () =>
|
||||
{
|
||||
var errorMessage = "Error while trying to login.";
|
||||
throw new ExceptionWithConfigData(errorMessage, ConfigData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
NameValueCollection qParams = new NameValueCollection();
|
||||
if (ConfigData.French.Value && !ConfigData.English.Value && !ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ConfigData.French.Value && ConfigData.English.Value && !ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "2");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ConfigData.French.Value && !ConfigData.English.Value && ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "3");
|
||||
}
|
||||
else
|
||||
{
|
||||
qParams.Add("lang", "0");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<string> catList = MapTorznabCapsToTrackers(query);
|
||||
foreach (string cat in catList)
|
||||
{
|
||||
qParams.Add("cat", cat);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.SanitizedSearchTerm))
|
||||
{
|
||||
qParams.Add("search", query.GetQueryString());
|
||||
}
|
||||
|
||||
string queryStr = qParams.GetQueryString();
|
||||
string searchUrl = $"{TorrentSearchUrl}?incldead=0&freeleech=0&sort=id&order=ascdesc&{queryStr}";
|
||||
|
||||
List<CQ> torrentRowList = new List<CQ>();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ fDom = results.Content;
|
||||
var firstPageRows = fDom["table[class='ttable_headinner'] > tbody > tr:not(:First-child)"];
|
||||
torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));
|
||||
|
||||
//If a search term is used, follow upto the first 4 pages (initial plus 3 more)
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()) && fDom["a[class='boutons']"].Filter("a[href*=&page=]").Length > 0)
|
||||
{
|
||||
int pageLinkCount;
|
||||
int.TryParse(fDom["a[class='boutons']"].Filter("a[href*=&page=]").Last().Attr("href").Split(new[] { "&page=" }, StringSplitOptions.None).LastOrDefault(), out pageLinkCount);
|
||||
for (int i = 1; i < Math.Min(4, pageLinkCount + 1); i++)
|
||||
{
|
||||
var sResults = await RequestStringWithCookiesAndRetry($"{searchUrl}&page={i}");
|
||||
CQ sDom = sResults.Content;
|
||||
var additionalPageRows = sDom["table[class='ttable_headinner'] > tbody > tr:not(:First-child)"];
|
||||
torrentRowList.AddRange(additionalPageRows.Select(sRow => sRow.Cq()));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CQ tRow in torrentRowList)
|
||||
{
|
||||
long torrentId = 0;
|
||||
string idTarget = "bookmarks.php?torrent=";
|
||||
string id = tRow.Find("a[href*=" + idTarget + "]").First().Attr("href").Trim();
|
||||
if (!string.IsNullOrEmpty(id) && id.Contains(idTarget))
|
||||
{
|
||||
long.TryParse(id.Substring(id.LastIndexOf(idTarget, StringComparison.Ordinal) + idTarget.Length), out torrentId);
|
||||
}
|
||||
|
||||
if (torrentId <= 0) continue;
|
||||
|
||||
long category = 0;
|
||||
string catTarget = "torrents.php?cat=";
|
||||
string cat = tRow.Find("a[href*=" + catTarget + "]").First().Attr("href").Trim();
|
||||
if (!string.IsNullOrEmpty(cat) && cat.Contains(catTarget))
|
||||
{
|
||||
long.TryParse(cat.Substring(cat.LastIndexOf(catTarget, StringComparison.Ordinal) + catTarget.Length), out category);
|
||||
}
|
||||
|
||||
Uri guid = new Uri($"{SiteLink}torrents-details.php?hit=1&id={torrentId}");
|
||||
Uri link = new Uri($"{SiteLink}download.php?hit=1&id={torrentId}");
|
||||
Uri comments = new Uri($"{SiteLink}comments.php?type=torrent&id={torrentId}");
|
||||
string title = tRow.Find("a[href*=torrents-details.php?id=]").First().Text().Trim();
|
||||
string stats = tRow.Find("div[id=kt" + torrentId.ToString() + "]").First().Text();
|
||||
string sizeStr = new Regex("Taille:(.*)Vitesse:").Match(stats).Groups[1].ToString().Trim();
|
||||
string pubDateStr = new Regex("Ajout.:(.*)Compl.t.s").Match(stats).Groups[1].ToString().Trim();
|
||||
DateTime pubDate = DateTime.ParseExact(pubDateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
|
||||
|
||||
string statistics = tRow.Find("a[href*=torrents-details.php?id=]").First().RenderSelection().Trim();
|
||||
string startTag = "<table ";
|
||||
string endTag = "</table>";
|
||||
CQ statsCq = startTag + new Regex(startTag + "(.*)" + endTag).Match(statistics).Groups[1].ToString().Trim() + endTag;
|
||||
int seeders;
|
||||
int leechers;
|
||||
int.TryParse(statsCq.Find("font[color=#05FC09]").First().Text(), out seeders);
|
||||
int.TryParse(statsCq.Find("font[color=red]").First().Text(), out leechers);
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = title;
|
||||
release.Guid = guid;
|
||||
release.Link = link;
|
||||
release.PublishDate = pubDate;
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
release.Description = title;
|
||||
release.Seeders = seeders;
|
||||
release.Peers = leechers + seeders;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Category = MapTrackerCatToNewznab(category.ToString());
|
||||
release.Comments = comments;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -179,6 +179,7 @@
|
|||
<Compile Include="Controllers\DownloadController.cs" />
|
||||
<Compile Include="Engine.cs" />
|
||||
<Compile Include="Indexers\AlphaRatio.cs" />
|
||||
<Compile Include="Indexers\BlueTigers.cs" />
|
||||
<Compile Include="Indexers\EuTorrents.cs" />
|
||||
<Compile Include="Indexers\Avistaz.cs" />
|
||||
<Compile Include="Indexers\BakaBT.cs" />
|
||||
|
@ -203,6 +204,7 @@
|
|||
<Compile Include="Indexers\FileList.cs" />
|
||||
<Compile Include="Indexers\Abstract\AvistazTracker.cs" />
|
||||
<Compile Include="Indexers\AnimeTorrents.cs" />
|
||||
<Compile Include="Models\IndexerConfig\Bespoke\ConfigurationDataBlueTigers.cs" />
|
||||
<Compile Include="Models\IndexerConfig\ConfigurationDataBasicLoginWithFilter.cs" />
|
||||
<Compile Include="Models\IndexerConfig\ConfigurationDataAPIKey.cs" />
|
||||
<Compile Include="Models\ManualSearchResult.cs" />
|
||||
|
@ -439,6 +441,9 @@
|
|||
<Content Include="Content\logos\beyondhd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\bluetigers.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\broadcastthenet.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Jackett.Models.IndexerConfig.Bespoke
|
||||
{
|
||||
public class ConfigurationDataBlueTigers : ConfigurationData
|
||||
{
|
||||
public StringItem Username { get; private set; }
|
||||
public StringItem Password { get; private set; }
|
||||
public DisplayItem Instructions { get; set; }
|
||||
public BoolItem French { get; set; }
|
||||
public BoolItem English { get; set; }
|
||||
public BoolItem Spanish { get; set; }
|
||||
|
||||
public ConfigurationDataBlueTigers(string displayInstructions)
|
||||
{
|
||||
Username = new StringItem { Name = "Username", Value = "" };
|
||||
Password = new StringItem { Name = "Password", Value = "" };
|
||||
Instructions = new DisplayItem(displayInstructions) { Name = "" };
|
||||
French = new BoolItem { Name = "French", Value = true };
|
||||
English = new BoolItem { Name = "English", Value = true };
|
||||
Spanish = new BoolItem { Name = "Spanish", Value = true };
|
||||
}
|
||||
|
||||
public ConfigurationDataBlueTigers(JToken json)
|
||||
{
|
||||
ConfigurationDataNCore configData = new ConfigurationDataNCore();
|
||||
|
||||
dynamic configArray = JsonConvert.DeserializeObject(json.ToString());
|
||||
foreach (var config in configArray)
|
||||
{
|
||||
string propertyName = UppercaseFirst((string)config.id);
|
||||
switch (propertyName)
|
||||
{
|
||||
case "Username":
|
||||
Username = new StringItem { Name = propertyName, Value = config.value };
|
||||
break;
|
||||
case "Password":
|
||||
Password = new StringItem { Name = propertyName, Value = config.value };
|
||||
break;
|
||||
case "French":
|
||||
French = new BoolItem { Name = propertyName, Value = config.value };
|
||||
break;
|
||||
case "English":
|
||||
English = new BoolItem { Name = propertyName, Value = config.value };
|
||||
break;
|
||||
case "Spanish":
|
||||
Spanish = new BoolItem { Name = propertyName, Value = config.value };
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string UppercaseFirst(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return string.Empty;
|
||||
return char.ToUpper(s[0]) + s.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue