Implement animetorrents #150

This commit is contained in:
KZ
2015-08-25 21:30:32 +01:00
parent a2611a5797
commit c7656971a2
8 changed files with 1626 additions and 1429 deletions

193
README.md
View File

@@ -1,97 +1,98 @@
## Jackett ## Jackett
#### Download #### Download
Downloads on the [Releases page](https://github.com/zone117x/Jackett/releases) Downloads on the [Releases page](https://github.com/zone117x/Jackett/releases)
#### Overview #### 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/). 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. 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. We were previously focused on TV but are working on extending searches to allow for searching other items such as movies, comics, and music.
#### Supported Systems #### Supported Systems
* Windows using .NET 4.5 * Windows using .NET 4.5
* Linux and OSX using Mono 4 (v3 should work but you may experience crashes). * Linux and OSX using Mono 4 (v3 should work but you may experience crashes).
#### Supported Trackers #### Supported Trackers
* [AlphaRatio](https://alpharatio.cc/) * [AlphaRatio](https://alpharatio.cc/)
* [AnimeBytes](https://animebytes.tv/) * [AnimeBytes](https://animebytes.tv/)
* [Avistaz](https://avistaz.to/) * [AnimeTorrents](http://animetorrents.me/)
* [BakaBT](http://bakabt.me/) * [Avistaz](https://avistaz.to/)
* [bB](http://reddit.com/r/baconbits) * [BakaBT](http://bakabt.me/)
* [BeyondHD](https://beyondhd.me/) * [bB](http://reddit.com/r/baconbits)
* [BIT-HDTV](https://www.bit-hdtv.com) * [BeyondHD](https://beyondhd.me/)
* [BitMeTV](http://www.bitmetv.org/) * [BIT-HDTV](https://www.bit-hdtv.com)
* [Demonoid](http://www.demonoid.pw/) * [BitMeTV](http://www.bitmetv.org/)
* [EuTorrents](https://eutorrents.to/) * [Demonoid](http://www.demonoid.pw/)
* [FileList](http://filelist.ro/) * [EuTorrents](https://eutorrents.to/)
* [FrenchTorrentDb](http://www.frenchtorrentdb.com/) * [FileList](http://filelist.ro/)
* [Freshon](https://freshon.tv/) * [FrenchTorrentDb](http://www.frenchtorrentdb.com/)
* [HD-Space](https://hd-space.org/) * [Freshon](https://freshon.tv/)
* [HD-Torrents.org](https://hd-torrents.org/) * [HD-Space](https://hd-space.org/)
* [Immortalseed.me](http://immortalseed.me) * [HD-Torrents.org](https://hd-torrents.org/)
* [IPTorrents](https://iptorrents.com/) * [Immortalseed.me](http://immortalseed.me)
* [MoreThan.tv](https://morethan.tv/) * [IPTorrents](https://iptorrents.com/)
* [pretome](https://pretome.info) * [MoreThan.tv](https://morethan.tv/)
* [PrivateHD](https://privatehd.to/) * [pretome](https://pretome.info)
* [RARGB](https://rarbg.to/) * [PrivateHD](https://privatehd.to/)
* [RuTor](http://rutor.org/) * [RARGB](https://rarbg.to/)
* [SceneAccess](https://sceneaccess.eu/login) * [RuTor](http://rutor.org/)
* [SceneTime](https://www.scenetime.com/) * [SceneAccess](https://sceneaccess.eu/login)
* [ShowRSS](https://showrss.info/) * [SceneTime](https://www.scenetime.com/)
* [Strike](https://getstrike.net/) * [ShowRSS](https://showrss.info/)
* [T411](http://www.t411.io/) * [Strike](https://getstrike.net/)
* [The Pirate Bay](https://thepiratebay.se/) * [T411](http://www.t411.io/)
* [TorrentBytes](https://www.torrentbytes.net/) * [The Pirate Bay](https://thepiratebay.se/)
* [TorrentDay](https://torrentday.eu/) * [TorrentBytes](https://www.torrentbytes.net/)
* [TorrentLeech](http://www.torrentleech.org/) * [TorrentDay](https://torrentday.eu/)
* [TorrentShack](http://torrentshack.me/) * [TorrentLeech](http://www.torrentleech.org/)
* [Torrentz](https://torrentz.eu/) * [TorrentShack](http://torrentshack.me/)
* [TV Chaos UK](https://tvchaosuk.com/) * [Torrentz](https://torrentz.eu/)
* [TV Chaos UK](https://tvchaosuk.com/)
#### Installation on Linux/OSX
1. Install [Mono 4](http://www.mono-project.com/download/) or better #### Installation on Linux/OSX
2. Install libcurl: 1. Install [Mono 4](http://www.mono-project.com/download/) or better
* Debian/Ubunutu: apt-get install libcurl-dev 2. Install libcurl:
* Redhat/Fedora: yum install libcurl-devel * Debian/Ubunutu: apt-get install libcurl-dev
* For other distros see the [Curl docs](http://curl.haxx.se/dlwiz/?type=devel). * Redhat/Fedora: yum install libcurl-devel
3. Download and extract the latest ```.tar.bz2``` release from the [website](http://jackett.net/Download) and run Jackett using mono with the command "mono JackettConsole.exe". * For other distros see the [Curl docs](http://curl.haxx.se/dlwiz/?type=devel).
3. Download and extract the latest ```.tar.bz2``` release from the [website](http://jackett.net/Download) and run Jackett using mono with the command "mono JackettConsole.exe".
#### Installation on Windows
#### Installation on Windows
Grab the latest release from the [web site](http://jackett.net/Download).
Grab the latest release from the [web site](http://jackett.net/Download).
We recommend you install Jackett as a Windows service using the supplied installer. When installed as a service the tray icon acts as a way to open/start/stop Jackett. If you opted to not install it as a service then Jackett will run its web server from the tray tool.
We recommend you install Jackett as a Windows service using the supplied installer. When installed as a service the tray icon acts as a way to open/start/stop Jackett. If you opted to not install it as a service then Jackett will run its web server from the tray tool.
Jackett can also be run from the command line using JackettConsole.exe if you would like to see log messages (Ensure the server isn't already running from the tray/service).
Jackett can also be run from the command line using JackettConsole.exe if you would like to see log messages (Ensure the server isn't already running from the tray/service).
#### Troubleshooting
#### Troubleshooting
* Command line switches
* Command line switches
You can pass various options when running via the command line, see --help for details.
You can pass various options when running via the command line, see --help for details.
* Unable to connect to certain trackers on Linux
* Unable to connect to certain trackers on Linux
Try running with the "--SSLFix true" if you are on Redhat/Fedora/NNS based libcurl. If the tracker is currently configured try removing it and adding it again. Alternatively try running with a different client via --UseClient (Warning: safecurl just executes curl and your details may be seen from the process list).
Try running with the "--SSLFix true" if you are on Redhat/Fedora/NNS based libcurl. If the tracker is currently configured try removing it and adding it again. Alternatively try running with a different client via --UseClient (Warning: safecurl just executes curl and your details may be seen from the process list).
* Enable logging
* Enable logging
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.
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.
### Additional Trackers
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 then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues) or contact us on IRC (see below). Pull requests must be made to the develop branch. ### Additional Trackers
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 then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues) or contact us on IRC (see below). Pull requests must be made to the develop branch.
### Contact & Support
Use the github issues pages or talk to us directly at: [irc.freenode.net#jackett](http://webchat.freenode.net/?channels=#jackett). ### Contact & Support
Use the github issues pages or talk to us directly at: [irc.freenode.net#jackett](http://webchat.freenode.net/?channels=#jackett).
### Screenshots
### Screenshots
![screenshot](http://i.imgur.com/t1sVva6.png "screenshot") ![screenshot](http://i.imgur.com/t1sVva6.png "screenshot")

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,176 @@
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;
}
}
}

View File

@@ -1,451 +1,452 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jackett.Models; using Jackett.Models;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using Jackett.Services; using Jackett.Services;
using Jackett.Utils; using Jackett.Utils;
using Jackett.Utils.Clients; using Jackett.Utils.Clients;
using AutoMapper; using AutoMapper;
using System.Threading; using System.Threading;
using Jackett.Models.IndexerConfig; using Jackett.Models.IndexerConfig;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public abstract class BaseIndexer public abstract class BaseIndexer
{ {
public string SiteLink { get; private set; } public string SiteLink { get; private set; }
public string DisplayDescription { get; private set; } public string DisplayDescription { get; private set; }
public string DisplayName { get; private set; } public string DisplayName { get; private set; }
public string ID { get { return GetIndexerID(GetType()); } } public string ID { get { return GetIndexerID(GetType()); } }
public bool IsConfigured { get; protected set; } public bool IsConfigured { get; protected set; }
public TorznabCapabilities TorznabCaps { get; private set; } public TorznabCapabilities TorznabCaps { get; private set; }
protected Logger logger; protected Logger logger;
protected IIndexerManagerService indexerService; protected IIndexerManagerService indexerService;
protected static List<CachedQueryResult> cache = new List<CachedQueryResult>(); protected static List<CachedQueryResult> cache = new List<CachedQueryResult>();
protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
protected IWebClient webclient; protected IWebClient webclient;
protected IProtectionService protectionService; protected IProtectionService protectionService;
protected readonly string downloadUrlBase = ""; protected readonly string downloadUrlBase = "";
protected string CookieHeader protected string CookieHeader
{ {
get { return configData.CookieHeader.Value; } get { return configData.CookieHeader.Value; }
set { configData.CookieHeader.Value = value; } set { configData.CookieHeader.Value = value; }
} }
protected ConfigurationData configData; protected ConfigurationData configData;
private List<CategoryMapping> categoryMapping = new List<CategoryMapping>(); private List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null, string downloadBase = null) public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null, string downloadBase = null)
{ {
if (!link.EndsWith("/")) if (!link.EndsWith("/"))
throw new Exception("Site link must end with a slash."); throw new Exception("Site link must end with a slash.");
DisplayName = name; DisplayName = name;
DisplayDescription = description; DisplayDescription = description;
SiteLink = link; SiteLink = link;
this.logger = logger; this.logger = logger;
indexerService = manager; indexerService = manager;
webclient = client; webclient = client;
protectionService = p; protectionService = p;
this.downloadUrlBase = downloadBase; this.downloadUrlBase = downloadBase;
this.configData = configData; this.configData = configData;
if (caps == null) if (caps == null)
caps = TorznabUtil.CreateDefaultTorznabTVCaps(); caps = TorznabUtil.CreateDefaultTorznabTVCaps();
TorznabCaps = caps; TorznabCaps = caps;
} }
public IEnumerable<ReleaseInfo> CleanLinks(IEnumerable<ReleaseInfo> releases) public IEnumerable<ReleaseInfo> CleanLinks(IEnumerable<ReleaseInfo> releases)
{ {
if (string.IsNullOrEmpty(downloadUrlBase)) if (string.IsNullOrEmpty(downloadUrlBase))
return releases; return releases;
foreach (var release in releases) foreach (var release in releases)
{ {
if (release.Link.ToString().StartsWith(downloadUrlBase)) if (release.Link.ToString().StartsWith(downloadUrlBase))
{ {
release.Link = new Uri(release.Link.ToString().Substring(downloadUrlBase.Length), UriKind.Relative); release.Link = new Uri(release.Link.ToString().Substring(downloadUrlBase.Length), UriKind.Relative);
} }
} }
return releases; return releases;
} }
public Uri UncleanLink(Uri link) public Uri UncleanLink(Uri link)
{ {
return new Uri(downloadUrlBase + link.ToString(), UriKind.RelativeOrAbsolute); return new Uri(downloadUrlBase + link.ToString(), UriKind.RelativeOrAbsolute);
} }
protected int MapTrackerCatToNewznab(string input) protected int MapTrackerCatToNewznab(string input)
{ {
if (null != input) if (null != input)
{ {
input = input.ToLowerInvariant(); input = input.ToLowerInvariant();
var mapping = categoryMapping.Where(m => m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant()).FirstOrDefault(); var mapping = categoryMapping.Where(m => m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant()).FirstOrDefault();
if (mapping != null) if (mapping != null)
{ {
return mapping.NewzNabCategory; return mapping.NewzNabCategory;
} }
} }
return 0; return 0;
} }
public static string GetIndexerID(Type type) public static string GetIndexerID(Type type)
{ {
return StringUtil.StripNonAlphaNumeric(type.Name.ToLowerInvariant()); return StringUtil.StripNonAlphaNumeric(type.Name.ToLowerInvariant());
} }
public virtual Task<ConfigurationData> GetConfigurationForSetup() public virtual Task<ConfigurationData> GetConfigurationForSetup()
{ {
return Task.FromResult<ConfigurationData>(configData); return Task.FromResult<ConfigurationData>(configData);
} }
public virtual void ResetBaseConfig() public virtual void ResetBaseConfig()
{ {
CookieHeader = string.Empty; CookieHeader = string.Empty;
IsConfigured = false; IsConfigured = false;
} }
protected virtual void SaveConfig() protected virtual void SaveConfig()
{ {
indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService, forDisplay: false)); indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService, forDisplay: false));
} }
protected void OnParseError(string results, Exception ex) protected void OnParseError(string results, Exception ex)
{ {
var fileName = string.Format("Error on {0} for {1}.txt", DateTime.Now.ToString("yyyyMMddHHmmss"), DisplayName); var fileName = string.Format("Error on {0} for {1}.txt", DateTime.Now.ToString("yyyyMMddHHmmss"), DisplayName);
var spacing = string.Join("", Enumerable.Repeat(Environment.NewLine, 5)); var spacing = string.Join("", Enumerable.Repeat(Environment.NewLine, 5));
var fileContents = string.Format("{0}{1}{2}", ex, spacing, results); var fileContents = string.Format("{0}{1}{2}", ex, spacing, results);
logger.Error(fileName + fileContents); logger.Error(fileName + fileContents);
} }
protected void CleanCache() protected void CleanCache()
{ {
foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList()) foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList())
{ {
cache.Remove(expired); cache.Remove(expired);
} }
} }
protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
{ {
var byteResult = new WebClientByteResult(); var byteResult = new WebClientByteResult();
// Map to byte // Map to byte
Mapper.Map(response, byteResult); Mapper.Map(response, byteResult);
await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies); await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies);
// Map to string // Map to string
Mapper.Map(byteResult, response); Mapper.Map(byteResult, response);
} }
protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
{ {
// Follow up to 5 redirects // Follow up to 5 redirects
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
if (!response.IsRedirect) if (!response.IsRedirect)
break; break;
await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies); await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies);
} }
} }
private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
{ {
if (incomingResponse.IsRedirect) if (incomingResponse.IsRedirect)
{ {
// Do redirect // Do redirect
var redirectedResponse = await webclient.GetBytes(new WebRequest() var redirectedResponse = await webclient.GetBytes(new WebRequest()
{ {
Url = overrideRedirectUrl ?? incomingResponse.RedirectingTo, Url = overrideRedirectUrl ?? incomingResponse.RedirectingTo,
Referer = referrer, Referer = referrer,
Cookies = overrideCookies ?? CookieHeader Cookies = overrideCookies ?? CookieHeader
}); });
Mapper.Map(redirectedResponse, incomingResponse); Mapper.Map(redirectedResponse, incomingResponse);
} }
} }
protected void LoadLegacyCookieConfig(JToken jsonConfig) protected void LoadLegacyCookieConfig(JToken jsonConfig)
{ {
string legacyCookieHeader = (string)jsonConfig["cookie_header"]; string legacyCookieHeader = (string)jsonConfig["cookie_header"];
if (!string.IsNullOrEmpty(legacyCookieHeader)) if (!string.IsNullOrEmpty(legacyCookieHeader))
{ {
CookieHeader = legacyCookieHeader; CookieHeader = legacyCookieHeader;
} }
else else
{ {
// Legacy cookie key // Legacy cookie key
var jcookies = jsonConfig["cookies"]; var jcookies = jsonConfig["cookies"];
if (jcookies is JArray) if (jcookies is JArray)
{ {
var array = (JArray)jcookies; var array = (JArray)jcookies;
legacyCookieHeader = string.Empty; legacyCookieHeader = string.Empty;
for (int i = 0; i < array.Count; i++) for (int i = 0; i < array.Count; i++)
{ {
if (i != 0) if (i != 0)
legacyCookieHeader += "; "; legacyCookieHeader += "; ";
legacyCookieHeader += array[i]; legacyCookieHeader += array[i];
} }
CookieHeader = legacyCookieHeader; CookieHeader = legacyCookieHeader;
} }
else if (jcookies != null) else if (jcookies != null)
{ {
CookieHeader = (string)jcookies; CookieHeader = (string)jcookies;
} }
} }
} }
public virtual void LoadFromSavedConfiguration(JToken jsonConfig) public virtual void LoadFromSavedConfiguration(JToken jsonConfig)
{ {
if (jsonConfig is JArray) if (jsonConfig is JArray)
{ {
configData.LoadValuesFromJson(jsonConfig, protectionService); configData.LoadValuesFromJson(jsonConfig, protectionService);
IsConfigured = true; IsConfigured = true;
} }
// read and upgrade old settings file format // read and upgrade old settings file format
else if (jsonConfig is Object) else if (jsonConfig is Object)
{ {
LoadLegacyCookieConfig(jsonConfig); LoadLegacyCookieConfig(jsonConfig);
SaveConfig(); SaveConfig();
IsConfigured = true; IsConfigured = true;
} }
} }
public async virtual Task<byte[]> Download(Uri link) public async virtual Task<byte[]> Download(Uri link)
{ {
var response = await RequestBytesWithCookiesAndRetry(link.ToString()); var response = await RequestBytesWithCookiesAndRetry(link.ToString());
return response.Content; return response.Content;
} }
protected async Task<WebClientByteResult> RequestBytesWithCookiesAndRetry(string url, string cookieOverride = null) protected async Task<WebClientByteResult> RequestBytesWithCookiesAndRetry(string url, string cookieOverride = null)
{ {
Exception lastException = null; Exception lastException = null;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
try try
{ {
return await RequestBytesWithCookies(url, cookieOverride); return await RequestBytesWithCookies(url, cookieOverride);
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error(string.Format("On attempt {0} downloading from {1}: {2}", (i + 1), DisplayName, e.Message)); logger.Error(string.Format("On attempt {0} downloading from {1}: {2}", (i + 1), DisplayName, e.Message));
lastException = e; lastException = e;
} }
await Task.Delay(500); await Task.Delay(500);
} }
throw lastException; throw lastException;
} }
protected async Task<WebClientStringResult> RequestStringWithCookies(string url, string cookieOverride = null, string referer = null) protected async Task<WebClientStringResult> RequestStringWithCookies(string url, string cookieOverride = null, string referer = null, Dictionary<string, string> headers = null)
{ {
var request = new Utils.Clients.WebRequest() var request = new Utils.Clients.WebRequest()
{ {
Url = url, Url = url,
Type = RequestType.GET, Type = RequestType.GET,
Cookies = CookieHeader, Cookies = CookieHeader,
Referer = referer Referer = referer,
}; Headers = headers
};
if (cookieOverride != null)
request.Cookies = cookieOverride; if (cookieOverride != null)
return await webclient.GetString(request); request.Cookies = cookieOverride;
} return await webclient.GetString(request);
}
protected async Task<WebClientStringResult> RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null)
{ protected async Task<WebClientStringResult> RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null, Dictionary<string,string> headers = null)
Exception lastException = null; {
for (int i = 0; i < 3; i++) Exception lastException = null;
{ for (int i = 0; i < 3; i++)
try {
{ try
return await RequestStringWithCookies(url, cookieOverride, referer); {
} return await RequestStringWithCookies(url, cookieOverride, referer, headers);
catch (Exception e) }
{ catch (Exception e)
logger.Error(string.Format("On attempt {0} checking for results from {1}: {2}", (i + 1), DisplayName, e.Message)); {
lastException = e; logger.Error(string.Format("On attempt {0} checking for results from {1}: {2}", (i + 1), DisplayName, e.Message));
} lastException = e;
await Task.Delay(500); }
} await Task.Delay(500);
}
throw lastException;
} throw lastException;
}
protected async Task<WebClientByteResult> RequestBytesWithCookies(string url, string cookieOverride = null)
{ protected async Task<WebClientByteResult> RequestBytesWithCookies(string url, string cookieOverride = null)
var request = new Utils.Clients.WebRequest() {
{ var request = new Utils.Clients.WebRequest()
Url = url, {
Type = RequestType.GET, Url = url,
Cookies = cookieOverride ?? CookieHeader Type = RequestType.GET,
}; Cookies = cookieOverride ?? CookieHeader
};
if (cookieOverride != null)
request.Cookies = cookieOverride; if (cookieOverride != null)
return await webclient.GetBytes(request); request.Cookies = cookieOverride;
} return await webclient.GetBytes(request);
}
protected async Task<WebClientStringResult> PostDataWithCookies(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null)
{ protected async Task<WebClientStringResult> PostDataWithCookies(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null)
var request = new Utils.Clients.WebRequest() {
{ var request = new Utils.Clients.WebRequest()
Url = url, {
Type = RequestType.POST, Url = url,
Cookies = cookieOverride ?? CookieHeader, Type = RequestType.POST,
PostData = data Cookies = cookieOverride ?? CookieHeader,
}; PostData = data
return await webclient.GetString(request); };
} return await webclient.GetString(request);
}
protected async Task<WebClientStringResult> PostDataWithCookiesAndRetry(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null)
{ protected async Task<WebClientStringResult> PostDataWithCookiesAndRetry(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null)
Exception lastException = null; {
for (int i = 0; i < 3; i++) Exception lastException = null;
{ for (int i = 0; i < 3; i++)
try {
{ try
return await PostDataWithCookies(url, data, cookieOverride); {
} return await PostDataWithCookies(url, data, cookieOverride);
catch (Exception e) }
{ catch (Exception e)
logger.Error(string.Format("On attempt {0} checking for results from {1}: {2}", (i + 1), DisplayName, e.Message)); {
lastException = e; logger.Error(string.Format("On attempt {0} checking for results from {1}: {2}", (i + 1), DisplayName, e.Message));
} lastException = e;
await Task.Delay(500); }
} await Task.Delay(500);
}
throw lastException;
} throw lastException;
}
protected async Task<WebClientStringResult> RequestLoginAndFollowRedirect(string url, IEnumerable<KeyValuePair<string, string>> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null)
{ protected async Task<WebClientStringResult> RequestLoginAndFollowRedirect(string url, IEnumerable<KeyValuePair<string, string>> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null)
var request = new Utils.Clients.WebRequest() {
{ var request = new Utils.Clients.WebRequest()
Url = url, {
Type = RequestType.POST, Url = url,
Cookies = cookies, Type = RequestType.POST,
Referer = referer, Cookies = cookies,
PostData = data Referer = referer,
}; PostData = data
var response = await webclient.GetString(request); };
var firstCallCookies = response.Cookies; var response = await webclient.GetString(request);
var firstCallCookies = response.Cookies;
if (response.IsRedirect)
{ if (response.IsRedirect)
await FollowIfRedirect(response, request.Url, null, response.Cookies); {
} await FollowIfRedirect(response, request.Url, null, response.Cookies);
}
if (returnCookiesFromFirstCall)
{ if (returnCookiesFromFirstCall)
response.Cookies = firstCallCookies; {
} response.Cookies = firstCallCookies;
}
return response;
} return response;
}
protected async Task ConfigureIfOK(string cookies, bool isLoggedin, Func<Task> onError)
{ protected async Task ConfigureIfOK(string cookies, bool isLoggedin, Func<Task> onError)
if (isLoggedin) {
{ if (isLoggedin)
CookieHeader = cookies; {
SaveConfig(); CookieHeader = cookies;
IsConfigured = true; SaveConfig();
} IsConfigured = true;
else }
{ else
await onError(); {
} await onError();
} }
}
public virtual IEnumerable<ReleaseInfo> FilterResults(TorznabQuery query, IEnumerable<ReleaseInfo> results)
{ public virtual IEnumerable<ReleaseInfo> FilterResults(TorznabQuery query, IEnumerable<ReleaseInfo> results)
foreach (var result in results) {
{ foreach (var result in results)
if (query.Categories.Length == 0 || query.Categories.Contains(result.Category) || result.Category == 0 || TorznabCatType.QueryContainsParentCategory(query.Categories, result.Category)) {
{ if (query.Categories.Length == 0 || query.Categories.Contains(result.Category) || result.Category == 0 || TorznabCatType.QueryContainsParentCategory(query.Categories, result.Category))
yield return result; {
} yield return result;
} }
} }
}
protected void AddCategoryMapping(string trackerCategory, int newznabCategory)
{ protected void AddCategoryMapping(string trackerCategory, int newznabCategory)
categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory)); {
} categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory));
}
protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory)
{ protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory)
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); {
if (!TorznabCaps.Categories.Contains(newznabCategory)) categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID));
TorznabCaps.Categories.Add(newznabCategory); if (!TorznabCaps.Categories.Contains(newznabCategory))
} TorznabCaps.Categories.Add(newznabCategory);
}
protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory)
{ protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory)
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); {
if (!TorznabCaps.Categories.Contains(newznabCategory)) categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID));
TorznabCaps.Categories.Add(newznabCategory); if (!TorznabCaps.Categories.Contains(newznabCategory))
} TorznabCaps.Categories.Add(newznabCategory);
}
protected void AddCategoryMapping(int trackerCategory, int newznabCategory)
{ protected void AddCategoryMapping(int trackerCategory, int newznabCategory)
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory)); {
} categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory));
}
protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories)
{ protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories)
foreach (var trackerCat in trackerCategories) {
{ foreach (var trackerCat in trackerCategories)
categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID)); {
} categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID));
} }
}
protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories)
{ protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories)
foreach (var newznabCat in newznabCategories) {
{ foreach (var newznabCat in newznabCategories)
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID)); {
} categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID));
} }
}
protected List<string> MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false)
{ protected List<string> MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false)
var result = new List<string>(); {
foreach (var cat in query.Categories) var result = new List<string>();
{ foreach (var cat in query.Categories)
var queryCats = new List<int> { cat }; {
var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat); var queryCats = new List<int> { cat };
if (newznabCat != null) var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat);
{ if (newznabCat != null)
queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID)); {
} queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID));
}
if (mapChildrenCatsToParent)
{ if (mapChildrenCatsToParent)
var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat)); {
if (parentNewznabCat != null) var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat));
{ if (parentNewznabCat != null)
queryCats.Add(parentNewznabCat.ID); {
} queryCats.Add(parentNewznabCat.ID);
} }
}
foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory)))
{ foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory)))
result.Add(mapping.TrackerCategory); {
} result.Add(mapping.TrackerCategory);
} }
}
return result.Distinct().ToList();
} return result.Distinct().ToList();
} }
} }
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +1,82 @@
using Autofac; using Autofac;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Autofac.Integration.WebApi; using Autofac.Integration.WebApi;
using Jackett.Indexers; using Jackett.Indexers;
using Jackett.Utils; using Jackett.Utils;
using Jackett.Utils.Clients; using Jackett.Utils.Clients;
using AutoMapper; using AutoMapper;
using Jackett.Models; using Jackett.Models;
namespace Jackett namespace Jackett
{ {
public class JackettModule : Module public class JackettModule : Module
{ {
protected override void Load(ContainerBuilder builder) protected override void Load(ContainerBuilder builder)
{ {
// Just register everything! // Just register everything!
var thisAssembly = typeof(JackettModule).Assembly; var thisAssembly = typeof(JackettModule).Assembly;
builder.RegisterAssemblyTypes(thisAssembly).Except<IIndexer>().AsImplementedInterfaces().SingleInstance(); builder.RegisterAssemblyTypes(thisAssembly).Except<IIndexer>().AsImplementedInterfaces().SingleInstance();
builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); builder.RegisterApiControllers(thisAssembly).InstancePerRequest();
builder.RegisterType<HttpWebClient>();
// Register the best web client for the platform or the override
switch (Startup.ClientOverride) // Register the best web client for the platform or the override
{ switch (Startup.ClientOverride)
case "httpclient": {
builder.RegisterType<HttpWebClient>().As<IWebClient>(); case "httpclient":
break; builder.RegisterType<HttpWebClient>().As<IWebClient>();
case "safecurl": break;
builder.RegisterType<UnixSafeCurlWebClient>().As<IWebClient>(); case "safecurl":
break; builder.RegisterType<UnixSafeCurlWebClient>().As<IWebClient>();
case "libcurl": break;
builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>(); case "libcurl":
break; builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>();
case "automatic": break;
default: case "automatic":
if (System.Environment.OSVersion.Platform == PlatformID.Unix) default:
{ if (System.Environment.OSVersion.Platform == PlatformID.Unix)
builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>(); {
} builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>();
else }
{ else
builder.RegisterType<HttpWebClient>().As<IWebClient>(); {
} builder.RegisterType<HttpWebClient>().As<IWebClient>();
break; }
} break;
}
// Register indexers
foreach (var indexer in thisAssembly.GetTypes() // Register indexers
.Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface) foreach (var indexer in thisAssembly.GetTypes()
.ToArray()) .Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface)
{ .ToArray())
builder.RegisterType(indexer).Named<IIndexer>(BaseIndexer.GetIndexerID(indexer)); {
} builder.RegisterType(indexer).Named<IIndexer>(BaseIndexer.GetIndexerID(indexer));
}
Mapper.CreateMap<WebClientByteResult, WebClientStringResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((be, str) =>
{ Mapper.CreateMap<WebClientByteResult, WebClientStringResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((be, str) =>
str.Content = Encoding.UTF8.GetString(be.Content); {
}); str.Content = Encoding.UTF8.GetString(be.Content);
});
Mapper.CreateMap<WebClientStringResult, WebClientByteResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((str, be) =>
{ Mapper.CreateMap<WebClientStringResult, WebClientByteResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((str, be) =>
if (!string.IsNullOrEmpty(str.Content)) {
{ if (!string.IsNullOrEmpty(str.Content))
be.Content = Encoding.UTF8.GetBytes(str.Content); {
} be.Content = Encoding.UTF8.GetBytes(str.Content);
}); }
});
Mapper.CreateMap<WebClientStringResult, WebClientStringResult>();
Mapper.CreateMap<WebClientByteResult, WebClientByteResult>(); Mapper.CreateMap<WebClientStringResult, WebClientStringResult>();
Mapper.CreateMap<ReleaseInfo, ReleaseInfo>(); Mapper.CreateMap<WebClientByteResult, WebClientByteResult>();
Mapper.CreateMap<ReleaseInfo, ReleaseInfo>();
Mapper.CreateMap<ReleaseInfo, TrackerCacheResult>().AfterMap((r, t) =>
{ Mapper.CreateMap<ReleaseInfo, TrackerCacheResult>().AfterMap((r, t) =>
t.CategoryDesc = TorznabCatType.GetCatDesc(r.Category); {
}); t.CategoryDesc = TorznabCatType.GetCatDesc(r.Category);
} });
} }
} }
}

