From 43d188c75171f04ca00876fc95864a20c2903e48 Mon Sep 17 00:00:00 2001 From: Kayomani Date: Thu, 30 Jul 2015 21:09:38 +0100 Subject: [PATCH] Implement torrentbytes --- .gitignore | 2 + src/Jackett/Content/logos/torrentbytes.png | Bin 0 -> 5450 bytes src/Jackett/Controllers/APIController.cs | 2 +- src/Jackett/Indexers/IPTorrents.cs | 2 +- src/Jackett/Indexers/TorrentBytes.cs | 179 +++++++++++++++++++++ src/Jackett/Jackett.csproj | 4 + src/Jackett/Models/TorznabCatType.cs | 24 +++ src/Jackett/Utils/StringUtil.cs | 6 + 8 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/Jackett/Content/logos/torrentbytes.png create mode 100644 src/Jackett/Indexers/TorrentBytes.cs diff --git a/.gitignore b/.gitignore index 57a1574c4..eb5a9e709 100644 --- a/.gitignore +++ b/.gitignore @@ -194,3 +194,5 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +/Build.mono +/Build.windows diff --git a/src/Jackett/Content/logos/torrentbytes.png b/src/Jackett/Content/logos/torrentbytes.png new file mode 100644 index 0000000000000000000000000000000000000000..245c86aeac2555bdecb545223b5af6c9ce441b1b GIT binary patch literal 5450 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 A$N&HU literal 0 HcmV?d00001 diff --git a/src/Jackett/Controllers/APIController.cs b/src/Jackett/Controllers/APIController.cs index 064bdc133..836aad9bc 100644 --- a/src/Jackett/Controllers/APIController.cs +++ b/src/Jackett/Controllers/APIController.cs @@ -78,7 +78,7 @@ namespace Jackett.Controllers } else { - logger.Info(string.Format("Found {0} releases from {1} for: {2}", releases.Count(), indexer.DisplayName, torznabQuery.SanitizedSearchTerm)); + logger.Info(string.Format("Found {0} releases from {1} for: {2} {3}", releases.Count(), indexer.DisplayName, torznabQuery.SanitizedSearchTerm, torznabQuery.GetEpisodeSearchString())); } var severUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); diff --git a/src/Jackett/Indexers/IPTorrents.cs b/src/Jackett/Indexers/IPTorrents.cs index 165fe845b..7e405ae06 100644 --- a/src/Jackett/Indexers/IPTorrents.cs +++ b/src/Jackett/Indexers/IPTorrents.cs @@ -128,7 +128,7 @@ namespace Jackett.Indexers if (queryCollection.Count > 0) { - searchUrl += "?" + string.Join("&", queryCollection.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(queryCollection[a]))); + searchUrl += "?" + queryCollection.GetQueryString(); } var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); diff --git a/src/Jackett/Indexers/TorrentBytes.cs b/src/Jackett/Indexers/TorrentBytes.cs new file mode 100644 index 000000000..f9fc9c7ab --- /dev/null +++ b/src/Jackett/Indexers/TorrentBytes.cs @@ -0,0 +1,179 @@ +using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Jackett.Utils.Clients; +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Jackett.Indexers +{ + public class TorrentBytes : BaseIndexer, IIndexer + { + private string BrowseUrl { get { return SiteLink + "browse.php"; } } + private string LoginUrl { get { return SiteLink + "takelogin.php"; } } + + public TorrentBytes(IIndexerManagerService i, IWebClient wc, Logger l) + : base(name: "TorrentBytes", + description: "A decade of torrentbytes", + link: "https://www.torrentbytes.net/", + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + client: wc, + logger: l) + { + + AddCategoryMapping(41, TorznabCatType.TV); + AddCategoryMapping(33, TorznabCatType.TVSD); + AddCategoryMapping(38, TorznabCatType.TVHD); + AddCategoryMapping(32, TorznabCatType.TVSD); + AddCategoryMapping(37, TorznabCatType.TVSD); + AddCategoryMapping(44, TorznabCatType.TVSD); + + AddCategoryMapping(40, TorznabCatType.Movies); + AddCategoryMapping(19, TorznabCatType.MoviesSD); + AddCategoryMapping(5, TorznabCatType.MoviesHD); + AddCategoryMapping(20, TorznabCatType.MoviesSD); + AddCategoryMapping(28, TorznabCatType.MoviesForeign); + AddCategoryMapping(45, TorznabCatType.MoviesSD); + + AddCategoryMapping(43, TorznabCatType.Audio); + AddCategoryMapping(48, TorznabCatType.AudioLossless); + AddCategoryMapping(6, TorznabCatType.AudioLossy); + AddCategoryMapping(46, TorznabCatType.Movies); + + AddCategoryMapping(1, TorznabCatType.Apps); + AddCategoryMapping(2, TorznabCatType.Apps); + AddCategoryMapping(23, TorznabCatType.Anime); + AddCategoryMapping(21, TorznabCatType.XXX); + AddCategoryMapping(9, TorznabCatType.XXXSD); + AddCategoryMapping(39, TorznabCatType.XXXHD); + AddCategoryMapping(29, TorznabCatType.XXXSD); + AddCategoryMapping(24, TorznabCatType.XXXImg); + } + + public Task GetConfigurationForSetup() + { + return Task.FromResult(new ConfigurationDataBasicLogin()); + } + + public async Task ApplyConfiguration(JToken configJson) + { + var incomingConfig = new ConfigurationDataBasicLogin(); + incomingConfig.LoadValuesFromJson(configJson); + var pairs = new Dictionary { + { "username", incomingConfig.Username.Value }, + { "password", incomingConfig.Password.Value }, + { "returnto", "/" }, + { "login", "Log in!" } + }; + + var loginPage = await RequestStringWithCookies(SiteLink, string.Empty); + + var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SiteLink, SiteLink); + ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () => + { + CQ dom = result.Content; + var messageEl = dom["body > div"].First(); + var errorMessage = messageEl.Text().Trim(); + throw new ExceptionWithConfigData(errorMessage, (ConfigurationData)incomingConfig); + }); + } + + public async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + var searchString = query.SanitizedSearchTerm + " " + query.GetEpisodeSearchString(); + var searchUrl = BrowseUrl; + var trackerCats = MapTorznabCapsToTrackers(query); + var queryCollection = new NameValueCollection(); + + // Tracker can only search OR return things in categories + if (!string.IsNullOrWhiteSpace(searchString)) + { + queryCollection.Add("search", searchString); + queryCollection.Add("cat", "0"); + queryCollection.Add("sc", "1"); + } + else + { + foreach (var cat in MapTorznabCapsToTrackers(query)) + { + queryCollection.Add("c" + cat, "1"); + } + + queryCollection.Add("incldead", "0"); + } + + searchUrl += "?" + queryCollection.GetQueryString(); + + // 15 results per page - really don't want to call the server twice but only 15 results per page is a bit crap! + await ProcessPage(releases, searchUrl); + await ProcessPage(releases, searchUrl + "&page=1"); + + return releases; + } + + private async Task ProcessPage(List releases, string searchUrl) + { + + var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl); + + var results = response.Content; + try + { + CQ dom = results; + + var rows = dom["#content table:eq(4) tr"]; + foreach (var row in rows.Skip(1)) + { + var release = new ReleaseInfo(); + + var link = row.Cq().Find("td:eq(1) a:eq(1)").First(); + release.Guid = new Uri(SiteLink + link.Attr("href")); + release.Comments = release.Guid; + release.Title = link.Text().Trim(); + release.Description = release.Title; + + // If we search an get no results, we still get a table just with no info. + if (string.IsNullOrWhiteSpace(release.Title)) + { + break; + } + + var cat = row.Cq().Find("td:eq(0) a").First().Attr("href").Substring(15); + release.Category = MapTrackerCatToNewznab(cat); + + + var qLink = row.Cq().Find("td:eq(1) a").First(); + release.Link = new Uri(SiteLink + qLink.Attr("href")); + + var added = row.Cq().Find("td:eq(4)").First().Text().Trim(); + release.PublishDate = DateTimeUtil.FromTimeAgo(added); + + var sizeStr = row.Cq().Find("td:eq(6)").First().Text().Trim(); + release.Size = ReleaseInfo.GetBytes(sizeStr); + + release.Seeders = ParseUtil.CoerceInt(row.Cq().Find("td:eq(8)").First().Text().Trim()); + release.Peers = ParseUtil.CoerceInt(row.Cq().Find("td:eq(9)").First().Text().Trim()) + release.Seeders; + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results, ex); + } + } + } +} diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 6af3a040a..21bc6aeac 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -186,6 +186,7 @@ + @@ -406,6 +407,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Jackett/Models/TorznabCatType.cs b/src/Jackett/Models/TorznabCatType.cs index c52c63cb7..f78fb3995 100644 --- a/src/Jackett/Models/TorznabCatType.cs +++ b/src/Jackett/Models/TorznabCatType.cs @@ -27,6 +27,10 @@ namespace Jackett.Models cats.Add(3000, "Audio"); cats.Add(3040, "Audio/Lossless"); cats.Add(3010, "Audio/MP3"); + cats.Add(6000, "XXX"); + cats.Add(6040, "XXX/x264"); + cats.Add(6010, "XXX/DVD"); + cats.Add(6060, "XXX/Imageset"); } public static bool QueryContainsParentCategory(int[] queryCats, int releaseCat) @@ -142,5 +146,25 @@ namespace Jackett.Models { get { return GetCat(3010); } } + + public static TorznabCategory XXX + { + get { return GetCat(6000); } + } + + public static TorznabCategory XXXHD + { + get { return GetCat(6040); } + } + + public static TorznabCategory XXXSD + { + get { return GetCat(6010); } + } + + public static TorznabCategory XXXImg + { + get { return GetCat(6060); } + } } } diff --git a/src/Jackett/Utils/StringUtil.cs b/src/Jackett/Utils/StringUtil.cs index 3b7d4b861..6fa5864e4 100644 --- a/src/Jackett/Utils/StringUtil.cs +++ b/src/Jackett/Utils/StringUtil.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Web; namespace Jackett.Utils { @@ -56,5 +58,9 @@ namespace Jackett.Utils return String.Join("\n", fields); } + public static string GetQueryString(this NameValueCollection collection) + { + return string.Join("&", collection.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(collection[a]))); + } } }