diff --git a/README.md b/README.md index 8c38e4ce8..306f050aa 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ We were previously focused on TV but are working on extending searches to allow #### 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) @@ -59,7 +60,7 @@ We were previously focused on TV but are working on extending searches to allow * Debian/Ubunutu: apt-get install libcurl-dev * Redhat/Fedora: yum install libcurl-devel * 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". +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 diff --git a/src/Jackett/Content/logos/animetorrents.png b/src/Jackett/Content/logos/animetorrents.png new file mode 100644 index 000000000..24c836e4b Binary files /dev/null and b/src/Jackett/Content/logos/animetorrents.png differ diff --git a/src/Jackett/Content/logos/nxtgn.png b/src/Jackett/Content/logos/nxtgn.png new file mode 100644 index 000000000..7a26136d4 Binary files /dev/null and b/src/Jackett/Content/logos/nxtgn.png differ diff --git a/src/Jackett/Controllers/PotatoController.cs b/src/Jackett/Controllers/PotatoController.cs index cf5a83fc3..e10662652 100644 --- a/src/Jackett/Controllers/PotatoController.cs +++ b/src/Jackett/Controllers/PotatoController.cs @@ -1,162 +1,167 @@ -using AutoMapper; -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.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Web.Http; - -namespace Jackett.Controllers -{ - [AllowAnonymous] - [JackettAPINoCache] - public class PotatoController : ApiController - { - private IIndexerManagerService indexerService; - private Logger logger; - private IServerService serverService; - private ICacheService cacheService; - private IWebClient webClient; - - public static int[] MOVIE_CATS - { - get - { - var torznabQuery = new TorznabQuery() - { - Categories = new int[1] { TorznabCatType.Movies.ID }, - }; - - torznabQuery.ExpandCatsToSubCats(); - return torznabQuery.Categories; - } - } - - public PotatoController(IIndexerManagerService i, Logger l, IServerService s, ICacheService c, IWebClient w) - { - indexerService = i; - logger = l; - serverService = s; - cacheService = c; - webClient = w; - } - - [HttpGet] - public async Task Call(string indexerID, [FromUri]TorrentPotatoRequest request) - { - var indexer = indexerService.GetIndexer(indexerID); - - var allowBadApiDueToDebug = false; -#if DEBUG - allowBadApiDueToDebug = Debugger.IsAttached; -#endif - - if (!allowBadApiDueToDebug && !string.Equals(request.passkey, serverService.Config.APIKey, StringComparison.InvariantCultureIgnoreCase)) - { - logger.Warn(string.Format("A request from {0} was made with an incorrect API key.", Request.GetOwinContext().Request.RemoteIpAddress)); - return Request.CreateResponse(HttpStatusCode.Forbidden, "Incorrect API key"); - } - - if (!indexer.IsConfigured) - { - logger.Warn(string.Format("Rejected a request to {0} which is unconfigured.", indexer.DisplayName)); - return Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer is not configured."); - } - - if (!indexer.TorznabCaps.Categories.Select(c => c.ID).Any(i => MOVIE_CATS.Contains(i))){ - logger.Warn(string.Format("Rejected a request to {0} which does not support searching for movies.", indexer.DisplayName)); - return Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer does not support movies."); - } - - var year = 0; - - if (string.IsNullOrWhiteSpace(request.search)) - { - // We are searching by IMDB id so look up the name - var response = await webClient.GetString(new Utils.Clients.WebRequest("http://www.omdbapi.com/?type=movie&i=" + request.imdbid)); - if (response.Status == HttpStatusCode.OK) - { - JObject result = JObject.Parse(response.Content); - if (result["Title"] != null) - { - request.search = result["Title"].ToString(); - year = ParseUtil.CoerceInt(result["Year"].ToString()); - } - } - } - - var torznabQuery = new TorznabQuery() - { - ApiKey = request.passkey, - Categories = MOVIE_CATS, - SearchTerm = request.search - }; - - IEnumerable releases = new List(); - - if (!string.IsNullOrWhiteSpace(torznabQuery.SanitizedSearchTerm)) - { - releases = await indexer.PerformQuery(torznabQuery); - releases = indexer.CleanLinks(releases); - } - - // Cache non query results - if (string.IsNullOrEmpty(torznabQuery.SanitizedSearchTerm)) - { - cacheService.CacheRssResults(indexer, releases); - } - - releases = indexer.FilterResults(torznabQuery, releases); - var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); - var potatoResponse = new TorrentPotatoResponse(); - - releases = TorznabUtil.FilterResultsToTitle(releases, torznabQuery.SanitizedSearchTerm, year); - - foreach (var r in releases) - { - var release = Mapper.Map(r); - release.Link = serverService.ConvertToProxyLink(release.Link, serverUrl, indexerID); - - potatoResponse.results.Add(new TorrentPotatoResponseItem() - { - release_name = release.Title + "[" + indexer.DisplayName + "]", // Suffix the indexer so we can see which tracker we are using in CPS as it just says torrentpotato >.> - torrent_id = release.Guid.ToString(), - details_url = release.Comments.ToString(), - download_url = release.Link.ToString(), - // imdb_id = request.imdbid, - freeleech = false, - type = "movie", - size = (long)release.Size/ (1024 * 1024), // This is in MB - leechers = (int)release.Peers - (int)release.Seeders, - seeders = (int)release.Seeders - }); - } - - // Log info - if (string.IsNullOrWhiteSpace(torznabQuery.SanitizedSearchTerm)) - { - logger.Info(string.Format("Found {0} torrentpotato releases from {1}", releases.Count(), indexer.DisplayName)); - } - else - { - logger.Info(string.Format("Found {0} torrentpotato releases from {1} for: {2}", releases.Count(), indexer.DisplayName, torznabQuery.GetQueryString())); - } - - // Force the return as Json - return new HttpResponseMessage() - { - Content = new JsonContent(potatoResponse) - }; - } - } -} +using AutoMapper; +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.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; + +namespace Jackett.Controllers +{ + [AllowAnonymous] + [JackettAPINoCache] + public class PotatoController : ApiController + { + private IIndexerManagerService indexerService; + private Logger logger; + private IServerService serverService; + private ICacheService cacheService; + private IWebClient webClient; + + public static int[] MOVIE_CATS + { + get + { + var torznabQuery = new TorznabQuery() + { + Categories = new int[1] { TorznabCatType.Movies.ID }, + }; + + torznabQuery.ExpandCatsToSubCats(); + return torznabQuery.Categories; + } + } + + public PotatoController(IIndexerManagerService i, Logger l, IServerService s, ICacheService c, IWebClient w) + { + indexerService = i; + logger = l; + serverService = s; + cacheService = c; + webClient = w; + } + + [HttpGet] + public async Task Call(string indexerID, [FromUri]TorrentPotatoRequest request) + { + var indexer = indexerService.GetIndexer(indexerID); + + var allowBadApiDueToDebug = false; +#if DEBUG + allowBadApiDueToDebug = Debugger.IsAttached; +#endif + + if (!allowBadApiDueToDebug && !string.Equals(request.passkey, serverService.Config.APIKey, StringComparison.InvariantCultureIgnoreCase)) + { + logger.Warn(string.Format("A request from {0} was made with an incorrect API key.", Request.GetOwinContext().Request.RemoteIpAddress)); + return Request.CreateResponse(HttpStatusCode.Forbidden, "Incorrect API key"); + } + + if (!indexer.IsConfigured) + { + logger.Warn(string.Format("Rejected a request to {0} which is unconfigured.", indexer.DisplayName)); + return Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer is not configured."); + } + + if (!indexer.TorznabCaps.Categories.Select(c => c.ID).Any(i => MOVIE_CATS.Contains(i))){ + logger.Warn(string.Format("Rejected a request to {0} which does not support searching for movies.", indexer.DisplayName)); + return Request.CreateResponse(HttpStatusCode.Forbidden, "This indexer does not support movies."); + } + + var year = 0; + + if (string.IsNullOrWhiteSpace(request.search)) + { + // We are searching by IMDB id so look up the name + var response = await webClient.GetString(new Utils.Clients.WebRequest("http://www.omdbapi.com/?type=movie&i=" + request.imdbid)); + if (response.Status == HttpStatusCode.OK) + { + JObject result = JObject.Parse(response.Content); + if (result["Title"] != null) + { + request.search = result["Title"].ToString(); + year = ParseUtil.CoerceInt(result["Year"].ToString()); + } + } + } + + var torznabQuery = new TorznabQuery() + { + ApiKey = request.passkey, + Categories = MOVIE_CATS, + SearchTerm = request.search + }; + + IEnumerable releases = new List(); + + if (!string.IsNullOrWhiteSpace(torznabQuery.SanitizedSearchTerm)) + { + releases = await indexer.PerformQuery(torznabQuery); + releases = indexer.CleanLinks(releases); + } + + // Cache non query results + if (string.IsNullOrEmpty(torznabQuery.SanitizedSearchTerm)) + { + cacheService.CacheRssResults(indexer, releases); + } + + releases = indexer.FilterResults(torznabQuery, releases); + var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); + var potatoResponse = new TorrentPotatoResponse(); + + releases = TorznabUtil.FilterResultsToTitle(releases, torznabQuery.SanitizedSearchTerm, year); + releases = TorznabUtil.FilterResultsToImdb(releases, request.imdbid); + + foreach (var r in releases) + { + var release = Mapper.Map(r); + release.Link = serverService.ConvertToProxyLink(release.Link, serverUrl, indexerID); + + // Only accept torrent links, magnet is not supported + if (release.Link != null) + { + potatoResponse.results.Add(new TorrentPotatoResponseItem() + { + release_name = release.Title + "[" + indexer.DisplayName + "]", // Suffix the indexer so we can see which tracker we are using in CPS as it just says torrentpotato >.> + torrent_id = release.Guid.ToString(), + details_url = release.Comments.ToString(), + download_url = release.Link.ToString(), + imdb_id = release.Imdb.HasValue ? "tt" + release.Imdb : null, + freeleech = false, + type = "movie", + size = (long)release.Size / (1024 * 1024), // This is in MB + leechers = (int)release.Peers - (int)release.Seeders, + seeders = (int)release.Seeders + }); + } + } + + // Log info + if (string.IsNullOrWhiteSpace(torznabQuery.SanitizedSearchTerm)) + { + logger.Info(string.Format("Found {0} torrentpotato releases from {1}", releases.Count(), indexer.DisplayName)); + } + else + { + logger.Info(string.Format("Found {0} torrentpotato releases from {1} for: {2}", releases.Count(), indexer.DisplayName, torznabQuery.GetQueryString())); + } + + // Force the return as Json + return new HttpResponseMessage() + { + Content = new JsonContent(potatoResponse) + }; + } + } +} diff --git a/src/Jackett/Indexers/AnimeTorrents.cs b/src/Jackett/Indexers/AnimeTorrents.cs new file mode 100644 index 000000000..48a1fa95d --- /dev/null +++ b/src/Jackett/Indexers/AnimeTorrents.cs @@ -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 ApplyConfiguration(JToken configJson) + { + configData.LoadValuesFromJson(configJson); + var pairs = new Dictionary { + { "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> PerformQuery(TorznabQuery query) + { + var releases = new List(); + 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() + { + { "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; + } + } +} diff --git a/src/Jackett/Indexers/BaseIndexer.cs b/src/Jackett/Indexers/BaseIndexer.cs index 71cf91979..2b7efef21 100644 --- a/src/Jackett/Indexers/BaseIndexer.cs +++ b/src/Jackett/Indexers/BaseIndexer.cs @@ -1,451 +1,452 @@ -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.Services; -using Jackett.Utils; -using Jackett.Utils.Clients; -using AutoMapper; -using System.Threading; -using Jackett.Models.IndexerConfig; - -namespace Jackett.Indexers -{ - public abstract class BaseIndexer - { - public string SiteLink { get; private set; } - public string DisplayDescription { get; private set; } - public string DisplayName { get; private set; } - public string ID { get { return GetIndexerID(GetType()); } } - - public bool IsConfigured { get; protected set; } - public TorznabCapabilities TorznabCaps { get; private set; } - protected Logger logger; - protected IIndexerManagerService indexerService; - protected static List cache = new List(); - protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); - protected IWebClient webclient; - protected IProtectionService protectionService; - protected readonly string downloadUrlBase = ""; - - protected string CookieHeader - { - get { return configData.CookieHeader.Value; } - set { configData.CookieHeader.Value = value; } - } - - - - protected ConfigurationData configData; - - private List categoryMapping = new List(); - - 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("/")) - throw new Exception("Site link must end with a slash."); - - DisplayName = name; - DisplayDescription = description; - SiteLink = link; - this.logger = logger; - indexerService = manager; - webclient = client; - protectionService = p; - this.downloadUrlBase = downloadBase; - - this.configData = configData; - - if (caps == null) - caps = TorznabUtil.CreateDefaultTorznabTVCaps(); - TorznabCaps = caps; - - } - - public IEnumerable CleanLinks(IEnumerable releases) - { - if (string.IsNullOrEmpty(downloadUrlBase)) - return releases; - foreach (var release in releases) - { - if (release.Link.ToString().StartsWith(downloadUrlBase)) - { - release.Link = new Uri(release.Link.ToString().Substring(downloadUrlBase.Length), UriKind.Relative); - } - } - - return releases; - } - - public Uri UncleanLink(Uri link) - { - return new Uri(downloadUrlBase + link.ToString(), UriKind.RelativeOrAbsolute); - } - - protected int MapTrackerCatToNewznab(string input) - { - if (null != input) - { - input = input.ToLowerInvariant(); - var mapping = categoryMapping.Where(m => m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant()).FirstOrDefault(); - if (mapping != null) - { - return mapping.NewzNabCategory; - } - } - return 0; - } - - public static string GetIndexerID(Type type) - { - return StringUtil.StripNonAlphaNumeric(type.Name.ToLowerInvariant()); - } - - public virtual Task GetConfigurationForSetup() - { - return Task.FromResult(configData); - } - - public virtual void ResetBaseConfig() - { - CookieHeader = string.Empty; - IsConfigured = false; - } - - protected virtual void SaveConfig() - { - indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService, forDisplay: false)); - } - - protected void OnParseError(string results, Exception ex) - { - 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 fileContents = string.Format("{0}{1}{2}", ex, spacing, results); - logger.Error(fileName + fileContents); - } - - protected void CleanCache() - { - foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList()) - { - cache.Remove(expired); - } - } - - protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) - { - var byteResult = new WebClientByteResult(); - // Map to byte - Mapper.Map(response, byteResult); - await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies); - // Map to string - Mapper.Map(byteResult, response); - } - - protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) - { - // Follow up to 5 redirects - for (int i = 0; i < 5; i++) - { - if (!response.IsRedirect) - break; - await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies); - } - } - - private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) - { - if (incomingResponse.IsRedirect) - { - // Do redirect - var redirectedResponse = await webclient.GetBytes(new WebRequest() - { - Url = overrideRedirectUrl ?? incomingResponse.RedirectingTo, - Referer = referrer, - Cookies = overrideCookies ?? CookieHeader - }); - Mapper.Map(redirectedResponse, incomingResponse); - } - } - - - protected void LoadLegacyCookieConfig(JToken jsonConfig) - { - string legacyCookieHeader = (string)jsonConfig["cookie_header"]; - if (!string.IsNullOrEmpty(legacyCookieHeader)) - { - CookieHeader = legacyCookieHeader; - } - else - { - // Legacy cookie key - var jcookies = jsonConfig["cookies"]; - if (jcookies is JArray) - { - var array = (JArray)jcookies; - legacyCookieHeader = string.Empty; - for (int i = 0; i < array.Count; i++) - { - if (i != 0) - legacyCookieHeader += "; "; - legacyCookieHeader += array[i]; - } - CookieHeader = legacyCookieHeader; - } - else if (jcookies != null) - { - CookieHeader = (string)jcookies; - } - } - } - - public virtual void LoadFromSavedConfiguration(JToken jsonConfig) - { - if (jsonConfig is JArray) - { - configData.LoadValuesFromJson(jsonConfig, protectionService); - IsConfigured = true; - } - // read and upgrade old settings file format - else if (jsonConfig is Object) - { - LoadLegacyCookieConfig(jsonConfig); - SaveConfig(); - IsConfigured = true; - } - } - - public async virtual Task Download(Uri link) - { - var response = await RequestBytesWithCookiesAndRetry(link.ToString()); - return response.Content; - } - - protected async Task RequestBytesWithCookiesAndRetry(string url, string cookieOverride = null) - { - Exception lastException = null; - for (int i = 0; i < 3; i++) - { - try - { - return await RequestBytesWithCookies(url, cookieOverride); - } - catch (Exception e) - { - logger.Error(string.Format("On attempt {0} downloading from {1}: {2}", (i + 1), DisplayName, e.Message)); - lastException = e; - } - await Task.Delay(500); - } - - throw lastException; - } - - protected async Task RequestStringWithCookies(string url, string cookieOverride = null, string referer = null) - { - var request = new Utils.Clients.WebRequest() - { - Url = url, - Type = RequestType.GET, - Cookies = CookieHeader, - Referer = referer - }; - - if (cookieOverride != null) - request.Cookies = cookieOverride; - return await webclient.GetString(request); - } - - protected async Task RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null) - { - Exception lastException = null; - for (int i = 0; i < 3; i++) - { - try - { - return await RequestStringWithCookies(url, cookieOverride, referer); - } - catch (Exception 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); - } - - throw lastException; - } - - protected async Task RequestBytesWithCookies(string url, string cookieOverride = null) - { - var request = new Utils.Clients.WebRequest() - { - Url = url, - Type = RequestType.GET, - Cookies = cookieOverride ?? CookieHeader - }; - - if (cookieOverride != null) - request.Cookies = cookieOverride; - return await webclient.GetBytes(request); - } - - protected async Task PostDataWithCookies(string url, IEnumerable> data, string cookieOverride = null) - { - var request = new Utils.Clients.WebRequest() - { - Url = url, - Type = RequestType.POST, - Cookies = cookieOverride ?? CookieHeader, - PostData = data - }; - return await webclient.GetString(request); - } - - protected async Task PostDataWithCookiesAndRetry(string url, IEnumerable> data, string cookieOverride = null) - { - Exception lastException = null; - for (int i = 0; i < 3; i++) - { - try - { - return await PostDataWithCookies(url, data, cookieOverride); - } - catch (Exception 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); - } - - throw lastException; - } - - protected async Task RequestLoginAndFollowRedirect(string url, IEnumerable> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null) - { - var request = new Utils.Clients.WebRequest() - { - Url = url, - Type = RequestType.POST, - Cookies = cookies, - Referer = referer, - PostData = data - }; - var response = await webclient.GetString(request); - var firstCallCookies = response.Cookies; - - if (response.IsRedirect) - { - await FollowIfRedirect(response, request.Url, null, response.Cookies); - } - - if (returnCookiesFromFirstCall) - { - response.Cookies = firstCallCookies; - } - - return response; - } - - protected async Task ConfigureIfOK(string cookies, bool isLoggedin, Func onError) - { - if (isLoggedin) - { - CookieHeader = cookies; - SaveConfig(); - IsConfigured = true; - } - else - { - await onError(); - } - } - - public virtual IEnumerable FilterResults(TorznabQuery query, IEnumerable 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)) - { - yield return result; - } - } - } - - protected void AddCategoryMapping(string trackerCategory, int newznabCategory) - { - categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory)); - } - - protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory) - { - categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); - if (!TorznabCaps.Categories.Contains(newznabCategory)) - TorznabCaps.Categories.Add(newznabCategory); - } - - protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory) - { - categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); - if (!TorznabCaps.Categories.Contains(newznabCategory)) - TorznabCaps.Categories.Add(newznabCategory); - } - - protected void AddCategoryMapping(int trackerCategory, int newznabCategory) - { - categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory)); - } - - protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories) - { - foreach (var trackerCat in trackerCategories) - { - categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID)); - } - } - - protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories) - { - foreach (var newznabCat in newznabCategories) - { - categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID)); - } - } - - protected List MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false) - { - var result = new List(); - foreach (var cat in query.Categories) - { - var queryCats = new List { cat }; - var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat); - if (newznabCat != null) - { - queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID)); - } - - if (mapChildrenCatsToParent) - { - var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat)); - if (parentNewznabCat != null) - { - queryCats.Add(parentNewznabCat.ID); - } - } - - foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory))) - { - result.Add(mapping.TrackerCategory); - } - } - - return result.Distinct().ToList(); - } - } -} +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.Services; +using Jackett.Utils; +using Jackett.Utils.Clients; +using AutoMapper; +using System.Threading; +using Jackett.Models.IndexerConfig; + +namespace Jackett.Indexers +{ + public abstract class BaseIndexer + { + public string SiteLink { get; private set; } + public string DisplayDescription { get; private set; } + public string DisplayName { get; private set; } + public string ID { get { return GetIndexerID(GetType()); } } + + public bool IsConfigured { get; protected set; } + public TorznabCapabilities TorznabCaps { get; private set; } + protected Logger logger; + protected IIndexerManagerService indexerService; + protected static List cache = new List(); + protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); + protected IWebClient webclient; + protected IProtectionService protectionService; + protected readonly string downloadUrlBase = ""; + + protected string CookieHeader + { + get { return configData.CookieHeader.Value; } + set { configData.CookieHeader.Value = value; } + } + + + + protected ConfigurationData configData; + + private List categoryMapping = new List(); + + 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("/")) + throw new Exception("Site link must end with a slash."); + + DisplayName = name; + DisplayDescription = description; + SiteLink = link; + this.logger = logger; + indexerService = manager; + webclient = client; + protectionService = p; + this.downloadUrlBase = downloadBase; + + this.configData = configData; + + if (caps == null) + caps = TorznabUtil.CreateDefaultTorznabTVCaps(); + TorznabCaps = caps; + + } + + public IEnumerable CleanLinks(IEnumerable releases) + { + if (string.IsNullOrEmpty(downloadUrlBase)) + return releases; + foreach (var release in releases) + { + if (release.Link.ToString().StartsWith(downloadUrlBase)) + { + release.Link = new Uri(release.Link.ToString().Substring(downloadUrlBase.Length), UriKind.Relative); + } + } + + return releases; + } + + public Uri UncleanLink(Uri link) + { + return new Uri(downloadUrlBase + link.ToString(), UriKind.RelativeOrAbsolute); + } + + protected int MapTrackerCatToNewznab(string input) + { + if (null != input) + { + input = input.ToLowerInvariant(); + var mapping = categoryMapping.Where(m => m.TrackerCategory.ToLowerInvariant() == input.ToLowerInvariant()).FirstOrDefault(); + if (mapping != null) + { + return mapping.NewzNabCategory; + } + } + return 0; + } + + public static string GetIndexerID(Type type) + { + return StringUtil.StripNonAlphaNumeric(type.Name.ToLowerInvariant()); + } + + public virtual Task GetConfigurationForSetup() + { + return Task.FromResult(configData); + } + + public virtual void ResetBaseConfig() + { + CookieHeader = string.Empty; + IsConfigured = false; + } + + protected virtual void SaveConfig() + { + indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService, forDisplay: false)); + } + + protected void OnParseError(string results, Exception ex) + { + 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 fileContents = string.Format("{0}{1}{2}", ex, spacing, results); + logger.Error(fileName + fileContents); + } + + protected void CleanCache() + { + foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList()) + { + cache.Remove(expired); + } + } + + protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) + { + var byteResult = new WebClientByteResult(); + // Map to byte + Mapper.Map(response, byteResult); + await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies); + // Map to string + Mapper.Map(byteResult, response); + } + + protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) + { + // Follow up to 5 redirects + for (int i = 0; i < 5; i++) + { + if (!response.IsRedirect) + break; + await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies); + } + } + + private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null) + { + if (incomingResponse.IsRedirect) + { + // Do redirect + var redirectedResponse = await webclient.GetBytes(new WebRequest() + { + Url = overrideRedirectUrl ?? incomingResponse.RedirectingTo, + Referer = referrer, + Cookies = overrideCookies ?? CookieHeader + }); + Mapper.Map(redirectedResponse, incomingResponse); + } + } + + + protected void LoadLegacyCookieConfig(JToken jsonConfig) + { + string legacyCookieHeader = (string)jsonConfig["cookie_header"]; + if (!string.IsNullOrEmpty(legacyCookieHeader)) + { + CookieHeader = legacyCookieHeader; + } + else + { + // Legacy cookie key + var jcookies = jsonConfig["cookies"]; + if (jcookies is JArray) + { + var array = (JArray)jcookies; + legacyCookieHeader = string.Empty; + for (int i = 0; i < array.Count; i++) + { + if (i != 0) + legacyCookieHeader += "; "; + legacyCookieHeader += array[i]; + } + CookieHeader = legacyCookieHeader; + } + else if (jcookies != null) + { + CookieHeader = (string)jcookies; + } + } + } + + public virtual void LoadFromSavedConfiguration(JToken jsonConfig) + { + if (jsonConfig is JArray) + { + configData.LoadValuesFromJson(jsonConfig, protectionService); + IsConfigured = true; + } + // read and upgrade old settings file format + else if (jsonConfig is Object) + { + LoadLegacyCookieConfig(jsonConfig); + SaveConfig(); + IsConfigured = true; + } + } + + public async virtual Task Download(Uri link) + { + var response = await RequestBytesWithCookiesAndRetry(link.ToString()); + return response.Content; + } + + protected async Task RequestBytesWithCookiesAndRetry(string url, string cookieOverride = null) + { + Exception lastException = null; + for (int i = 0; i < 3; i++) + { + try + { + return await RequestBytesWithCookies(url, cookieOverride); + } + catch (Exception e) + { + logger.Error(string.Format("On attempt {0} downloading from {1}: {2}", (i + 1), DisplayName, e.Message)); + lastException = e; + } + await Task.Delay(500); + } + + throw lastException; + } + + protected async Task RequestStringWithCookies(string url, string cookieOverride = null, string referer = null, Dictionary headers = null) + { + var request = new Utils.Clients.WebRequest() + { + Url = url, + Type = RequestType.GET, + Cookies = CookieHeader, + Referer = referer, + Headers = headers + }; + + if (cookieOverride != null) + request.Cookies = cookieOverride; + return await webclient.GetString(request); + } + + protected async Task RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null, Dictionary headers = null) + { + Exception lastException = null; + for (int i = 0; i < 3; i++) + { + try + { + return await RequestStringWithCookies(url, cookieOverride, referer, headers); + } + catch (Exception 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); + } + + throw lastException; + } + + protected async Task RequestBytesWithCookies(string url, string cookieOverride = null) + { + var request = new Utils.Clients.WebRequest() + { + Url = url, + Type = RequestType.GET, + Cookies = cookieOverride ?? CookieHeader + }; + + if (cookieOverride != null) + request.Cookies = cookieOverride; + return await webclient.GetBytes(request); + } + + protected async Task PostDataWithCookies(string url, IEnumerable> data, string cookieOverride = null) + { + var request = new Utils.Clients.WebRequest() + { + Url = url, + Type = RequestType.POST, + Cookies = cookieOverride ?? CookieHeader, + PostData = data + }; + return await webclient.GetString(request); + } + + protected async Task PostDataWithCookiesAndRetry(string url, IEnumerable> data, string cookieOverride = null) + { + Exception lastException = null; + for (int i = 0; i < 3; i++) + { + try + { + return await PostDataWithCookies(url, data, cookieOverride); + } + catch (Exception 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); + } + + throw lastException; + } + + protected async Task RequestLoginAndFollowRedirect(string url, IEnumerable> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null) + { + var request = new Utils.Clients.WebRequest() + { + Url = url, + Type = RequestType.POST, + Cookies = cookies, + Referer = referer, + PostData = data + }; + var response = await webclient.GetString(request); + var firstCallCookies = response.Cookies; + + if (response.IsRedirect) + { + await FollowIfRedirect(response, request.Url, null, response.Cookies); + } + + if (returnCookiesFromFirstCall) + { + response.Cookies = firstCallCookies; + } + + return response; + } + + protected async Task ConfigureIfOK(string cookies, bool isLoggedin, Func onError) + { + if (isLoggedin) + { + CookieHeader = cookies; + SaveConfig(); + IsConfigured = true; + } + else + { + await onError(); + } + } + + public virtual IEnumerable FilterResults(TorznabQuery query, IEnumerable 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)) + { + yield return result; + } + } + } + + protected void AddCategoryMapping(string trackerCategory, int newznabCategory) + { + categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory)); + } + + protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory) + { + categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); + if (!TorznabCaps.Categories.Contains(newznabCategory)) + TorznabCaps.Categories.Add(newznabCategory); + } + + protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory) + { + categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID)); + if (!TorznabCaps.Categories.Contains(newznabCategory)) + TorznabCaps.Categories.Add(newznabCategory); + } + + protected void AddCategoryMapping(int trackerCategory, int newznabCategory) + { + categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory)); + } + + protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories) + { + foreach (var trackerCat in trackerCategories) + { + categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID)); + } + } + + protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories) + { + foreach (var newznabCat in newznabCategories) + { + categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID)); + } + } + + protected List MapTorznabCapsToTrackers(TorznabQuery query, bool mapChildrenCatsToParent = false) + { + var result = new List(); + foreach (var cat in query.Categories) + { + var queryCats = new List { cat }; + var newznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.ID == cat); + if (newznabCat != null) + { + queryCats.AddRange(newznabCat.SubCategories.Select(c => c.ID)); + } + + if (mapChildrenCatsToParent) + { + var parentNewznabCat = TorznabCatType.AllCats.FirstOrDefault(c => c.SubCategories.Contains(newznabCat)); + if (parentNewznabCat != null) + { + queryCats.Add(parentNewznabCat.ID); + } + } + + foreach (var mapping in categoryMapping.Where(c => queryCats.Contains(c.NewzNabCategory))) + { + result.Add(mapping.TrackerCategory); + } + } + + return result.Distinct().ToList(); + } + } +} diff --git a/src/Jackett/Indexers/NxtGn.cs b/src/Jackett/Indexers/NxtGn.cs new file mode 100644 index 000000000..f17659eb9 --- /dev/null +++ b/src/Jackett/Indexers/NxtGn.cs @@ -0,0 +1,213 @@ +using CsQuery; +using Jackett.Indexers; +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 System.Web.UI.WebControls; +using Jackett.Models.IndexerConfig; + +namespace Jackett.Indexers +{ + public class NxtGn : BaseIndexer, IIndexer + { + private string LoginUrl { get { return SiteLink + "login.php"; } } + private string SearchUrl { get { return SiteLink + "browse.php"; } } + private string ProfileUrl { get { return SiteLink + "my.php"; } } + + new ConfigurationDataBasicLoginWithRSS configData + { + get { return (ConfigurationDataBasicLoginWithRSS)base.configData; } + set { base.configData = value; } + } + + public NxtGn(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps) + : base(name: "NextGen", + description: "A danish closed torrent tracker", + link: "https://nxtgn.org/", + caps: TorznabUtil.CreateDefaultTorznabTVCaps(), + manager: i, + client: c, + logger: l, + p: ps, + configData: new ConfigurationDataBasicLoginWithRSS()) + { + AddCategoryMapping(47, TorznabCatType.Movies3D); + AddCategoryMapping(38, TorznabCatType.MoviesHD); + AddCategoryMapping(38, TorznabCatType.MoviesWEBDL); + AddCategoryMapping(38, TorznabCatType.MoviesBluRay); + AddCategoryMapping(5, TorznabCatType.MoviesSD); + AddCategoryMapping(23, TorznabCatType.MoviesForeign); + AddCategoryMapping(22, TorznabCatType.MoviesSD); + //AddCategoryMapping(4, TorznabCatType.TVFOREIGN); + //AddCategoryMapping(4, TorznabCatType.TVSD); + //AddCategoryMapping(4, TorznabCatType.TVDocumentary); + //AddCategoryMapping(4, TorznabCatType.TVSport); + //AddCategoryMapping(4, TorznabCatType.TV); + //AddCategoryMapping(31, TorznabCatType.TVHD); + //AddCategoryMapping(21, TorznabCatType.TVFOREIGN); + AddCategoryMapping(46, TorznabCatType.TV); + AddCategoryMapping(46, TorznabCatType.TVHD); + //AddCategoryMapping(45, TorznabCatType.TV); + //AddCategoryMapping(45, TorznabCatType.TVSD); + //AddCategoryMapping(24, TorznabCatType.TVFOREIGN); + AddCategoryMapping(26, TorznabCatType.TV); + AddCategoryMapping(26, TorznabCatType.TVHD); + AddCategoryMapping(26, TorznabCatType.TVWEBDL); + AddCategoryMapping(33, TorznabCatType.MoviesHD); + AddCategoryMapping(33, TorznabCatType.Movies); + AddCategoryMapping(17, TorznabCatType.MoviesForeign); + AddCategoryMapping(17, TorznabCatType.MoviesDVD); + AddCategoryMapping(9, TorznabCatType.MoviesHD); + AddCategoryMapping(9, TorznabCatType.Movies); + AddCategoryMapping(9, TorznabCatType.MoviesBluRay); + AddCategoryMapping(43, TorznabCatType.TV); + AddCategoryMapping(43, TorznabCatType.TVHD); + AddCategoryMapping(43, TorznabCatType.TVWEBDL); + } + + public async Task ApplyConfiguration(JToken configJson) + { + var loginPage = await RequestStringWithCookies(LoginUrl); + CQ loginDom = loginPage.Content; + var loginPostUrl = loginDom["#login"].Attr("action"); + + configData.LoadValuesFromJson(configJson); + var pairs = new Dictionary { + { "username", configData.Username.Value }, + { "password", configData.Password.Value } + }; + // Get inital cookies + CookieHeader = string.Empty; + var response = await RequestLoginAndFollowRedirect(SiteLink + loginPostUrl, pairs, CookieHeader, true, null, LoginUrl); + + await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("Velkommen tilbage"), () => + { + CQ dom = response.Content; + var messageEl = dom["inputs"]; + var errorMessage = messageEl.Text().Trim(); + throw new ExceptionWithConfigData(errorMessage, configData); + }); + + var profilePage = await RequestStringWithCookies(ProfileUrl, response.Cookies); + CQ profileDom = profilePage.Content; + var passKey = profileDom["input[name=resetkey]"].Parent().Text(); + passKey = passKey.Substring(0, passKey.IndexOf(' ')); + configData.RSSKey.Value = passKey; + SaveConfig(); + return IndexerConfigurationStatus.RequiresTesting; + } + + public async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + var breakWhile = false; + var page = 0; + while (page < 3) + { + string episodeSearchUrl; + if (string.IsNullOrEmpty(query.GetQueryString())) + { + episodeSearchUrl = SearchUrl + "?page=" + page; + breakWhile = true; + } + else + { + var cats = MapTorznabCapsToTrackers(query); + var catsUrlPart = string.Join("&", cats.Select(c => $"c{c}=1")); + episodeSearchUrl = string.Format("{0}?search={1}&cat=0&incldead=0&{2}&page={3}", SearchUrl, HttpUtility.UrlEncode(query.GetQueryString()), catsUrlPart, page); + } + page++; + var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); + try + { + CQ dom = results.Content; + + var rows = dom["#torrent-table-wrapper > div"]; + + foreach (var row in rows.Skip(1)) + { + var release = new ReleaseInfo(); + + var qRow = row.Cq(); + var qLink = qRow.Find("#torrent-udgivelse2-users > a").First(); + var qDesc = qRow.Find("#torrent-udgivelse2-users > p").FirstOrDefault(); + + var moviesCats = new[] { 47, 38, 5, 23, 22, 33, 17, 9 }; + var seriesCats = new[] { 46, 26, 43 }; + var catUrl = qRow.Find(".torrent-icon > a").Attr("href"); + var cat = catUrl.Substring(catUrl.LastIndexOf('=') + 1); + var catNo = int.Parse(cat); + if (moviesCats.Contains(catNo)) + release.Category = TorznabCatType.Movies.ID; + else if (seriesCats.Contains(catNo)) + release.Category = TorznabCatType.TV.ID; + else + continue; + + releases.Add(release); + + var torrentUrl = qLink.Attr("href"); + var torrentId = torrentUrl.Substring(torrentUrl.LastIndexOf('=') + 1); + + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; + release.Title = qLink.Attr("title"); + release.Description = qDesc != null ? qDesc.InnerText : release.Title; + release.Guid = new Uri(SiteLink + torrentUrl); + release.Comments = new Uri(release.Guid + "#startcomments"); + + var downloadUrl = $"{SiteLink}download.php?id={torrentId}&rss&passkey={configData.RSSKey.Value}"; + release.Link = new Uri(downloadUrl); + + var qAdded = qRow.Find("#torrent-added").First(); + var addedStr = qAdded.Text().Trim(); + release.PublishDate = DateTime.ParseExact(addedStr, "dd-MM-yyyyHH:mm:ss", CultureInfo.InvariantCulture); + + release.Seeders = ParseUtil.CoerceInt(qRow.Find("#torrent-seeders").Text().Trim()); + release.Peers = ParseUtil.CoerceInt(qRow.Find("#torrent-leechers").Text().Trim()) + release.Seeders; + + var sizeStr = qRow.Find("#torrent-size").First().Text(); + release.Size = ReleaseInfo.GetBytes(sizeStr); + + var infoLink = qRow.Find("#infolink"); + var linkContainer = infoLink.Children().First().Children().First(); + var url = linkContainer.Attr("href"); + var img = linkContainer.Children().First(); + var imgUrl = img.Attr("src"); + if (imgUrl == "/pic/imdb.png") + { + release.Imdb = long.Parse(url.Substring(url.LastIndexOf('t') + 1)); + } + else if (imgUrl == "/pic/TV.png") + { + release.TheTvDbId = long.Parse(url.Substring(url.LastIndexOf('=') + 1)); + } + } + var nextPage = dom["#torrent-table-wrapper + p[align=center]"].Children().Last(); + if (!nextPage.Is("a")) + breakWhile = true; + } + catch (Exception ex) + { + OnParseError(results.Content, ex); + } + if (breakWhile) + break; + } + return releases; + } + } +} diff --git a/src/Jackett/Indexers/TorrentDay.cs b/src/Jackett/Indexers/TorrentDay.cs index c67779d83..2943dd772 100644 --- a/src/Jackett/Indexers/TorrentDay.cs +++ b/src/Jackett/Indexers/TorrentDay.cs @@ -1,212 +1,213 @@ -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.Threading.Tasks; -using System.Web; -using Jackett.Models.IndexerConfig; -using System.Collections.Specialized; - -namespace Jackett.Indexers -{ - public class TorrentDay : BaseIndexer, IIndexer - { - private string StartPageUrl { get { return SiteLink + "login.php"; } } - private string LoginUrl { get { return SiteLink + "tak3login.php"; } } - private string SearchUrl { get { return SiteLink + "browse.php"; } } - - new ConfigurationDataRecaptchaLogin configData - { - get { return (ConfigurationDataRecaptchaLogin)base.configData; } - set { base.configData = value; } - } - - public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) - : base(name: "TorrentDay", - description: "TorrentDay", - link: "https://torrentday.eu/", - caps: TorznabUtil.CreateDefaultTorznabTVCaps(), - manager: i, - client: wc, - logger: l, - p: ps, - configData: new ConfigurationDataRecaptchaLogin()) - { - - AddCategoryMapping(29, TorznabCatType.TVAnime); - AddCategoryMapping(28, TorznabCatType.PC); - AddCategoryMapping(28, TorznabCatType.AudioAudiobook); - AddCategoryMapping(20, TorznabCatType.Books); - AddCategoryMapping(30, TorznabCatType.TVDocumentary); - //Freelech - //Mac - - AddCategoryMapping(25, TorznabCatType.MoviesSD); - AddCategoryMapping(11, TorznabCatType.MoviesHD); - AddCategoryMapping(5, TorznabCatType.MoviesHD); - AddCategoryMapping(3, TorznabCatType.MoviesSD); - AddCategoryMapping(21, TorznabCatType.MoviesSD); - AddCategoryMapping(22, TorznabCatType.MoviesForeign); - // Movie packs - AddCategoryMapping(44, TorznabCatType.MoviesSD); - AddCategoryMapping(1, TorznabCatType.MoviesSD); - - // Music foreign - // Music packs - // Music videos - - AddCategoryMapping(4, TorznabCatType.PCGames); - // ps3 - // psp - // wii - // 360 - - AddCategoryMapping(24, TorznabCatType.TVSD); - AddCategoryMapping(32, TorznabCatType.TVHD); - AddCategoryMapping(31, TorznabCatType.TVSD); - AddCategoryMapping(33, TorznabCatType.TVSD); - AddCategoryMapping(14, TorznabCatType.TVHD); - AddCategoryMapping(26, TorznabCatType.TVSD); - AddCategoryMapping(7, TorznabCatType.TVHD); - AddCategoryMapping(2, TorznabCatType.TVSD); - - AddCategoryMapping(6, TorznabCatType.XXX); - AddCategoryMapping(15, TorznabCatType.XXX); - } - - public override async Task GetConfigurationForSetup() - { - var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty); - CQ cq = loginPage.Content; - var result = new ConfigurationDataRecaptchaLogin(); - result.CookieHeader.Value = loginPage.Cookies; - result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey"); - return result; - } - - public async Task ApplyConfiguration(JToken configJson) - { - configData.LoadValuesFromJson(configJson); - var pairs = new Dictionary { - { "username", configData.Username.Value }, - { "password", configData.Password.Value }, - { "g-recaptcha-response", configData.Captcha.Value } - }; - - if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie)) - { - // Cookie was manually supplied - CookieHeader = configData.Captcha.Cookie; - try - { - var results = await PerformQuery(new TorznabQuery()); - if (results.Count() == 0) - { - throw new Exception("Your cookie did not work"); - } - - SaveConfig(); - IsConfigured = true; - } - catch (Exception e) - { - IsConfigured = false; - throw new Exception("Your cookie did not work: " + e.Message); - } - } - - var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, SiteLink, LoginUrl); - await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () => - { - CQ dom = result.Content; - var messageEl = dom["#login"]; - messageEl.Children("form").Remove(); - var errorMessage = messageEl.Text().Trim(); - - if (string.IsNullOrWhiteSpace(errorMessage)) - { - errorMessage = dom.Text(); - } - - throw new ExceptionWithConfigData(errorMessage, configData); - }); - return IndexerConfigurationStatus.RequiresTesting; - } - - public async Task> PerformQuery(TorznabQuery query) - { - var releases = new List(); - var searchString = query.GetQueryString(); - var queryUrl = SearchUrl; - var queryCollection = new NameValueCollection(); - - if (!string.IsNullOrWhiteSpace(searchString)) - queryCollection.Add("search", searchString); - - foreach (var cat in MapTorznabCapsToTrackers(query)) - queryCollection.Add("c" + cat, "1"); - - if (queryCollection.Count > 0) - queryUrl += "?" + queryCollection.GetQueryString(); - - var results = await RequestStringWithCookiesAndRetry(queryUrl); - - // Check for being logged out - if (results.IsRedirect) - throw new AuthenticationException(); - - try - { - CQ dom = results.Content; - var rows = dom["#torrentTable > tbody > tr.browse"]; - foreach (var row in rows) - { - CQ qRow = row.Cq(); - var release = new ReleaseInfo(); - - release.MinimumRatio = 1; - release.MinimumSeedTime = 172800; - release.Title = qRow.Find(".torrentName").Text(); - release.Description = release.Title; - release.Guid = new Uri(SiteLink + qRow.Find(".torrentName").Attr("href")); - release.Comments = release.Guid; - release.Link = new Uri(SiteLink + qRow.Find(".dlLinksInfo > a").Attr("href")); - - var sizeStr = qRow.Find(".sizeInfo").Text(); - release.Size = ReleaseInfo.GetBytes(sizeStr); - - var dateStr = qRow.Find(".ulInfo").Text().Split('|').Last().Trim(); - var agoIdx = dateStr.IndexOf("ago"); - if (agoIdx > -1) - { - dateStr = dateStr.Substring(0, agoIdx); - } - release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); - - release.Seeders = ParseUtil.CoerceInt(qRow.Find(".seedersInfo").Text()); - release.Peers = ParseUtil.CoerceInt(qRow.Find(".leechersInfo").Text()) + release.Seeders; - - var cat = qRow.Find("td:eq(0) a").First().Attr("href").Substring(15);//browse.php?cat=24 - release.Category = MapTrackerCatToNewznab(cat); - - releases.Add(release); - } - } - catch (Exception ex) - { - OnParseError(results.Content, ex); - } - return releases; - } - } -} +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.Threading.Tasks; +using System.Web; +using Jackett.Models.IndexerConfig; +using System.Collections.Specialized; + +namespace Jackett.Indexers +{ + public class TorrentDay : BaseIndexer, IIndexer + { + private string StartPageUrl { get { return SiteLink + "login.php"; } } + private string LoginUrl { get { return SiteLink + "tak3login.php"; } } + private string SearchUrl { get { return SiteLink + "browse.php"; } } + + new ConfigurationDataRecaptchaLogin configData + { + get { return (ConfigurationDataRecaptchaLogin)base.configData; } + set { base.configData = value; } + } + + public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) + : base(name: "TorrentDay", + description: "TorrentDay", + link: "https://torrentday.eu/", + caps: TorznabUtil.CreateDefaultTorznabTVCaps(), + manager: i, + client: wc, + logger: l, + p: ps, + configData: new ConfigurationDataRecaptchaLogin()) + { + + AddCategoryMapping(29, TorznabCatType.TVAnime); + AddCategoryMapping(28, TorznabCatType.PC); + AddCategoryMapping(28, TorznabCatType.AudioAudiobook); + AddCategoryMapping(20, TorznabCatType.Books); + AddCategoryMapping(30, TorznabCatType.TVDocumentary); + //Freelech + //Mac + + AddCategoryMapping(25, TorznabCatType.MoviesSD); + AddCategoryMapping(11, TorznabCatType.MoviesHD); + AddCategoryMapping(5, TorznabCatType.MoviesHD); + AddCategoryMapping(3, TorznabCatType.MoviesSD); + AddCategoryMapping(21, TorznabCatType.MoviesSD); + AddCategoryMapping(22, TorznabCatType.MoviesForeign); + // Movie packs + AddCategoryMapping(44, TorznabCatType.MoviesSD); + AddCategoryMapping(1, TorznabCatType.MoviesSD); + + // Music foreign + // Music packs + // Music videos + + AddCategoryMapping(4, TorznabCatType.PCGames); + // ps3 + // psp + // wii + // 360 + + AddCategoryMapping(24, TorznabCatType.TVSD); + AddCategoryMapping(32, TorznabCatType.TVHD); + AddCategoryMapping(31, TorznabCatType.TVSD); + AddCategoryMapping(33, TorznabCatType.TVSD); + AddCategoryMapping(14, TorznabCatType.TVHD); + AddCategoryMapping(26, TorznabCatType.TVSD); + AddCategoryMapping(7, TorznabCatType.TVHD); + AddCategoryMapping(2, TorznabCatType.TVSD); + + AddCategoryMapping(6, TorznabCatType.XXX); + AddCategoryMapping(15, TorznabCatType.XXX); + } + + public override async Task GetConfigurationForSetup() + { + var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty); + CQ cq = loginPage.Content; + var result = new ConfigurationDataRecaptchaLogin(); + result.CookieHeader.Value = loginPage.Cookies; + result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey"); + return result; + } + + public async Task ApplyConfiguration(JToken configJson) + { + configData.LoadValuesFromJson(configJson); + var pairs = new Dictionary { + { "username", configData.Username.Value }, + { "password", configData.Password.Value }, + { "g-recaptcha-response", configData.Captcha.Value } + }; + + if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie)) + { + // Cookie was manually supplied + CookieHeader = configData.Captcha.Cookie; + try + { + var results = await PerformQuery(new TorznabQuery()); + if (results.Count() == 0) + { + throw new Exception("Your cookie did not work"); + } + + SaveConfig(); + IsConfigured = true; + return IndexerConfigurationStatus.Completed; + } + catch (Exception e) + { + IsConfigured = false; + throw new Exception("Your cookie did not work: " + e.Message); + } + } + + var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, SiteLink, LoginUrl); + await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () => + { + CQ dom = result.Content; + var messageEl = dom["#login"]; + messageEl.Children("form").Remove(); + var errorMessage = messageEl.Text().Trim(); + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + errorMessage = dom.Text(); + } + + throw new ExceptionWithConfigData(errorMessage, configData); + }); + return IndexerConfigurationStatus.RequiresTesting; + } + + public async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + var searchString = query.GetQueryString(); + var queryUrl = SearchUrl; + var queryCollection = new NameValueCollection(); + + if (!string.IsNullOrWhiteSpace(searchString)) + queryCollection.Add("search", searchString); + + foreach (var cat in MapTorznabCapsToTrackers(query)) + queryCollection.Add("c" + cat, "1"); + + if (queryCollection.Count > 0) + queryUrl += "?" + queryCollection.GetQueryString(); + + var results = await RequestStringWithCookiesAndRetry(queryUrl); + + // Check for being logged out + if (results.IsRedirect) + throw new AuthenticationException(); + + try + { + CQ dom = results.Content; + var rows = dom["#torrentTable > tbody > tr.browse"]; + foreach (var row in rows) + { + CQ qRow = row.Cq(); + var release = new ReleaseInfo(); + + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; + release.Title = qRow.Find(".torrentName").Text(); + release.Description = release.Title; + release.Guid = new Uri(SiteLink + qRow.Find(".torrentName").Attr("href")); + release.Comments = release.Guid; + release.Link = new Uri(SiteLink + qRow.Find(".dlLinksInfo > a").Attr("href")); + + var sizeStr = qRow.Find(".sizeInfo").Text(); + release.Size = ReleaseInfo.GetBytes(sizeStr); + + var dateStr = qRow.Find(".ulInfo").Text().Split('|').Last().Trim(); + var agoIdx = dateStr.IndexOf("ago"); + if (agoIdx > -1) + { + dateStr = dateStr.Substring(0, agoIdx); + } + release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr); + + release.Seeders = ParseUtil.CoerceInt(qRow.Find(".seedersInfo").Text()); + release.Peers = ParseUtil.CoerceInt(qRow.Find(".leechersInfo").Text()) + release.Seeders; + + var cat = qRow.Find("td:eq(0) a").First().Attr("href").Substring(15);//browse.php?cat=24 + release.Category = MapTrackerCatToNewznab(cat); + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results.Content, ex); + } + return releases; + } + } +} diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index b84a20994..f01f8b875 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -1,609 +1,617 @@ - - - - - Debug - AnyCPU - {E636D5F8-68B4-4903-B4ED-CCFD9C9E899F} - Library - Properties - Jackett - Jackett - v4.5 - 512 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll - True - - - ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll - True - - - ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll - True - - - ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll - True - - - ..\packages\AutoMapper.4.0.4\lib\net45\AutoMapper.dll - True - - - ..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll - True - - - ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll - True - - - ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll - True - - - ..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll - True - - - ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll - True - - - ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll - True - - - ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll - True - - - ..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll - True - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\packages\NLog.4.0.1\lib\net45\NLog.dll - True - - - ..\packages\NLog.Windows.Forms.2.0.0.0\lib\net35\NLog.Windows.Forms.dll - True - - - ..\packages\Owin.1.0\lib\net40\Owin.dll - True - - - - - - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - True - - - - - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - TorznabCatType.tt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - - - - - - - - - - - - - - Designer - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - Always - - - Designer - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - TextTemplatingFileGenerator - TorznabCatType.generated.cs - - - - PreserveNewest - - - - - {74420A79-CC16-442C-8B1E-7C1B913844F0} - CurlSharp - - - - - False - Microsoft .NET Framework 4.5.1 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - + + + + + Debug + AnyCPU + {E636D5F8-68B4-4903-B4ED-CCFD9C9E899F} + Library + Properties + Jackett + Jackett + v4.5 + 512 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll + True + + + ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll + True + + + ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll + True + + + ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll + True + + + ..\packages\AutoMapper.4.0.4\lib\net45\AutoMapper.dll + True + + + ..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll + True + + + ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll + True + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll + True + + + ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll + True + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + True + + + ..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll + True + + + ..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll + True + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\NLog.4.0.1\lib\net45\NLog.dll + True + + + ..\packages\NLog.Windows.Forms.2.0.0.0\lib\net35\NLog.Windows.Forms.dll + True + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + + + + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + True + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll + True + + + + + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + TorznabCatType.tt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + + + + + + + + + + + + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + Always + + + Designer + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + TextTemplatingFileGenerator + TorznabCatType.generated.cs + + + + PreserveNewest + + + + + {74420A79-CC16-442C-8B1E-7C1B913844F0} + CurlSharp + + + + + False + Microsoft .NET Framework 4.5.1 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + --> + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/src/Jackett/JackettModule.cs b/src/Jackett/JackettModule.cs index cccbbe192..988b61238 100644 --- a/src/Jackett/JackettModule.cs +++ b/src/Jackett/JackettModule.cs @@ -1,81 +1,82 @@ -using Autofac; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Autofac.Integration.WebApi; -using Jackett.Indexers; -using Jackett.Utils; -using Jackett.Utils.Clients; -using AutoMapper; -using Jackett.Models; - -namespace Jackett -{ - public class JackettModule : Module - { - protected override void Load(ContainerBuilder builder) - { - // Just register everything! - var thisAssembly = typeof(JackettModule).Assembly; - builder.RegisterAssemblyTypes(thisAssembly).Except().AsImplementedInterfaces().SingleInstance(); - builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); - - // Register the best web client for the platform or the override - switch (Startup.ClientOverride) - { - case "httpclient": - builder.RegisterType().As(); - break; - case "safecurl": - builder.RegisterType().As(); - break; - case "libcurl": - builder.RegisterType().As(); - break; - case "automatic": - default: - if (System.Environment.OSVersion.Platform == PlatformID.Unix) - { - builder.RegisterType().As(); - } - else - { - builder.RegisterType().As(); - } - break; - } - - // Register indexers - foreach (var indexer in thisAssembly.GetTypes() - .Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface) - .ToArray()) - { - builder.RegisterType(indexer).Named(BaseIndexer.GetIndexerID(indexer)); - } - - Mapper.CreateMap().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((be, str) => - { - str.Content = Encoding.UTF8.GetString(be.Content); - }); - - Mapper.CreateMap().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((str, be) => - { - if (!string.IsNullOrEmpty(str.Content)) - { - be.Content = Encoding.UTF8.GetBytes(str.Content); - } - }); - - Mapper.CreateMap(); - Mapper.CreateMap(); - Mapper.CreateMap(); - - Mapper.CreateMap().AfterMap((r, t) => - { - t.CategoryDesc = TorznabCatType.GetCatDesc(r.Category); - }); - } - } -} +using Autofac; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Autofac.Integration.WebApi; +using Jackett.Indexers; +using Jackett.Utils; +using Jackett.Utils.Clients; +using AutoMapper; +using Jackett.Models; + +namespace Jackett +{ + public class JackettModule : Module + { + protected override void Load(ContainerBuilder builder) + { + // Just register everything! + var thisAssembly = typeof(JackettModule).Assembly; + builder.RegisterAssemblyTypes(thisAssembly).Except().AsImplementedInterfaces().SingleInstance(); + builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); + builder.RegisterType(); + + // Register the best web client for the platform or the override + switch (Startup.ClientOverride) + { + case "httpclient": + builder.RegisterType().As(); + break; + case "safecurl": + builder.RegisterType().As(); + break; + case "libcurl": + builder.RegisterType().As(); + break; + case "automatic": + default: + if (System.Environment.OSVersion.Platform == PlatformID.Unix) + { + builder.RegisterType().As(); + } + else + { + builder.RegisterType().As(); + } + break; + } + + // Register indexers + foreach (var indexer in thisAssembly.GetTypes() + .Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface) + .ToArray()) + { + builder.RegisterType(indexer).Named(BaseIndexer.GetIndexerID(indexer)); + } + + Mapper.CreateMap().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((be, str) => + { + str.Content = Encoding.UTF8.GetString(be.Content); + }); + + Mapper.CreateMap().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((str, be) => + { + if (!string.IsNullOrEmpty(str.Content)) + { + be.Content = Encoding.UTF8.GetBytes(str.Content); + } + }); + + Mapper.CreateMap(); + Mapper.CreateMap(); + Mapper.CreateMap(); + + Mapper.CreateMap().AfterMap((r, t) => + { + t.CategoryDesc = TorznabCatType.GetCatDesc(r.Category); + }); + } + } +} diff --git a/src/Jackett/Models/ReleaseInfo.cs b/src/Jackett/Models/ReleaseInfo.cs index 4c3318075..8484ab651 100644 --- a/src/Jackett/Models/ReleaseInfo.cs +++ b/src/Jackett/Models/ReleaseInfo.cs @@ -21,6 +21,7 @@ namespace Jackett.Models public long? Size { get; set; } public string Description { get; set; } public long? RageID { get; set; } + public long? TheTvDbId { get; set; } public long? Imdb { get; set; } public int? Seeders { get; set; } public int? Peers { get; set; } diff --git a/src/Jackett/Models/ResultPage.cs b/src/Jackett/Models/ResultPage.cs index 7593ea0b6..4b70cb29b 100644 --- a/src/Jackett/Models/ResultPage.cs +++ b/src/Jackett/Models/ResultPage.cs @@ -81,6 +81,8 @@ namespace Jackett.Models ), getTorznabElement("magneturl", r.MagnetUri), getTorznabElement("rageid", r.RageID), + getTorznabElement("thetvdb", r.TheTvDbId), + getTorznabElement("imdb", r.Imdb), getTorznabElement("seeders", r.Seeders), getTorznabElement("peers", r.Peers), getTorznabElement("infohash", r.InfoHash), diff --git a/src/Jackett/Models/TorrentPotatoResponseItem.cs b/src/Jackett/Models/TorrentPotatoResponseItem.cs index 809ada2b6..9c860610a 100644 --- a/src/Jackett/Models/TorrentPotatoResponseItem.cs +++ b/src/Jackett/Models/TorrentPotatoResponseItem.cs @@ -12,7 +12,7 @@ namespace Jackett.Models public string torrent_id { get; set; } public string details_url { get; set; } public string download_url { get; set; } - // public string imdb_id { get; set; } + public string imdb_id { get; set; } public bool freeleech { get; set; } public string type { get; set; } public long size { get; set; } diff --git a/src/Jackett/Properties/AssemblyInfo.cs b/src/Jackett/Properties/AssemblyInfo.cs index 6e876de18..879a02a76 100644 --- a/src/Jackett/Properties/AssemblyInfo.cs +++ b/src/Jackett/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Jackett")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Jackett")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5881fb69-3cb2-42b7-a744-2c1e537176bd")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.6.3.0")] -[assembly: AssemblyFileVersion("0.6.3.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jackett")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Jackett")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5881fb69-3cb2-42b7-a744-2c1e537176bd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.6.4.0")] +[assembly: AssemblyFileVersion("0.6.4.0")] diff --git a/src/Jackett/Utils/Clients/HttpWebClient.cs b/src/Jackett/Utils/Clients/HttpWebClient.cs index 89e7eed01..5387dd932 100644 --- a/src/Jackett/Utils/Clients/HttpWebClient.cs +++ b/src/Jackett/Utils/Clients/HttpWebClient.cs @@ -1,110 +1,118 @@ -using AutoMapper; -using Jackett.Models; -using NLog; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; - -namespace Jackett.Utils.Clients -{ - class HttpWebClient : IWebClient - { - private Logger logger; - - public HttpWebClient(Logger l) - { - logger = l; - } - - - public void Init() - { - } - - public async Task GetBytes(WebRequest request) - { - logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url)); - var result = await Run(request); - logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "" : result.Content.Length.ToString()))); - return result; - } - - public async Task GetString(WebRequest request) - { - logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url)); - var result = await Run(request); - logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "" : Encoding.UTF8.GetString(result.Content)))); - return Mapper.Map(result); - } - - private async Task Run(WebRequest request) - { - var cookies = new CookieContainer(); - if (!string.IsNullOrEmpty(request.Cookies)) - { - var uri = new Uri(request.Url); - foreach (var c in request.Cookies.Split(';')) - { - try - { - cookies.SetCookies(uri, c); - } - catch (CookieException ex) - { - logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); - } - } - } - - var client = new HttpClient(new HttpClientHandler - { - CookieContainer = cookies, - AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more. - UseCookies = true, - }); - - client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); - HttpResponseMessage response = null; - - if (request.Type == RequestType.POST) - { - var content = new FormUrlEncodedContent(request.PostData); - response = await client.PostAsync(request.Url, content); - } - else - { - response = await client.GetAsync(request.Url); - } - - var result = new WebClientByteResult(); - result.Content = await response.Content.ReadAsByteArrayAsync(); - if (response.Headers.Location != null) - { - result.RedirectingTo = response.Headers.Location.ToString(); - } - result.Status = response.StatusCode; - - // Compatiblity issue between the cookie format and httpclient - // Pull it out manually ignoring the expiry date then set it manually - // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer - IEnumerable cookieHeaders; - if (response.Headers.TryGetValues("set-cookie", out cookieHeaders)) - { - var cookieBuilder = new StringBuilder(); - foreach (var c in cookieHeaders) - { - cookieBuilder.AppendFormat("{0} ", c.Substring(0, c.IndexOf(';') + 1)); - } - - result.Cookies = cookieBuilder.ToString().TrimEnd(); - } - - ServerUtil.ResureRedirectIsFullyQualified(request, result); - return result; - } - } -} +using AutoMapper; +using Jackett.Models; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Utils.Clients +{ + public class HttpWebClient : IWebClient + { + private Logger logger; + + public HttpWebClient(Logger l) + { + logger = l; + } + + + public void Init() + { + } + + public async Task GetBytes(WebRequest request) + { + logger.Debug(string.Format("WindowsWebClient:GetBytes(Url:{0})", request.Url)); + var result = await Run(request); + logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1} bytes", result.Status, (result.Content == null ? "" : result.Content.Length.ToString()))); + return result; + } + + public async Task GetString(WebRequest request) + { + logger.Debug(string.Format("WindowsWebClient:GetString(Url:{0})", request.Url)); + var result = await Run(request); + logger.Debug(string.Format("WindowsWebClient: Returning {0} => {1}", result.Status, (result.Content == null ? "" : Encoding.UTF8.GetString(result.Content)))); + return Mapper.Map(result); + } + + private async Task Run(WebRequest request) + { + var cookies = new CookieContainer(); + if (!string.IsNullOrEmpty(request.Cookies)) + { + var uri = new Uri(request.Url); + foreach (var c in request.Cookies.Split(';')) + { + try + { + cookies.SetCookies(uri, c); + } + catch (CookieException ex) + { + logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); + } + } + } + + var client = new HttpClient(new HttpClientHandler + { + CookieContainer = cookies, + AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more. + UseCookies = true, + }); + + client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); + HttpResponseMessage response = null; + + if (request.Headers != null) + { + foreach (var header in request.Headers) + { + client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + } + + if (request.Type == RequestType.POST) + { + var content = new FormUrlEncodedContent(request.PostData); + response = await client.PostAsync(request.Url, content); + } + else + { + response = await client.GetAsync(request.Url); + } + + var result = new WebClientByteResult(); + result.Content = await response.Content.ReadAsByteArrayAsync(); + if (response.Headers.Location != null) + { + result.RedirectingTo = response.Headers.Location.ToString(); + } + result.Status = response.StatusCode; + + // Compatiblity issue between the cookie format and httpclient + // Pull it out manually ignoring the expiry date then set it manually + // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer + IEnumerable cookieHeaders; + if (response.Headers.TryGetValues("set-cookie", out cookieHeaders)) + { + var cookieBuilder = new StringBuilder(); + foreach (var c in cookieHeaders) + { + cookieBuilder.AppendFormat("{0} ", c.Substring(0, c.IndexOf(';') + 1)); + } + + result.Cookies = cookieBuilder.ToString().TrimEnd(); + } + + ServerUtil.ResureRedirectIsFullyQualified(request, result); + return result; + } + } +} diff --git a/src/Jackett/Utils/Clients/WebRequest.cs b/src/Jackett/Utils/Clients/WebRequest.cs index 2d8862b18..a9ae22d6e 100644 --- a/src/Jackett/Utils/Clients/WebRequest.cs +++ b/src/Jackett/Utils/Clients/WebRequest.cs @@ -1,89 +1,95 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; - -namespace Jackett.Utils.Clients -{ - public class WebRequest - { - public WebRequest() - { - PostData = new List>(); - Type = RequestType.GET; - } - - public WebRequest(string url) - { - PostData = new List>(); - Type = RequestType.GET; - Url = url; - } - - public string Url { get; set; } - public IEnumerable> PostData { get; set; } - public string Cookies { get; set; } - public string Referer { get; set; } - public RequestType Type { get; set; } - - - public override bool Equals(System.Object obj) - { - if (obj is WebRequest) - { - var other = obj as WebRequest; - var postDataSame = PostData == null && other.PostData == null; - if (!postDataSame) - { - if (!(PostData == null || other.PostData == null)) - { - var ok = PostData.Count() == other.PostData.Count(); - foreach (var i in PostData) - { - if (!other.PostData.Any(item => item.Key == i.Key)) - { - ok = false; - break; - } - - if (PostData.FirstOrDefault(item => item.Key == i.Key).Value != other.PostData.FirstOrDefault(item => item.Key == i.Key).Value) - { - ok = false; - break; - } - } - - if (ok) - { - postDataSame = true; - } - } - } - - return other.Url == Url && - other.Referer == Referer && - other.Cookies == Cookies && - other.Type == Type && - postDataSame; - - } - else - { - return false; - } - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } - - public enum RequestType - { - GET, - POST - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Utils.Clients +{ + public class WebRequest + { + public WebRequest() + { + PostData = new List>(); + Type = RequestType.GET; + Headers = new Dictionary(); + } + + public WebRequest(string url) + { + PostData = new List>(); + Type = RequestType.GET; + Url = url; + Headers = new Dictionary(); + } + + public string Url { get; set; } + public IEnumerable> PostData { get; set; } + public string Cookies { get; set; } + public string Referer { get; set; } + public RequestType Type { get; set; } + + /// + /// Warning this is only implemented on HTTPWebClient currently! + /// + public Dictionary Headers { get; set; } + + public override bool Equals(System.Object obj) + { + if (obj is WebRequest) + { + var other = obj as WebRequest; + var postDataSame = PostData == null && other.PostData == null; + if (!postDataSame) + { + if (!(PostData == null || other.PostData == null)) + { + var ok = PostData.Count() == other.PostData.Count(); + foreach (var i in PostData) + { + if (!other.PostData.Any(item => item.Key == i.Key)) + { + ok = false; + break; + } + + if (PostData.FirstOrDefault(item => item.Key == i.Key).Value != other.PostData.FirstOrDefault(item => item.Key == i.Key).Value) + { + ok = false; + break; + } + } + + if (ok) + { + postDataSame = true; + } + } + } + + return other.Url == Url && + other.Referer == Referer && + other.Cookies == Cookies && + other.Type == Type && + postDataSame; + + } + else + { + return false; + } + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public enum RequestType + { + GET, + POST + } +} diff --git a/src/Jackett/Utils/JsonContent.cs b/src/Jackett/Utils/JsonContent.cs index 412962bda..17132f029 100644 --- a/src/Jackett/Utils/JsonContent.cs +++ b/src/Jackett/Utils/JsonContent.cs @@ -25,7 +25,7 @@ namespace Jackett.Utils protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) { - var json = JsonConvert.SerializeObject(_value, Formatting.Indented); + var json = JsonConvert.SerializeObject(_value, Formatting.Indented, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); var writer = new StreamWriter(stream); writer.Write(json); await writer.FlushAsync(); diff --git a/src/Jackett/Utils/TorznabCapsUtil.cs b/src/Jackett/Utils/TorznabCapsUtil.cs index 6fc28e8b5..954355d33 100644 --- a/src/Jackett/Utils/TorznabCapsUtil.cs +++ b/src/Jackett/Utils/TorznabCapsUtil.cs @@ -1,53 +1,86 @@ -using Jackett.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace Jackett.Utils -{ - public class TorznabUtil - { - static Regex reduceSpacesRegex = new Regex("\\s{2,}", RegexOptions.Compiled); - - public static TorznabCapabilities CreateDefaultTorznabTVCaps() - { - var caps = new TorznabCapabilities(); - caps.Categories.AddRange(new[] { - TorznabCatType.TV, - TorznabCatType.TVSD, - TorznabCatType.TVHD - }); - return caps; - } - - public static IEnumerable FilterResultsToTitle(IEnumerable results, string name, int year) - { - if (string.IsNullOrWhiteSpace(name)) - return results; - - name = CleanTitle(name); - var filteredResults = new List(); - foreach (var result in results) - { - if (result.Title == null) - continue; - if (CleanTitle(result.Title).Contains(name) && - (year ==0 || result.Title.Contains(year.ToString()))) - { - filteredResults.Add(result); - } - } - - return filteredResults; - } - - private static string CleanTitle(string title) - { - title = title.Replace(':', ' ').Replace('.', ' ').Replace('-', ' ').Replace('_', ' ').Replace('+', ' '); - return reduceSpacesRegex.Replace(title, " ").ToLowerInvariant(); - } - } -} +using Jackett.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Jackett.Utils +{ + public class TorznabUtil + { + static Regex reduceSpacesRegex = new Regex("\\s{2,}", RegexOptions.Compiled); + + static Regex findYearRegex = new Regex(@"(?<=\[|\(|\s)(\d{4})(?=\]|\)|\s)", RegexOptions.Compiled); + + public static TorznabCapabilities CreateDefaultTorznabTVCaps() + { + var caps = new TorznabCapabilities(); + caps.Categories.AddRange(new[] { + TorznabCatType.TV, + TorznabCatType.TVSD, + TorznabCatType.TVHD + }); + return caps; + } + + private static int GetYearFromTitle(string title) + { + var match = findYearRegex.Match(title); + if (match.Success) + { + var year = ParseUtil.CoerceInt(match.Value); + if(year>1850 && year < 2100) + { + return year; + } + } + + return 0; + } + + public static IEnumerable FilterResultsToTitle(IEnumerable results, string name, int imdbYear) + { + if (string.IsNullOrWhiteSpace(name)) + return results; + + name = CleanTitle(name); + var filteredResults = new List(); + foreach (var result in results) + { + if (result.Title == null) + continue; + + // Match on title + if (CleanTitle(result.Title).Contains(name)) + { + // Match on year + var titleYear = GetYearFromTitle(result.Title); + if (imdbYear == 0 || titleYear == 0 || titleYear == imdbYear) + { + filteredResults.Add(result); + } + } + } + + return filteredResults; + } + + public static IEnumerable FilterResultsToImdb(IEnumerable results, string imdb) + { + if (string.IsNullOrWhiteSpace(imdb)) + return results; + // Filter out releases that do have a valid imdb ID, that is not equal to the one we're searching for. + return + results.Where( + result => !result.Imdb.HasValue || result.Imdb.Value == 0 || ("tt" + result.Imdb.Value).Equals(imdb)); + } + + private static string CleanTitle(string title) + { + title = title.Replace(':', ' ').Replace('.', ' ').Replace('-', ' ').Replace('_', ' ').Replace('+', ' ').Replace("'", ""); + return reduceSpacesRegex.Replace(title, " ").ToLowerInvariant(); + } + } +} diff --git a/src/Website/Views/Home/Index.cshtml b/src/Website/Views/Home/Index.cshtml index 17e7def08..0e93e5864 100644 --- a/src/Website/Views/Home/Index.cshtml +++ b/src/Website/Views/Home/Index.cshtml @@ -1,58 +1,58 @@ -@{ - ViewBag.Title = "Home Page"; -} - -