View File

@@ -1,110 +1,118 @@
using AutoMapper; using AutoMapper;
using Jackett.Models; using Jackett.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Jackett.Utils.Clients namespace Jackett.Utils.Clients
{ {
class HttpWebClient : IWebClient public class HttpWebClient : IWebClient
{ {
private Logger logger; private Logger logger;
public HttpWebClient(Logger l) public HttpWebClient(Logger l)
{ {
logger = l; logger = l;
} }
public void Init() public void Init()
{ {
} }
public async Task<WebClientByteResult> GetBytes(WebRequest request) public async Task<WebClientByteResult> GetBytes(WebRequest request)
{ {
logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url)); logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url));
var result = await Run(request); var result = await Run(request);
logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "<NULL>" : result.Content.Length.ToString()))); logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "<NULL>" : result.Content.Length.ToString())));
return result; return result;
} }
public async Task<WebClientStringResult> GetString(WebRequest request) public async Task<WebClientStringResult> GetString(WebRequest request)
{ {
logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url)); logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url));
var result = await Run(request); var result = await Run(request);
logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "<NULL>" : Encoding.UTF8.GetString(result.Content)))); logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "<NULL>" : Encoding.UTF8.GetString(result.Content))));
return Mapper.Map<WebClientStringResult>(result); return Mapper.Map<WebClientStringResult>(result);
} }
private async Task<WebClientByteResult> Run(WebRequest request) private async Task<WebClientByteResult> Run(WebRequest request)
{ {
var cookies = new CookieContainer(); var cookies = new CookieContainer();
if (!string.IsNullOrEmpty(request.Cookies)) if (!string.IsNullOrEmpty(request.Cookies))
{ {
var uri = new Uri(request.Url); var uri = new Uri(request.Url);
foreach (var c in request.Cookies.Split(';')) foreach (var c in request.Cookies.Split(';'))
{ {
try try
{ {
cookies.SetCookies(uri, c); cookies.SetCookies(uri, c);
} }
catch (CookieException ex) catch (CookieException ex)
{ {
logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message);
} }
} }
} }
var client = new HttpClient(new HttpClientHandler var client = new HttpClient(new HttpClientHandler
{ {
CookieContainer = cookies, CookieContainer = cookies,
AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more. AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more.
UseCookies = true, UseCookies = true,
}); });
client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent);
HttpResponseMessage response = null; HttpResponseMessage response = null;
if (request.Type == RequestType.POST) if (request.Headers != null)
{ {
var content = new FormUrlEncodedContent(request.PostData); foreach (var header in request.Headers)
response = await client.PostAsync(request.Url, content); {
} client.DefaultRequestHeaders.Add(header.Key, header.Value);
else }
{ }
response = await client.GetAsync(request.Url);
} if (request.Type == RequestType.POST)
{
var result = new WebClientByteResult(); var content = new FormUrlEncodedContent(request.PostData);
result.Content = await response.Content.ReadAsByteArrayAsync(); response = await client.PostAsync(request.Url, content);
if (response.Headers.Location != null) }
{ else
result.RedirectingTo = response.Headers.Location.ToString(); {
} response = await client.GetAsync(request.Url);
result.Status = response.StatusCode; }
// Compatiblity issue between the cookie format and httpclient var result = new WebClientByteResult();
// Pull it out manually ignoring the expiry date then set it manually result.Content = await response.Content.ReadAsByteArrayAsync();
// http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer if (response.Headers.Location != null)
IEnumerable<string> cookieHeaders; {
if (response.Headers.TryGetValues("set-cookie", out cookieHeaders)) result.RedirectingTo = response.Headers.Location.ToString();
{ }
var cookieBuilder = new StringBuilder(); result.Status = response.StatusCode;
foreach (var c in cookieHeaders)
{ // Compatiblity issue between the cookie format and httpclient
cookieBuilder.AppendFormat("{0} ", c.Substring(0, c.IndexOf(';') + 1)); // Pull it out manually ignoring the expiry date then set it manually
} // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer
IEnumerable<string> cookieHeaders;
result.Cookies = cookieBuilder.ToString().TrimEnd(); if (response.Headers.TryGetValues("set-cookie", out cookieHeaders))
} {
var cookieBuilder = new StringBuilder();
ServerUtil.ResureRedirectIsFullyQualified(request, result); foreach (var c in cookieHeaders)
return result; {
} cookieBuilder.AppendFormat("{0} ", c.Substring(0, c.IndexOf(';') + 1));
} }
}
result.Cookies = cookieBuilder.ToString().TrimEnd();
}
ServerUtil.ResureRedirectIsFullyQualified(request, result);
return result;
}
}
}

