Only allow private trackers
81
README.md
@@ -2,12 +2,10 @@
|
||||
|
||||
This project is a new fork and is recruiting development help. If you are able to help out please contact us.
|
||||
|
||||
#### Overview
|
||||
This software creates a [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/master/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) API server on your machine. Torznab enables software such as [Sonarr](https://sonarr.tv) to access data from your favorite indexers in a similar fashion to rss but with added features such as searching. TorrentPotato is an interface accessible to [CouchPotato](https://couchpota.to/).
|
||||
|
||||
Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.
|
||||
|
||||
We were previously focused on TV but are working on extending searches to allow for searching other items such as movies, comics, and music.
|
||||
Developer note: The software implments the [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/master/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) APIs.
|
||||
|
||||
|
||||
|
||||
#### Supported Systems
|
||||
@@ -15,47 +13,38 @@ We were previously focused on TV but are working on extending searches to allow
|
||||
* Linux and OSX using Mono 4 (v3 should work but you may experience crashes).
|
||||
|
||||
|
||||
#### Supported Trackers
|
||||
* [AlphaRatio](https://alpharatio.cc/)
|
||||
* [AnimeBytes](https://animebytes.tv/)
|
||||
* [AnimeTorrents](http://animetorrents.me/)
|
||||
* [Avistaz](https://avistaz.to/)
|
||||
* [BakaBT](http://bakabt.me/)
|
||||
* [bB](http://reddit.com/r/baconbits)
|
||||
* [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/)
|
||||
* [FileList](http://filelist.ro/)
|
||||
* [FrenchTorrentDb](http://www.frenchtorrentdb.com/)
|
||||
* [Freshon](https://freshon.tv/)
|
||||
* [HD-Space](https://hd-space.org/)
|
||||
* [HD-Torrents.org](https://hd-torrents.org/)
|
||||
* [Immortalseed.me](http://immortalseed.me)
|
||||
* [IPTorrents](https://iptorrents.com/)
|
||||
* [MoreThan.tv](https://morethan.tv/)
|
||||
* [NextGen](https://nxtgn.org/)
|
||||
* [pretome](https://pretome.info)
|
||||
* [PrivateHD](https://privatehd.to/)
|
||||
* [RARBG](https://rarbg.to/)
|
||||
* [RuTor](http://rutor.org/)
|
||||
* [SceneAccess](https://sceneaccess.eu/login)
|
||||
* [SceneTime](https://www.scenetime.com/)
|
||||
* [Shazbat](www.shazbat.tv/login)
|
||||
* [ShowRSS](https://showrss.info/)
|
||||
* [Strike](https://getstrike.net/)
|
||||
* [T411](http://www.t411.io/)
|
||||
* [TehConnection](https://tehconnection.eu/)
|
||||
* [The Pirate Bay](https://thepiratebay.se/)
|
||||
* [TorrentBytes](https://www.torrentbytes.net/)
|
||||
* [TorrentDay](https://torrentday.eu/)
|
||||
* [TorrentLeech](http://www.torrentleech.org/)
|
||||
* [TorrentShack](http://torrentshack.me/)
|
||||
* [Torrentz](https://torrentz.eu/)
|
||||
* [TV Chaos UK](https://tvchaosuk.com/)
|
||||
#### Supported Private Trackers
|
||||
* AlphaRatio
|
||||
* AnimeBytes
|
||||
* Avistaz
|
||||
* BakaBT
|
||||
* bB
|
||||
* BeyondHD
|
||||
* BIT-HDTV
|
||||
* BitMeTV
|
||||
* BlueTigers
|
||||
* BTN
|
||||
* EuTorrents
|
||||
* FileList
|
||||
* Freshon
|
||||
* HD-Space
|
||||
* HD-Torrents
|
||||
* Immortalseed
|
||||
* IPTorrents
|
||||
* MoreThan
|
||||
* NextGen
|
||||
* Pretome
|
||||
* PrivateHD
|
||||
* SceneAccess
|
||||
* SceneTime
|
||||
* Shazbat
|
||||
* Strike
|
||||
* TehConnection
|
||||
* TorrentBytes
|
||||
* TorrentDay
|
||||
* TorrentLeech
|
||||
* TorrentShack
|
||||
* TV Chaos UK
|
||||
|
||||
#### Installation on Windows
|
||||
|
||||
@@ -97,7 +86,7 @@ Try running with the "--SSLFix true" if you are on Redhat/Fedora/NNS based libcu
|
||||
You can get additional logging with the switches "-t -l". Please post logs if you are unable to resolve your issue with these switches ensuring to remove your username/password/cookies.
|
||||
|
||||
### Contributing
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project.
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project. We currently only support private trackers.
|
||||
|
||||
|
||||
### Screenshots
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 5.5 KiB |
@@ -1,176 +0,0 @@
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class AnimeTorrents : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "ajax/torrents_data.php"; } }
|
||||
private string SearchUrlReferer { get { return SiteLink + "torrents.php?cat=0&searchin=filename&search="; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public AnimeTorrents(IIndexerManagerService i, HttpWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "AnimeTorrents",
|
||||
description: "Definitive source for anime and manga",
|
||||
link: "http://animetorrents.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: c, // Forced HTTP client for custom headers
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD); // Anime Movie
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesHD); // Anime Movie HD
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // Anime Series
|
||||
AddCategoryMapping(7, TorznabCatType.TVAnime); // Anime Series HD
|
||||
AddCategoryMapping(5, TorznabCatType.XXXDVD); // Hentai (censored)
|
||||
AddCategoryMapping(9, TorznabCatType.XXXDVD); // Hentai (censored) HD
|
||||
AddCategoryMapping(4, TorznabCatType.XXXDVD); // Hentai (un-censored)
|
||||
AddCategoryMapping(8, TorznabCatType.XXXDVD); // Hentai (un-censored) HD
|
||||
AddCategoryMapping(13, TorznabCatType.BooksForeign); // Light Novel
|
||||
AddCategoryMapping(3, TorznabCatType.BooksComics); // Manga
|
||||
AddCategoryMapping(10, TorznabCatType.BooksComics); // Manga 18+
|
||||
AddCategoryMapping(11, TorznabCatType.TVAnime); // OVA
|
||||
AddCategoryMapping(12, TorznabCatType.TVAnime); // OVA HD
|
||||
AddCategoryMapping(14, TorznabCatType.BooksComics); // Doujin Anime
|
||||
AddCategoryMapping(15, TorznabCatType.XXXDVD); // Doujin Anime 18+
|
||||
AddCategoryMapping(16, TorznabCatType.AudioForeign); // Doujin Music
|
||||
AddCategoryMapping(17, TorznabCatType.BooksComics); // Doujinshi
|
||||
AddCategoryMapping(18, TorznabCatType.BooksComics); // Doujinshi 18+
|
||||
AddCategoryMapping(19, TorznabCatType.Audio); // OST
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "form", "login" },
|
||||
{ "rememberme[]", "1" }
|
||||
};
|
||||
|
||||
var loginPage = await RequestStringWithCookiesAndRetry(LoginUrl, null, null);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SearchUrl, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom[".ui-state-error"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
queryCollection.Add("total", "146"); // Not sure what this is about but its required!
|
||||
|
||||
var cat = "0";
|
||||
var queryCats = MapTorznabCapsToTrackers(query);
|
||||
if (queryCats.Count == 1)
|
||||
{
|
||||
cat = queryCats.First().ToString();
|
||||
}
|
||||
|
||||
queryCollection.Add("cat", cat);
|
||||
queryCollection.Add("searchin", "filename");
|
||||
queryCollection.Add("search", searchString);
|
||||
queryCollection.Add("page", "1");
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var extraHeaders = new Dictionary<string, string>()
|
||||
{
|
||||
{ "X-Requested-With", "XMLHttpRequest" }
|
||||
};
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, SearchUrlReferer, extraHeaders);
|
||||
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
var rows = dom["tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("td:eq(1) a:eq(0)").First();
|
||||
release.Title = qTitleLink.Find("strong").Text().Trim();
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
if (string.IsNullOrWhiteSpace(release.Title))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
var dateString = qRow.Find("td:eq(4)").Text();
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
|
||||
var qLink = qRow.Find("td:eq(2) a");
|
||||
release.Link = new Uri(qLink.Attr("href"));
|
||||
|
||||
var sizeStr = qRow.Find("td:eq(5)").Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
var connections = qRow.Find("td:eq(7)").Text().Trim().Split("/".ToCharArray(),StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(connections[0].Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(connections[1].Trim()) + release.Seeders;
|
||||
|
||||
var rCat = row.Cq().Find("td:eq(0) a").First().Attr("href");
|
||||
var rCatIdx = rCat.IndexOf("cat=");
|
||||
if (rCatIdx > -1)
|
||||
{
|
||||
rCat = rCat.Substring(rCatIdx + 4);
|
||||
}
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(rCat);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,196 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BakaBT : BaseIndexer, IIndexer
|
||||
{
|
||||
public string SearchUrl { get { return SiteLink + "browse.php?only=0&hentai=1&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&c1=1&reorder=1&q="; } }
|
||||
public string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BakaBT",
|
||||
description: "Anime Community",
|
||||
link: "http://bakabt.me/",
|
||||
caps: new TorznabCapabilities(TorznabCatType.TVAnime),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var loginForm = await webclient.GetString(new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl,
|
||||
Type = RequestType.GET
|
||||
});
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnto", "/index.php" }
|
||||
};
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginForm.Cookies, true, null, SiteLink);
|
||||
var responseContent = response.Content;
|
||||
await ConfigureIfOK(response.Cookies, responseContent.Contains("<a href=\"logout.php\">Logout</a>"), () =>
|
||||
{
|
||||
CQ dom = responseContent;
|
||||
var messageEl = dom[".error"].First();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
|
||||
// This tracker only deals with full seasons so chop off the episode/season number if we have it D:
|
||||
if (!string.IsNullOrWhiteSpace(query.SearchTerm))
|
||||
{
|
||||
var splitindex = query.SearchTerm.LastIndexOf(' ');
|
||||
if (splitindex > -1)
|
||||
query.SearchTerm = query.SearchTerm.Substring(0, splitindex);
|
||||
}
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.SanitizedSearchTerm;
|
||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom[".torrents tr.torrent"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("a.title").First();
|
||||
var title = qTitleLink.Text().Trim();
|
||||
|
||||
// Insert before the release info
|
||||
var taidx = title.IndexOf('(');
|
||||
var tbidx = title.IndexOf('[');
|
||||
|
||||
if (taidx == -1)
|
||||
taidx = title.Length;
|
||||
|
||||
if (tbidx == -1)
|
||||
tbidx = title.Length;
|
||||
var titleSplit = Math.Min(taidx, tbidx);
|
||||
var titleSeries = title.Substring(0, titleSplit);
|
||||
var releaseInfo = title.Substring(titleSplit);
|
||||
|
||||
// For each over each pipe deliminated name
|
||||
foreach (var name in titleSeries.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = (name + releaseInfo).Trim();
|
||||
// Ensure the season is defined as this tracker only deals with full seasons
|
||||
if (release.Title.IndexOf("Season") == -1)
|
||||
{
|
||||
// Insert before the release info
|
||||
var aidx = release.Title.IndexOf('(');
|
||||
var bidx = release.Title.IndexOf('[');
|
||||
|
||||
if (aidx == -1)
|
||||
aidx = release.Title.Length;
|
||||
|
||||
if (bidx == -1)
|
||||
bidx = release.Title.Length;
|
||||
|
||||
var insertPoint = Math.Min(aidx, bidx);
|
||||
release.Title = release.Title.Substring(0, insertPoint) + "Season 1 " + release.Title.Substring(insertPoint);
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
release.Link = new Uri(SiteLink + qRow.Find(".peers a").First().Attr("href"));
|
||||
|
||||
release.Seeders = int.Parse(qRow.Find(".peers a").Get(0).InnerText);
|
||||
release.Peers = release.Seeders + int.Parse(qRow.Find(".peers a").Get(1).InnerText);
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
|
||||
var size = qRow.Find(".size").First().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(size);
|
||||
|
||||
//22 Jul 15
|
||||
var dateStr = qRow.Find(".added").First().Text().Replace("'", string.Empty);
|
||||
if (dateStr.Split(' ')[0].Length == 1)
|
||||
dateStr = "0" + dateStr;
|
||||
|
||||
if (string.Equals(dateStr, "yesterday", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now.AddDays(-1);
|
||||
}
|
||||
else if (string.Equals(dateStr, "today", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(dateStr, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var downloadPage = await RequestStringWithCookies(link.ToString());
|
||||
CQ dom = downloadPage.Content;
|
||||
var downloadLink = dom.Find(".download_link").First().Attr("href");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(downloadLink))
|
||||
{
|
||||
throw new Exception("Unable to find download link.");
|
||||
}
|
||||
|
||||
var response = await RequestBytesWithCookies(SiteLink + downloadLink);
|
||||
return response.Content;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,233 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Jackett.Utils;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils.Clients;
|
||||
using System.Text.RegularExpressions;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class RuTor : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "search/0/{0}/000/0/{1}"; } }
|
||||
private string BrowseUrl { get { return SiteLink + "browse/0/{0}/0/0"; } }
|
||||
readonly static string defaultSiteLink = "http://rutor.org/";
|
||||
|
||||
new ConfigurationDataRuTor configData
|
||||
{
|
||||
get { return (ConfigurationDataRuTor)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public RuTor(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "RUTor",
|
||||
description: "Свободный торрент трекер",
|
||||
link: "http://rutor.org/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRuTor(defaultSiteLink))
|
||||
{
|
||||
TorznabCaps.Categories.Add(TorznabCatType.TVAnime);
|
||||
TorznabCaps.Categories.Add(TorznabCatType.Movies);
|
||||
TorznabCaps.Categories.Add(TorznabCatType.Audio);
|
||||
TorznabCaps.Categories.Add(TorznabCatType.Books);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var oldConfig = configData;
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
configData = oldConfig;
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
|
||||
protected override void SaveConfig()
|
||||
{
|
||||
indexerService.SaveConfig(this as IIndexer, JsonConvert.SerializeObject(configData));
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
var json = jsonConfig.ToString();
|
||||
configData = JsonConvert.DeserializeObject<ConfigurationDataRuTor>(json);
|
||||
IsConfigured = true;
|
||||
}
|
||||
|
||||
private readonly int CAT_ANY = 0;
|
||||
private readonly int CAT_FOREIGN_MOVIE = 1;
|
||||
// private readonly int CAT_OUR_MOVIES = 5;
|
||||
// private readonly int CAT_POP_SCIFI_MOVIES = 12;
|
||||
private readonly int CAT_TV_SERIES = 4;
|
||||
// private readonly int CAT_TV = 6;
|
||||
// private readonly int CAT_ANIMATION = 7;
|
||||
private readonly int CAT_ANIME = 10;
|
||||
private readonly int CAT_MUSIC = 2;
|
||||
// private readonly int CAT_GAMES = 8;
|
||||
// private readonly int CAT_SOFTWARE = 9;
|
||||
// private readonly int CAT_SPORTS_HEALTH = 13;
|
||||
// private readonly int CAT_HUMOR = 15;
|
||||
// private readonly int CAT_ECONOMY_LIFE = 14;
|
||||
private readonly int CAT_BOOKS = 11;
|
||||
// private readonly int CAT_OTHER = 3;
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchCategory = CAT_ANY;
|
||||
|
||||
if (query.Categories.Contains(TorznabCatType.TV.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.TVHD.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.TVSD.ID))
|
||||
{
|
||||
searchCategory = CAT_TV_SERIES;
|
||||
}
|
||||
|
||||
if ((searchCategory == CAT_ANY) &&
|
||||
(query.Categories.Contains(TorznabCatType.Movies.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.MoviesForeign.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.MoviesHD.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.MoviesSD.ID)))
|
||||
{
|
||||
searchCategory = CAT_FOREIGN_MOVIE;
|
||||
}
|
||||
|
||||
if ((searchCategory == CAT_ANY) &&
|
||||
(query.Categories.Contains(TorznabCatType.TVAnime.ID)))
|
||||
{
|
||||
searchCategory = CAT_ANIME;
|
||||
}
|
||||
|
||||
if ((searchCategory == CAT_ANY) &&
|
||||
(query.Categories.Contains(TorznabCatType.Books.ID)))
|
||||
{
|
||||
searchCategory = CAT_BOOKS;
|
||||
}
|
||||
|
||||
if ((searchCategory == CAT_ANY) &&
|
||||
(query.Categories.Contains(TorznabCatType.Audio.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.AudioLossless.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.AudioMP3.ID)))
|
||||
{
|
||||
searchCategory = CAT_MUSIC;
|
||||
}
|
||||
|
||||
string queryUrl = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryUrl = string.Format(BrowseUrl, searchCategory);
|
||||
}
|
||||
else
|
||||
{
|
||||
queryUrl = string.Format(SearchUrl, searchCategory, HttpUtility.UrlEncode(searchString.Trim()));
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(queryUrl, string.Empty);
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom["#index table tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
var date = StringUtil.StripNonAlphaNumeric(row.Cq().Find("td:eq(0)").Text().Trim()
|
||||
.Replace("Янв", "01")
|
||||
.Replace("Фев", "02")
|
||||
.Replace("Мар", "03")
|
||||
.Replace("Апр", "04")
|
||||
.Replace("Май", "05")
|
||||
.Replace("Июн", "06")
|
||||
.Replace("Июл", "07")
|
||||
.Replace("Авг", "08")
|
||||
.Replace("Сен", "09")
|
||||
.Replace("Окт", "10")
|
||||
.Replace("Ноя", "11")
|
||||
.Replace("Дек", "12"));
|
||||
|
||||
release.PublishDate = DateTime.ParseExact(date, "ddMMyy", CultureInfo.InvariantCulture);
|
||||
|
||||
var hasTorrent = row.Cq().Find("td:eq(1) a").Length == 3;
|
||||
var titleIndex = 1;
|
||||
if (hasTorrent)
|
||||
titleIndex++;
|
||||
|
||||
release.Title = row.Cq().Find("td:eq(1) a:eq(" + titleIndex + ")").Text().Trim();
|
||||
if (configData.StripRussian.Value)
|
||||
{
|
||||
var split = release.Title.IndexOf('/');
|
||||
if (split > -1)
|
||||
{
|
||||
release.Title = release.Title.Substring(split + 1).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
|
||||
var hasComments = row.Cq().Find("td:eq(2) img").Length > 0;
|
||||
var sizeCol = 2;
|
||||
|
||||
if (hasComments)
|
||||
sizeCol++;
|
||||
|
||||
var sizeStr = StringUtil.StripRegex(row.Cq().Find("td:eq(" + sizeCol + ")").Text(), "[^a-zA-Z0-9\\. -]", " ").Trim();
|
||||
string[] sizeSplit = sizeStr.Split(' ');
|
||||
release.Size = ReleaseInfo.GetBytes(sizeSplit[1].ToLower(), ParseUtil.CoerceFloat(sizeSplit[0]));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Cq().Find(".green").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.Cq().Find(".red").Text().Trim()) + release.Seeders;
|
||||
|
||||
release.Guid = new Uri(configData.Url.Value + row.Cq().Find("td:eq(1) a:eq(" + titleIndex + ")").Attr("href").Substring(1));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
if (hasTorrent)
|
||||
{
|
||||
release.Link = new Uri(row.Cq().Find("td:eq(1) a:eq(0)").Attr("href"));
|
||||
release.MagnetUri = new Uri(row.Cq().Find("td:eq(1) a:eq(1)").Attr("href"));
|
||||
}
|
||||
else
|
||||
{
|
||||
release.MagnetUri = new Uri(row.Cq().Find("td:eq(1) a:eq(0)").Attr("href"));
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,187 +0,0 @@
|
||||
using Jackett.Models;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Rarbg : BaseIndexer, IIndexer
|
||||
{
|
||||
readonly static string defaultSiteLink = "https://torrentapi.org/";
|
||||
|
||||
private Uri BaseUri
|
||||
{
|
||||
get { return new Uri(configData.Url.Value); }
|
||||
set { configData.Url.Value = value.ToString(); }
|
||||
}
|
||||
|
||||
private string ApiEndpoint { get { return BaseUri + "pubapi_v2.php"; } }
|
||||
private string TokenUrl { get { return ApiEndpoint + "?get_token=get_token"; } }
|
||||
private string SearchUrl { get { return ApiEndpoint + "?app_id=jackett_v{0}&mode={1}&format=json_extended&search_string={2}&token={3}"; } }
|
||||
|
||||
|
||||
new ConfigurationDataUrl configData
|
||||
{
|
||||
get { return (ConfigurationDataUrl)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
private DateTime lastTokenFetch;
|
||||
private string token;
|
||||
|
||||
readonly TimeSpan TOKEN_DURATION = TimeSpan.FromMinutes(10);
|
||||
|
||||
private bool HasValidToken { get { return !string.IsNullOrEmpty(token) && lastTokenFetch > DateTime.Now - TOKEN_DURATION; } }
|
||||
|
||||
Dictionary<string, int> categoryLabels;
|
||||
|
||||
public Rarbg(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "RARBG",
|
||||
description: "RARBG",
|
||||
link: defaultSiteLink,
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
categoryLabels = new Dictionary<string, int>();
|
||||
|
||||
AddCat(4, TorznabCatType.XXX, "XXX (18+)");
|
||||
AddCat(14, TorznabCatType.MoviesSD, "Movies/XVID");
|
||||
AddCat(48, TorznabCatType.MoviesHD, "Movies/XVID/720");
|
||||
AddCat(17, TorznabCatType.MoviesSD, "Movies/x264");
|
||||
AddCat(44, TorznabCatType.MoviesHD, "Movies/x264/1080");
|
||||
AddCat(45, TorznabCatType.MoviesHD, "Movies/x264/720");
|
||||
AddCat(47, TorznabCatType.Movies3D, "Movies/x264/3D");
|
||||
AddCat(42, TorznabCatType.MoviesBluRay, "Movies/Full BD");
|
||||
AddCat(46, TorznabCatType.MoviesBluRay, "Movies/BD Remux");
|
||||
AddCat(18, TorznabCatType.TVSD, "TV Episodes");
|
||||
AddCat(41, TorznabCatType.TVHD, "TV HD Episodes");
|
||||
AddCat(23, TorznabCatType.AudioMP3, "Music/MP3");
|
||||
AddCat(25, TorznabCatType.AudioLossless, "Music/FLAC");
|
||||
AddCat(27, TorznabCatType.PCGames, "Games/PC ISO");
|
||||
AddCat(28, TorznabCatType.PCGames, "Games/PC RIP");
|
||||
AddCat(40, TorznabCatType.ConsolePS3, "Games/PS3");
|
||||
AddCat(32, TorznabCatType.ConsoleXbox360, "Games/XBOX-360");
|
||||
AddCat(33, TorznabCatType.PCISO, "Software/PC ISO");
|
||||
AddCat(35, TorznabCatType.BooksEbook, "e-Books");
|
||||
}
|
||||
|
||||
void AddCat(int cat, TorznabCategory catType, string label)
|
||||
{
|
||||
AddCategoryMapping(cat, catType);
|
||||
categoryLabels.Add(label, cat);
|
||||
}
|
||||
|
||||
async Task CheckToken()
|
||||
{
|
||||
if (!HasValidToken)
|
||||
{
|
||||
var result = await RequestStringWithCookiesAndRetry(TokenUrl);
|
||||
var json = JObject.Parse(result.Content);
|
||||
token = json.Value<string>("token");
|
||||
lastTokenFetch = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
return PerformQuery(query, 0);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query, int attempts = 0)
|
||||
{
|
||||
await CheckToken();
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var queryStr = HttpUtility.UrlEncode(query.GetQueryString());
|
||||
|
||||
var mode = string.IsNullOrEmpty(queryStr) ? "list" : "search";
|
||||
var episodeSearchUrl = string.Format(SearchUrl, Engine.ConfigService.GetVersion(), mode, queryStr, token);
|
||||
var cats = string.Join(";", MapTorznabCapsToTrackers(query));
|
||||
if (!string.IsNullOrEmpty(cats))
|
||||
{
|
||||
episodeSearchUrl += "&category=" + cats;
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl, string.Empty);
|
||||
|
||||
try
|
||||
{
|
||||
var jsonContent = JObject.Parse(response.Content);
|
||||
|
||||
int errorCode = jsonContent.Value<int>("error_code");
|
||||
if (errorCode == 20) // no results found
|
||||
{
|
||||
return releases.ToArray();
|
||||
}
|
||||
|
||||
if (errorCode > 0) // too many requests per second
|
||||
{
|
||||
if (attempts < 3)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2));
|
||||
return await PerformQuery(query, ++attempts);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(jsonContent.Value<string>("error"));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in jsonContent.Value<JArray>("torrent_results"))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.Title = item.Value<string>("title");
|
||||
release.Description = release.Title;
|
||||
release.Category = MapTrackerCatToNewznab(categoryLabels[item.Value<string>("category")].ToString());
|
||||
|
||||
release.MagnetUri = new Uri(item.Value<string>("download"));
|
||||
release.InfoHash = release.MagnetUri.ToString().Split(':')[3].Split('&')[0];
|
||||
|
||||
release.Comments = new Uri(item.Value<string>("info_page"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
// ex: 2015-08-16 21:25:08 +0000
|
||||
var dateStr = item.Value<string>("pubdate").Replace(" +0000", "");
|
||||
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime();
|
||||
|
||||
release.Seeders = item.Value<int>("seeders");
|
||||
release.Peers = item.Value<int>("leechers") + release.Seeders;
|
||||
release.Size = item.Value<long>("size");
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,132 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Xml;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class ShowRSS : BaseIndexer, IIndexer
|
||||
{
|
||||
readonly static string defaultSiteLink = "http://showrss.info/";
|
||||
|
||||
private Uri BaseUri
|
||||
{
|
||||
get { return new Uri(configData.Url.Value); }
|
||||
set { configData.Url.Value = value.ToString(); }
|
||||
}
|
||||
|
||||
private string SearchAllUrl { get { return BaseUri + "feeds/all.rss"; } }
|
||||
|
||||
new ConfigurationDataUrl configData
|
||||
{
|
||||
get { return (ConfigurationDataUrl)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public ShowRSS(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "ShowRSS",
|
||||
description: "showRSS is a service that allows you to keep track of your favorite TV shows",
|
||||
link: defaultSiteLink,
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
if (jsonConfig is JObject)
|
||||
{
|
||||
BaseUri = new Uri(jsonConfig.Value<string>("base_url"));
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
public override Task<byte[]> Download(Uri link)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var episodeSearchUrl = string.Format(SearchAllUrl);
|
||||
var result = await RequestStringWithCookiesAndRetry(episodeSearchUrl, string.Empty);
|
||||
var xmlDoc = new XmlDocument();
|
||||
|
||||
try
|
||||
{
|
||||
xmlDoc.LoadXml(result.Content);
|
||||
ReleaseInfo release;
|
||||
string serie_title;
|
||||
|
||||
foreach (XmlNode node in xmlDoc.GetElementsByTagName("item"))
|
||||
{
|
||||
release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
serie_title = node.SelectSingleNode(".//*[local-name()='rawtitle']").InnerText;
|
||||
release.Title = serie_title;
|
||||
|
||||
release.Comments = new Uri(node.SelectSingleNode("link").InnerText);
|
||||
int category = 0;
|
||||
int.TryParse(node.SelectSingleNode("title").InnerText, out category);
|
||||
release.Category = category;
|
||||
var test = node.SelectSingleNode("enclosure");
|
||||
release.Guid = new Uri(test.Attributes["url"].Value);
|
||||
release.PublishDate = DateTime.Parse(node.SelectSingleNode("pubDate").InnerText, CultureInfo.InvariantCulture);
|
||||
|
||||
release.Description = node.SelectSingleNode("description").InnerText;
|
||||
release.InfoHash = node.SelectSingleNode("description").InnerText;
|
||||
release.Size = 0;
|
||||
release.Seeders = 1;
|
||||
release.Peers = 1;
|
||||
release.MagnetUri = new Uri(node.SelectSingleNode("link").InnerText);
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(result.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,173 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Strike : BaseIndexer, IIndexer
|
||||
{
|
||||
readonly static string defaultSiteLink = "https://getstrike.net/";
|
||||
|
||||
private Uri BaseUri
|
||||
{
|
||||
get { return new Uri(configData.Url.Value); }
|
||||
set { configData.Url.Value = value.ToString(); }
|
||||
}
|
||||
|
||||
private string SearchUrl { get { return BaseUri + "api/v2/torrents/search/?phrase={0}"; } }
|
||||
private string DownloadUrl { get { return BaseUri + "torrents/api/download/{0}.torrent"; } }
|
||||
|
||||
new ConfigurationDataStrike configData
|
||||
{
|
||||
get { return (ConfigurationDataStrike)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
|
||||
public Strike(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Strike",
|
||||
description: "Torrent search engine",
|
||||
link: defaultSiteLink,
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataStrike(defaultSiteLink))
|
||||
{
|
||||
AddCategoryMapping("Anime", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("Applications", TorznabCatType.PC);
|
||||
AddCategoryMapping("Books", TorznabCatType.Books);
|
||||
AddCategoryMapping("Games", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("Movies", TorznabCatType.Movies);
|
||||
AddCategoryMapping("TV", TorznabCatType.TV);
|
||||
AddCategoryMapping("XXX", TorznabCatType.XXX);
|
||||
AddCategoryMapping("Music", TorznabCatType.Audio);
|
||||
|
||||
/*AddCategoryMapping("Movies:Highres Movies", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("Movies:3D Movies", TorznabCatType.Movies3D);
|
||||
AddCategoryMapping("Books:Ebooks", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("Books:Comics", TorznabCatType.BooksComics);
|
||||
AddCategoryMapping("Books:Audio Books", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("Games:XBOX360", TorznabCatType.ConsoleXbox360);
|
||||
AddCategoryMapping("Games:Wii", TorznabCatType.ConsoleWii);
|
||||
AddCategoryMapping("Games:PSP", TorznabCatType.ConsolePSP);
|
||||
AddCategoryMapping("Games:PS3", TorznabCatType.ConsolePS3);
|
||||
AddCategoryMapping("Games:PC", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("Games:Android", TorznabCatType.PCPhoneAndroid);
|
||||
AddCategoryMapping("Music:Mp3", TorznabCatType.AudioMP3);*/
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
if (jsonConfig is JObject)
|
||||
{
|
||||
BaseUri = new Uri(jsonConfig.Value<string>("base_url"));
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
var queryString = query.GetQueryString();
|
||||
var searchTerm = string.IsNullOrEmpty(queryString) ? DateTime.Now.Year.ToString() : queryString;
|
||||
var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchTerm));
|
||||
|
||||
var trackerCategories = MapTorznabCapsToTrackers(query, mapChildrenCatsToParent: true);
|
||||
|
||||
// This tracker can only search one cat at a time, otherwise search all and filter results
|
||||
if (trackerCategories.Count == 1)
|
||||
{
|
||||
episodeSearchUrl += "&category=" + trackerCategories[0];
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl, string.Empty);
|
||||
try
|
||||
{
|
||||
var jResults = JObject.Parse(results.Content);
|
||||
foreach (JObject result in (JArray)jResults["torrents"])
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
if (trackerCategories.Count > 0 && !trackerCategories.Contains((string)result["torrent_category"]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
release.Category = MapTrackerCatToNewznab((string)result["torrent_category"]);
|
||||
|
||||
release.Title = (string)result["torrent_title"];
|
||||
release.Description = release.Title;
|
||||
release.Seeders = (int)result["seeds"];
|
||||
release.Peers = (int)result["leeches"] + release.Seeders;
|
||||
release.Size = (long)result["size"];
|
||||
|
||||
// "Apr 2, 2015", "Apr 12, 2015" (note the spacing)
|
||||
// some are unix timestamps, some are not.. :/
|
||||
var dateString = string.Join(" ", ((string)result["upload_date"]).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
float dateVal;
|
||||
if (ParseUtil.TryCoerceFloat(dateString, out dateVal))
|
||||
release.PublishDate = DateTimeUtil.UnixTimestampToDateTime(dateVal);
|
||||
else
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "MMM d, yyyy", CultureInfo.InvariantCulture);
|
||||
|
||||
release.Guid = new Uri((string)result["page"]);
|
||||
release.Comments = release.Guid;
|
||||
|
||||
release.InfoHash = (string)result["torrent_hash"];
|
||||
release.MagnetUri = new Uri((string)result["magnet_uri"]);
|
||||
release.Link = new Uri(string.Format(DownloadUrl, release.InfoHash));
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override Task<byte[]> Download(Uri link)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,249 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class T411 : BaseIndexer, IIndexer
|
||||
{
|
||||
private readonly string CommentsUrl = "";
|
||||
const string ApiUrl = "http://api.t411.in";
|
||||
const string AuthUrl = ApiUrl + "/auth";
|
||||
const string SearchUrl = ApiUrl + "/torrents/search/{0}";
|
||||
const string DownloadUrl = ApiUrl + "/torrents/download/{0}";
|
||||
|
||||
HttpClientHandler handler;
|
||||
HttpClient client;
|
||||
|
||||
new ConfigurationDataLoginTokin configData
|
||||
{
|
||||
get { return (ConfigurationDataLoginTokin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public T411(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "T411",
|
||||
description: "French Torrent Tracker",
|
||||
link: "http://www.t411.in/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataLoginTokin())
|
||||
{
|
||||
CommentsUrl = SiteLink + "/torrents/{0}";
|
||||
IsConfigured = false;
|
||||
handler = new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = true
|
||||
};
|
||||
client = new HttpClient(handler);
|
||||
|
||||
|
||||
|
||||
AddCategoryMapping("Film\\/Vidéo", TorznabCatType.Movies);
|
||||
AddCategoryMapping("Vidéo-clips", TorznabCatType.Other);
|
||||
AddCategoryMapping("Série TV", TorznabCatType.TV);
|
||||
AddCategoryMapping("Animation", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("Film", TorznabCatType.Movies);
|
||||
AddCategoryMapping("Concert", TorznabCatType.AudioVideo);
|
||||
AddCategoryMapping("Documentaire", TorznabCatType.Audio);
|
||||
AddCategoryMapping("Spectacle", TorznabCatType.TV);
|
||||
AddCategoryMapping("Sport", TorznabCatType.TVSport);
|
||||
AddCategoryMapping("Animation Série", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("Emission TV", TorznabCatType.TV);
|
||||
|
||||
|
||||
AddCategoryMapping("Application", TorznabCatType.PC0day);
|
||||
AddCategoryMapping("Linux", TorznabCatType.PC);
|
||||
AddCategoryMapping("MacOS", TorznabCatType.PCMac);
|
||||
AddCategoryMapping("Windows", TorznabCatType.PC);
|
||||
AddCategoryMapping("Smartphone", TorznabCatType.PCPhoneOther);
|
||||
AddCategoryMapping("Tablette", TorznabCatType.PCPhoneOther);
|
||||
AddCategoryMapping("Autre", TorznabCatType.PC);
|
||||
AddCategoryMapping("Formation", TorznabCatType.PC);
|
||||
|
||||
AddCategoryMapping("Emulation", TorznabCatType.PC);
|
||||
AddCategoryMapping("Emulateurs", TorznabCatType.PC);
|
||||
AddCategoryMapping("Roms", TorznabCatType.PC);
|
||||
|
||||
AddCategoryMapping("GPS", TorznabCatType.Other);
|
||||
AddCategoryMapping("Applications", TorznabCatType.Other);
|
||||
AddCategoryMapping("Cartes", TorznabCatType.Other);
|
||||
AddCategoryMapping("Divers", TorznabCatType.Other);
|
||||
|
||||
AddCategoryMapping("Audio", TorznabCatType.Audio);
|
||||
AddCategoryMapping("Karaoke", TorznabCatType.Audio);
|
||||
AddCategoryMapping("Samples", TorznabCatType.Audio);
|
||||
AddCategoryMapping("Musique", TorznabCatType.Audio);
|
||||
AddCategoryMapping("Podcast Radio", TorznabCatType.Audio);
|
||||
|
||||
AddCategoryMapping("eBook", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("Audio", TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping("Bds", TorznabCatType.AudioVideo);
|
||||
AddCategoryMapping("Comics", TorznabCatType.BooksComics);
|
||||
AddCategoryMapping("Livres", TorznabCatType.Books);
|
||||
AddCategoryMapping("Mangas", TorznabCatType.BooksForeign);
|
||||
AddCategoryMapping("Presse", TorznabCatType.BooksMagazines);
|
||||
|
||||
AddCategoryMapping("xXx", TorznabCatType.XXX);
|
||||
AddCategoryMapping("eBooks", TorznabCatType.XXXImageset);
|
||||
AddCategoryMapping("Jeux vidéo", TorznabCatType.XXX);
|
||||
AddCategoryMapping("Video", TorznabCatType.XXXDVD);
|
||||
//AddCategoryMapping("Animation", TorznabCatType.XXX); Used above :/
|
||||
|
||||
AddCategoryMapping("Jeu vidéo", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("Linux", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("MacOS", TorznabCatType.PCGames);
|
||||
// AddCategoryMapping("Windows", TorznabCatType.PCGames); Used above :/
|
||||
AddCategoryMapping("Nintendo", TorznabCatType.Console);
|
||||
AddCategoryMapping("Sony", TorznabCatType.Console);
|
||||
AddCategoryMapping("Microsoft", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("Smartphone", TorznabCatType.PCPhoneOther);
|
||||
AddCategoryMapping("Tablette", TorznabCatType.PCPhoneOther);
|
||||
AddCategoryMapping("Autre", TorznabCatType.Other);
|
||||
|
||||
AddCategoryMapping("Jeux vidéo", TorznabCatType.Other);
|
||||
}
|
||||
|
||||
async Task<string> GetAuthToken(bool forceFetch = false)
|
||||
{
|
||||
if (!forceFetch && configData.LastTokenFetchDateTime > DateTime.Now - TimeSpan.FromHours(48))
|
||||
{
|
||||
return configData.ApiToken.Value;
|
||||
}
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var content = new FormUrlEncodedContent(pairs);
|
||||
|
||||
var response = await client.PostAsync(AuthUrl, content);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonResponse = JObject.Parse(responseContent);
|
||||
if (jsonResponse["error"] != null)
|
||||
{
|
||||
throw new ApplicationException((string)jsonResponse["error"]);
|
||||
}
|
||||
configData.ApiToken.Value = (string)jsonResponse["token"];
|
||||
configData.LastTokenFetchDateTime = DateTime.Now;
|
||||
return configData.ApiToken.Value;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
Exception tokenFetchEx = null;
|
||||
try
|
||||
{
|
||||
await GetAuthToken(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tokenFetchEx = new ExceptionWithConfigData(ex.Message, configData);
|
||||
}
|
||||
|
||||
await ConfigureIfOK(string.Empty, tokenFetchEx == null, () =>
|
||||
{
|
||||
throw tokenFetchEx;
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
if (jsonConfig is JObject)
|
||||
{
|
||||
configData.ApiToken.Value = jsonConfig.Value<string>("token"); ;
|
||||
configData.Username.Value = jsonConfig.Value<string>("username");
|
||||
configData.Password.Value = jsonConfig.Value<string>("password");
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchTerm = string.IsNullOrEmpty(query.SanitizedSearchTerm) ? "%20" : query.SanitizedSearchTerm;
|
||||
var searchString = searchTerm + " " + query.GetEpisodeSearchString();
|
||||
var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchString));
|
||||
|
||||
var message = new HttpRequestMessage();
|
||||
message.Method = HttpMethod.Get;
|
||||
message.RequestUri = new Uri(episodeSearchUrl);
|
||||
message.Headers.TryAddWithoutValidation("Authorization", await GetAuthToken());
|
||||
|
||||
var response = await client.SendAsync(message);
|
||||
var results = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var jsonResult = JObject.Parse(results);
|
||||
try
|
||||
{
|
||||
var items = (JArray)jsonResult["torrents"];
|
||||
foreach (var item in items)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
var torrentId = (string)item["id"];
|
||||
release.Link = new Uri(string.Format(DownloadUrl, torrentId));
|
||||
release.Title = (string)item["name"];
|
||||
release.Description = release.Title;
|
||||
release.Comments = new Uri(string.Format(CommentsUrl, (string)item["rewritename"]));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var dateUtc = DateTime.ParseExact((string)item["added"], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
release.PublishDate = DateTime.SpecifyKind(dateUtc, DateTimeKind.Utc).ToLocalTime();
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt((string)item["seeders"]);
|
||||
release.Peers = ParseUtil.CoerceInt((string)item["leechers"]) + release.Seeders;
|
||||
release.Size = ParseUtil.CoerceLong((string)item["size"]);
|
||||
release.Category = MapTrackerCatToNewznab((string)item["categoryname"]);
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var message = new HttpRequestMessage();
|
||||
message.Method = HttpMethod.Get;
|
||||
message.RequestUri = link;
|
||||
message.Headers.TryAddWithoutValidation("Authorization", await GetAuthToken());
|
||||
|
||||
var response = await client.SendAsync(message);
|
||||
return await response.Content.ReadAsByteArrayAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,161 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class ThePirateBay : BaseIndexer, IIndexer
|
||||
{
|
||||
readonly static string defaultSiteLink = "https://thepiratebay.mn/";
|
||||
|
||||
private Uri BaseUri
|
||||
{
|
||||
get { return new Uri(configData.Url.Value); }
|
||||
set { configData.Url.Value = value.ToString(); }
|
||||
}
|
||||
|
||||
private string SearchUrl { get { return BaseUri + "search/{0}/0/99/208,205"; } }
|
||||
private string RecentUrl { get { return BaseUri + "recent"; } }
|
||||
|
||||
new ConfigurationDataUrl configData
|
||||
{
|
||||
get { return (ConfigurationDataUrl)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public ThePirateBay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "The Pirate Bay",
|
||||
description: "The worlds largest bittorrent indexer",
|
||||
link: defaultSiteLink,
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
if (jsonConfig is JObject)
|
||||
{
|
||||
BaseUri = new Uri(jsonConfig.Value<string>("base_url"));
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var queryStr = HttpUtility.UrlEncode(query.GetQueryString());
|
||||
var episodeSearchUrl = string.IsNullOrWhiteSpace(queryStr) ? RecentUrl : string.Format(SearchUrl, queryStr);
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl, string.Empty);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
|
||||
var rows = dom["#searchResult > tbody > tr"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (row.ChildElements.Count() < 2)
|
||||
continue;
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
CQ qRow = row.Cq();
|
||||
CQ qLink = qRow.Find(".detName > .detLink").First();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Title = qLink.Text().Trim();
|
||||
release.Description = release.Title;
|
||||
release.Comments = new Uri(BaseUri + qLink.Attr("href").TrimStart('/'));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var downloadCol = row.ChildElements.ElementAt(1).Cq().Children("a");
|
||||
release.MagnetUri = new Uri(downloadCol.Attr("href"));
|
||||
release.InfoHash = release.MagnetUri.ToString().Split(':')[3].Split('&')[0];
|
||||
|
||||
var descString = qRow.Find(".detDesc").Text().Trim();
|
||||
var descParts = descString.Split(',');
|
||||
|
||||
var timeString = descParts[0].Split(' ')[1];
|
||||
|
||||
if (timeString.Contains(" ago"))
|
||||
{
|
||||
release.PublishDate = (DateTime.Now - TimeSpan.FromMinutes(ParseUtil.CoerceInt(timeString.Split(' ')[0])));
|
||||
}
|
||||
else if (timeString.Contains("Today"))
|
||||
{
|
||||
release.PublishDate = (DateTime.UtcNow - TimeSpan.FromHours(2) - TimeSpan.Parse(timeString.Split(' ')[1])).ToLocalTime();
|
||||
}
|
||||
else if (timeString.Contains("Y-day"))
|
||||
{
|
||||
release.PublishDate = (DateTime.UtcNow - TimeSpan.FromHours(26) - TimeSpan.Parse(timeString.Split(' ')[1])).ToLocalTime();
|
||||
}
|
||||
else if (timeString.Contains(':'))
|
||||
{
|
||||
var utc = DateTime.ParseExact(timeString, "MM-dd HH:mm", CultureInfo.InvariantCulture) - TimeSpan.FromHours(2);
|
||||
release.PublishDate = DateTime.SpecifyKind(utc, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
var utc = DateTime.ParseExact(timeString, "MM-dd yyyy", CultureInfo.InvariantCulture) - TimeSpan.FromHours(2);
|
||||
release.PublishDate = DateTime.SpecifyKind(utc, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
|
||||
release.Size = ReleaseInfo.GetBytes(descParts[1]);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(2).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(3).Cq().Text()) + release.Seeders;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
return releases.ToArray();
|
||||
}
|
||||
|
||||
public override Task<byte[]> Download(Uri link)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,212 +0,0 @@
|
||||
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.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml;
|
||||
using System.Linq;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Torrentz : BaseIndexer, IIndexer
|
||||
{
|
||||
readonly static string defaultSiteLink = "https://torrentz.eu/";
|
||||
|
||||
private Uri BaseUri
|
||||
{
|
||||
get { return new Uri(configData.Url.Value); }
|
||||
set { configData.Url.Value = value.ToString(); }
|
||||
}
|
||||
|
||||
private string SearchUrl { get { return BaseUri + "feed_verifiedP?f={0}"; } }
|
||||
|
||||
new ConfigurationDataUrl configData
|
||||
{
|
||||
get { return (ConfigurationDataUrl)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
|
||||
public Torrentz(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Torrentz",
|
||||
description: "Torrentz is a meta-search engine and a Multisearch. This means we just search other search engines.",
|
||||
link: defaultSiteLink,
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
// Override to load legacy config format
|
||||
public override void LoadFromSavedConfiguration(JToken jsonConfig)
|
||||
{
|
||||
if (jsonConfig is JObject)
|
||||
{
|
||||
BaseUri = new Uri(jsonConfig.Value<string>("base_url"));
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchString.Trim()));
|
||||
var xmlDoc = new XmlDocument();
|
||||
string xml = string.Empty;
|
||||
var result = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
xmlDoc.LoadXml(result.Content);
|
||||
|
||||
ReleaseInfo release;
|
||||
TorrentzHelper td;
|
||||
string serie_title;
|
||||
|
||||
foreach (XmlNode node in xmlDoc.GetElementsByTagName("item"))
|
||||
{
|
||||
release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
serie_title = node.SelectSingleNode("title").InnerText;
|
||||
release.Title = serie_title;
|
||||
|
||||
release.Comments = new Uri(node.SelectSingleNode("link").InnerText);
|
||||
int category = 0;
|
||||
int.TryParse(node.SelectSingleNode("category").InnerText, out category);
|
||||
release.Category = category;
|
||||
release.Guid = new Uri(node.SelectSingleNode("guid").InnerText);
|
||||
release.PublishDate = DateTime.Parse(node.SelectSingleNode("pubDate").InnerText, CultureInfo.InvariantCulture);
|
||||
|
||||
td = new TorrentzHelper(node.SelectSingleNode("description").InnerText);
|
||||
release.Description = td.Description;
|
||||
release.InfoHash = td.hash;
|
||||
release.Size = td.Size;
|
||||
release.Seeders = td.Seeders;
|
||||
release.Peers = td.Peers + release.Seeders;
|
||||
release.MagnetUri = TorrentzHelper.createMagnetLink(td.hash, serie_title);
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(xml, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override Task<byte[]> Download(Uri link)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentzHelper
|
||||
{
|
||||
public TorrentzHelper(string description)
|
||||
{
|
||||
this.Description = description;
|
||||
if (null == description)
|
||||
{
|
||||
this.Description = "";
|
||||
this.Size = 0;
|
||||
this.Peers = 0;
|
||||
this.Seeders = 0;
|
||||
this.hash = "";
|
||||
}
|
||||
else
|
||||
FillProperties();
|
||||
}
|
||||
|
||||
public static Uri createMagnetLink(string hash, string title)
|
||||
{
|
||||
string MagnetLink = "magnet:?xt=urn:btih:{0}&dn={1}&tr={2}";
|
||||
string Trackers = WebUtility.UrlEncode("udp://tracker.publicbt.com:80&tr=udp://tracker.openbittorrent.com:80&tr=udp://tracker.ccc.de:80&tr=udp://tracker.istole.it:80");
|
||||
title = WebUtility.UrlEncode(title);
|
||||
|
||||
return new Uri(string.Format(MagnetLink, hash, title, Trackers));
|
||||
}
|
||||
|
||||
private void FillProperties()
|
||||
{
|
||||
string description = this.Description;
|
||||
int counter = 0;
|
||||
while (description.Contains(" "))
|
||||
{
|
||||
int nextSpace = description.IndexOf(": ") + 1;
|
||||
int secondSpace;
|
||||
if (counter != 0)
|
||||
secondSpace = description.IndexOf(" ", nextSpace + 1);
|
||||
else
|
||||
secondSpace = description.IndexOf(": ", nextSpace + 2) - description.IndexOf(" ", nextSpace);
|
||||
|
||||
string val;
|
||||
if (secondSpace == -1)
|
||||
{
|
||||
val = description.Substring(nextSpace).Trim();
|
||||
description = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = description.Substring(nextSpace, secondSpace - nextSpace).Trim();
|
||||
description = description.Substring(secondSpace);
|
||||
}
|
||||
|
||||
switch (counter)
|
||||
{
|
||||
case 0:
|
||||
this.Size = ReleaseInfo.GetBytes(val);
|
||||
break;
|
||||
case 1:
|
||||
this.Seeders = ParseUtil.CoerceInt(val.Contains(",") ? val.Remove(val.IndexOf(","), 1) : val);
|
||||
break;
|
||||
case 2:
|
||||
this.Peers = ParseUtil.CoerceInt(val.Contains(",") ? val.Remove(val.IndexOf(","), 1) : val);
|
||||
break;
|
||||
case 3:
|
||||
this.hash = val;
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
public string Description { get; set; }
|
||||
public long Size { get; set; }
|
||||
public int Seeders { get; set; }
|
||||
public int Peers { get; set; }
|
||||
public string hash { get; set; }
|
||||
}
|
||||
}
|
@@ -190,7 +190,6 @@
|
||||
<Compile Include="Indexers\BlueTigers.cs" />
|
||||
<Compile Include="Indexers\EuTorrents.cs" />
|
||||
<Compile Include="Indexers\Avistaz.cs" />
|
||||
<Compile Include="Indexers\BakaBT.cs" />
|
||||
<Compile Include="Indexers\BaseIndexer.cs" />
|
||||
<Compile Include="Indexers\BB.cs" />
|
||||
<Compile Include="Indexers\BeyondHD.cs" />
|
||||
@@ -212,15 +211,12 @@
|
||||
<Compile Include="Indexers\ImmortalSeed.cs" />
|
||||
<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" />
|
||||
<Compile Include="Indexers\Rarbg.cs" />
|
||||
<Compile Include="Indexers\TVChaosUK.cs" />
|
||||
<Compile Include="Indexers\NCore.cs" />
|
||||
<Compile Include="Indexers\RuTor.cs" />
|
||||
<Compile Include="Indexers\TorrentBytes.cs" />
|
||||
<Compile Include="Indexers\IPTorrents.cs" />
|
||||
<Compile Include="Indexers\MoreThanTV.cs" />
|
||||
@@ -228,15 +224,10 @@
|
||||
<Compile Include="Indexers\PrivateHD.cs" />
|
||||
<Compile Include="Indexers\SceneAccess.cs" />
|
||||
<Compile Include="Indexers\SceneTime.cs" />
|
||||
<Compile Include="Indexers\ShowRSS.cs" />
|
||||
<Compile Include="Indexers\SpeedCD.cs" />
|
||||
<Compile Include="Indexers\Strike.cs" />
|
||||
<Compile Include="Indexers\T411.cs" />
|
||||
<Compile Include="Indexers\ThePirateBay.cs" />
|
||||
<Compile Include="Indexers\TorrentDay.cs" />
|
||||
<Compile Include="Indexers\TorrentLeech.cs" />
|
||||
<Compile Include="Indexers\TorrentShack.cs" />
|
||||
<Compile Include="Indexers\Torrentz.cs" />
|
||||
<Compile Include="JackettProtectedAttribute.cs" />
|
||||
<Compile Include="Models\CachedLog.cs" />
|
||||
<Compile Include="Models\CachedResult.cs" />
|
||||
@@ -437,15 +428,9 @@
|
||||
<Content Include="Content\logos\animebytes.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\animetorrents.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\avistaz.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\bakabt.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\bb.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -500,12 +485,6 @@
|
||||
<Content Include="Content\logos\privatehd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\rarbg.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\rutor.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\sceneaccess.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -515,15 +494,9 @@
|
||||
<Content Include="Content\logos\shazbat.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\showrss.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\speedcd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\t411.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\tehconnection.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -584,18 +557,9 @@
|
||||
<Content Include="Content\logos\morethantv.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\strike.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\thepiratebay.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\torrentleech.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\torrentz.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\tvchaosuk.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|