What is Jackett?

-

- Jackett creates a Torznab (with nZEDb category numbering) and TorrentPotato API server on your machine. This enables supporting software to access your favorite torrent indexers in a similar fashion to rss but with added features such as searching. To see the list of the trackers we support see our Github page. -

- - -

We are on Github

-
-
-
-
- Follow development, report issues and contribute on our Github page. -
-

- -
-
-
- Github -
- -
-
-

Applications with support for Jackett

- -
-
- - Sonarr - -
-
- - Couchpotato - -
-
- - Mylar - -
+@{ + ViewBag.Title = "Home Page"; +} + +

What is Jackett?

+

+ Jackett creates a Torznab (with nZEDb category numbering) and TorrentPotato API server on your machine. This enables supporting software to access your favorite torrent indexers in a similar fashion to rss but with added features such as searching. To see the list of the trackers we support see our Github page. +

+ + +

We are on Github

+
+
+
+
+ Follow development, report issues and contribute on our Github page. +
+

+ +
+
+
+ Github +
+ +
+
+

Applications with support for Jackett

+ +
+
+ + Sonarr + +
+
+ + Couchpotato + +
+
+ + Mylar + +
\ No newline at end of file diff --git a/src/Website/Views/Shared/_Layout.cshtml b/src/Website/Views/Shared/_Layout.cshtml index d7373becc..bf531c4bf 100644 --- a/src/Website/Views/Shared/_Layout.cshtml +++ b/src/Website/Views/Shared/_Layout.cshtml @@ -1,48 +1,52 @@ - - - - - - - - - - - - - - - - - - - - Jackett - - -
- - Jackett - -
- - @RenderBody() -
- -
- -
- - - - - + + + + + + + + + + + + + + + + + + + + + + + + Jackett Torznab Indexer + + +
+ + Jackett + +
+ + @RenderBody() +
+ +
+ +
+ + + + + \ No newline at end of file