View File

@@ -1,89 +1,95 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Jackett.Utils.Clients namespace Jackett.Utils.Clients
{ {
public class WebRequest public class WebRequest
{ {
public WebRequest() public WebRequest()
{ {
PostData = new List<KeyValuePair<string, string>>(); PostData = new List<KeyValuePair<string, string>>();
Type = RequestType.GET; Type = RequestType.GET;
} Headers = new Dictionary<string, string>();
}
public WebRequest(string url)
{ public WebRequest(string url)
PostData = new List<KeyValuePair<string, string>>(); {
Type = RequestType.GET; PostData = new List<KeyValuePair<string, string>>();
Url = url; Type = RequestType.GET;
} Url = url;
Headers = new Dictionary<string, string>();
public string Url { get; set; } }
public IEnumerable<KeyValuePair<string, string>> PostData { get; set; }
public string Cookies { get; set; } public string Url { get; set; }
public string Referer { get; set; } public IEnumerable<KeyValuePair<string, string>> PostData { get; set; }
public RequestType Type { get; set; } public string Cookies { get; set; }
public string Referer { get; set; }
public RequestType Type { get; set; }
public override bool Equals(System.Object obj)
{ /// <summary>
if (obj is WebRequest) /// Warning this is only implemented on HTTPWebClient currently!
{ /// </summary>
var other = obj as WebRequest; public Dictionary<string, string> Headers { get; set; }
var postDataSame = PostData == null && other.PostData == null;
if (!postDataSame) public override bool Equals(System.Object obj)
{ {
if (!(PostData == null || other.PostData == null)) if (obj is WebRequest)
{ {
var ok = PostData.Count() == other.PostData.Count(); var other = obj as WebRequest;
foreach (var i in PostData) var postDataSame = PostData == null && other.PostData == null;
{ if (!postDataSame)
if (!other.PostData.Any(item => item.Key == i.Key)) {
{ if (!(PostData == null || other.PostData == null))
ok = false; {
break; var ok = PostData.Count() == other.PostData.Count();
} foreach (var i in PostData)
{
if (PostData.FirstOrDefault(item => item.Key == i.Key).Value != other.PostData.FirstOrDefault(item => item.Key == i.Key).Value) if (!other.PostData.Any(item => item.Key == i.Key))
{ {
ok = false; ok = false;
break; break;
} }
}
if (PostData.FirstOrDefault(item => item.Key == i.Key).Value != other.PostData.FirstOrDefault(item => item.Key == i.Key).Value)
if (ok) {
{ ok = false;
postDataSame = true; break;
} }
} }
}
if (ok)
return other.Url == Url && {
other.Referer == Referer && postDataSame = true;
other.Cookies == Cookies && }
other.Type == Type && }
postDataSame; }
} return other.Url == Url &&
else other.Referer == Referer &&
{ other.Cookies == Cookies &&
return false; other.Type == Type &&
} postDataSame;
}
}
public override int GetHashCode() else
{ {
return base.GetHashCode(); return false;
} }
} }
public enum RequestType public override int GetHashCode()
{ {
GET, return base.GetHashCode();
POST }
} }
}
public enum RequestType
{
GET,
POST
}
}