diff --git a/src/Jackett/Indexers/Abstract/CouchPotatoTracker.cs b/src/Jackett/Indexers/Abstract/CouchPotatoTracker.cs new file mode 100644 index 000000000..fdb9e12e9 --- /dev/null +++ b/src/Jackett/Indexers/Abstract/CouchPotatoTracker.cs @@ -0,0 +1,118 @@ +using Jackett.Models; +using Jackett.Models.IndexerConfig; +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.Threading.Tasks; + +namespace Jackett.Indexers.Abstract +{ + public abstract class CouchPotatoTracker : BaseWebIndexer + { + protected string endpoint; + protected string APIUrl { get { return SiteLink + endpoint; } } + + new ConfigurationDataUserPasskey configData + { + get { return (ConfigurationDataUserPasskey)base.configData; } + set { base.configData = value; } + } + + public CouchPotatoTracker(IIndexerConfigurationService configService, IWebClient client, Logger logger, IProtectionService p, string name, string description, string link, string endpoint) + : base(name: name, + description: description, + link: link, + caps: new TorznabCapabilities(), + configService: configService, + client: client, + logger: logger, + p: p, + configData: new ConfigurationDataUserPasskey()) + { + this.endpoint = endpoint; + TorznabCaps.SupportsImdbSearch = true; + } + + public override async Task ApplyConfiguration(JToken configJson) + { + LoadValuesFromJson(configJson); + IsConfigured = true; + SaveConfig(); + return IndexerConfigurationStatus.RequiresTesting; + } + + protected override async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + var searchString = query.GetQueryString(); + + var searchUrl = APIUrl; + var queryCollection = new NameValueCollection(); + + if (!string.IsNullOrEmpty(query.ImdbID)) + { + queryCollection.Add("imdbid", "browse"); + } + if (searchString != null) + { + queryCollection.Add("search", searchString); + } + queryCollection.Add("passkey", configData.Passkey.Value); + queryCollection.Add("user", configData.Username.Value); + + searchUrl += "?" + queryCollection.GetQueryString(); + + var response = await RequestStringWithCookiesAndRetry(searchUrl); + + JObject json = null; + try + { + json = JObject.Parse(response.Content); + } + catch (Exception ex) + { + throw new Exception("Error while parsing json: " + response.Content, ex); + } + var error = (string)json["error"]; + if (error != null) + throw new Exception(error); + + try + { + foreach (JObject r in json["results"]) + { + var release = new ReleaseInfo(); + release.Title = (string)r["release_name"]; + release.Comments = new Uri((string)r["details_url"]); + release.Link = new Uri((string)r["download_url"]); + release.Guid = release.Link; + release.Imdb = ParseUtil.GetImdbID((string)r["imdb_id"]); + var freeleech = (bool)r["freeleech"]; + if (freeleech) + release.DownloadVolumeFactor = 0; + else + release.DownloadVolumeFactor = 1; + release.UploadVolumeFactor = 1; + var type = (string)r["type"]; + release.Category = MapTrackerCatToNewznab(type); + release.Size = (long?)r["size"] * 1024 * 1024; + release.Seeders = (int?)r["seeders"]; + release.Peers = release.Seeders + (int?)r["leechers"]; + release.PublishDate = DateTimeUtil.FromUnknown((string)r["publish_date"]); + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(response.Content, ex); + } + + return releases; + } + } +} diff --git a/src/Jackett/Indexers/DanishBits.cs b/src/Jackett/Indexers/DanishBits.cs index 94fbe8ccc..8c200b72f 100644 --- a/src/Jackett/Indexers/DanishBits.cs +++ b/src/Jackett/Indexers/DanishBits.cs @@ -1,310 +1,44 @@ -using CsQuery; -using Jackett.Models; +using Jackett.Models; using Jackett.Services; using Jackett.Utils.Clients; -using Newtonsoft.Json.Linq; using NLog; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; using System.Text; -using System.Text.RegularExpressions; +using Jackett.Indexers.Abstract; +using System.Collections.Generic; using System.Threading.Tasks; -using System.Web; -using CsQuery.ExtensionMethods; -using Jackett.Models.IndexerConfig; -using Jackett.Utils; namespace Jackett.Indexers { - public class DanishBits : BaseWebIndexer + public class DanishBits : CouchPotatoTracker { - private string LoginUrl { get { return SiteLink + "login.php"; } } - private string SearchUrl { get { return SiteLink + "torrents.php"; } } - - new NxtGnConfigurationData configData - { - get { return (NxtGnConfigurationData)base.configData; } - set { base.configData = value; } - } - public DanishBits(IIndexerConfigurationService configService, IWebClient c, Logger l, IProtectionService ps) : base(name: "DanishBits", description: "A danish closed torrent tracker", link: "https://danishbits.org/", - caps: new TorznabCapabilities(), + endpoint: "couchpotato.php", configService: configService, client: c, logger: l, - p: ps, - configData: new NxtGnConfigurationData()) + p: ps + ) { Encoding = Encoding.GetEncoding("UTF-8"); Language = "da-dk"; Type = "private"; - // Movies Mapping - // DanishBits HD - AddCategoryMapping(2, TorznabCatType.MoviesHD); - AddCategoryMapping(2, TorznabCatType.MoviesWEBDL); - - // Danske film - AddCategoryMapping(3, TorznabCatType.MoviesHD); - AddCategoryMapping(3, TorznabCatType.MoviesWEBDL); - AddCategoryMapping(3, TorznabCatType.MoviesDVD); - AddCategoryMapping(3, TorznabCatType.MoviesForeign); - AddCategoryMapping(3, TorznabCatType.MoviesSD); - - // DVDR Nordic - AddCategoryMapping(10, TorznabCatType.MoviesDVD); - AddCategoryMapping(10, TorznabCatType.MoviesForeign); - - // Custom - AddCategoryMapping(28, TorznabCatType.MoviesHD); - AddCategoryMapping(28, TorznabCatType.MoviesDVD); - - // Custom HD - AddCategoryMapping(29, TorznabCatType.MoviesHD); - AddCategoryMapping(29, TorznabCatType.MoviesWEBDL); - - // Custom Tablet - AddCategoryMapping(31, TorznabCatType.MoviesSD); - - if (!configData.OnlyDanishCategories.Value) - { - // Bluray - AddCategoryMapping(8, TorznabCatType.MoviesBluRay); - - // Boxset - AddCategoryMapping(9, TorznabCatType.MoviesHD); - AddCategoryMapping(9, TorznabCatType.MoviesForeign); - AddCategoryMapping(9, TorznabCatType.MoviesDVD); - - // DVDR - AddCategoryMapping(11, TorznabCatType.MoviesDVD); - - // HDx264 - AddCategoryMapping(22, TorznabCatType.MoviesHD); - - // XviD/MP4/SDx264 - AddCategoryMapping(24, TorznabCatType.MoviesSD); - } - - // TV Mapping - // DanishBits TV - AddCategoryMapping(1, TorznabCatType.TVHD); - AddCategoryMapping(1, TorznabCatType.TVWEBDL); - - // Dansk TV - AddCategoryMapping(4, TorznabCatType.TVHD); - AddCategoryMapping(4, TorznabCatType.TVWEBDL); - AddCategoryMapping(4, TorznabCatType.TVFOREIGN); - AddCategoryMapping(4, TorznabCatType.TVSD); - - // Custom TV - AddCategoryMapping(30, TorznabCatType.TVHD); - AddCategoryMapping(30, TorznabCatType.TVWEBDL); - - if (!configData.OnlyDanishCategories.Value) - { - // TV - AddCategoryMapping(20, TorznabCatType.TVHD); - AddCategoryMapping(20, TorznabCatType.TVSD); - AddCategoryMapping(20, TorznabCatType.TVWEBDL); - - // TV Boxset - AddCategoryMapping(21, TorznabCatType.TVHD); - AddCategoryMapping(21, TorznabCatType.TVSD); - AddCategoryMapping(21, TorznabCatType.TVWEBDL); - } - - // E-book - AddCategoryMapping(12, TorznabCatType.BooksEbook); - // Audiobooks - AddCategoryMapping(6, TorznabCatType.AudioAudiobook); - } - - public override async Task ApplyConfiguration(JToken configJson) - { - LoadValuesFromJson(configJson); - var pairs = new Dictionary { - { "username", configData.Username.Value }, - { "password", configData.Password.Value }, - { "langlang", null }, - { "login", "login" } - }; - // Get inital cookies - CookieHeader = string.Empty; - var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl); - - await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () => - { - CQ dom = response.Content; - var messageEl = dom["#loginform .warning"]; - var errorMessage = messageEl.Text().Trim(); - throw new ExceptionWithConfigData(errorMessage, configData); - }); - return IndexerConfigurationStatus.RequiresTesting; + AddCategoryMapping("movie", TorznabCatType.Movies); + AddCategoryMapping("tv", TorznabCatType.TV); } protected override async Task> PerformQuery(TorznabQuery query) { - TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2, 0, 0), 3, 5, DayOfWeek.Sunday); - TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 10, 5, DayOfWeek.Sunday); - TimeSpan delta = new TimeSpan(1, 0, 0); - TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition); - TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment }; - TimeZoneInfo denmarkTz = TimeZoneInfo.CreateCustomTimeZone("Denmark Time", new TimeSpan(1, 0, 0), "(GMT+01:00) Denmark Time", "Denmark Time", "Denmark DST", adjustments); - - var releasesPerPage = 100; - var releases = new List(); - - var page = (query.Offset / releasesPerPage) + 1; - - string episodeSearchUrl; - if (string.IsNullOrEmpty(query.GetQueryString())) - { - episodeSearchUrl = SearchUrl + "?page=" + page; + var newQuery = query; + if (string.IsNullOrEmpty(query.SearchTerm) && string.IsNullOrEmpty(query.ImdbID)) + { + newQuery = query.Clone(); + newQuery.SearchTerm = "%"; } - else - { - var cats = MapTorznabCapsToTrackers(query); - var catsUrlPart = string.Join("&", cats.Select(c => $"filter_{c}=on")); - episodeSearchUrl = $"{SearchUrl}?page={page}&group=0&{catsUrlPart}&search={HttpUtility.UrlEncode(query.GetQueryString())}&pre_type=torrents&type="; - } - var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); - if (string.IsNullOrEmpty(results.Content)) - { - CookieHeader = string.Empty; - var pairs = new Dictionary - { - {"username", configData.Username.Value}, - {"password", configData.Password.Value}, - {"langlang", null}, - {"login", "login"} - }; - var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl); - - await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("logout.php"), () => - { - CQ dom = response.Content; - var messageEl = dom["#loginform .warning"]; - var errorMessage = messageEl.Text().Trim(); - throw new ExceptionWithConfigData(errorMessage, configData); - }); - results = await RequestStringWithCookiesAndRetry(episodeSearchUrl); - } - try - { - CQ dom = results.Content; - var rows = dom["#torrent_table tr.torrent"]; - foreach (var row in rows) - { - var qRow = row.Cq(); - var release = new ReleaseInfo - { - MinimumRatio = 1, - MinimumSeedTime = 172800 - }; - - var catAnchor = row.FirstChild.FirstChild; - var catUrl = catAnchor.GetAttribute("href"); - var catStr = Regex.Match(catUrl, "filter_(?[0-9]+)=on").Groups["catNo"].Value; - var catNo = int.Parse(catStr); - var moviesCatsDanish = new[] { 2, 3, 10, 28, 29, 31 }; - var moviesCatsIntl = new[] { 8, 9, 11, 22, 24 }; - var moviesCats = configData.OnlyDanishCategories.Value - ? moviesCatsDanish - : moviesCatsDanish.Concat(moviesCatsIntl); - var seriesCatsDanish = new[] { 1, 4, 30 }; - var seriesCatsIntl = new[] { 20, 21 }; - var seriesCats = configData.OnlyDanishCategories.Value - ? seriesCatsDanish - : seriesCatsDanish.Concat(seriesCatsIntl); - if (moviesCats.Contains(catNo)) - release.Category = new List { TorznabCatType.Movies.ID }; - else if (seriesCats.Contains(catNo)) - release.Category = new List { TorznabCatType.TV.ID }; - else if (catNo == 12) - release.Category = new List { TorznabCatType.BooksEbook.ID }; - else if (catNo == 6) - release.Category = new List { TorznabCatType.AudioAudiobook.ID }; - else - continue; - - var titleAnchor = qRow.Find("div.croptorrenttext a").FirstElement(); - var title = titleAnchor.GetAttribute("title"); - release.Title = title; - - var dlUrlAnchor = qRow.Find("span.right a[title=\"Direkte download link\"]").FirstElement(); - var dlUrl = dlUrlAnchor.GetAttribute("href"); - release.Link = new Uri(SiteLink + dlUrl); - - var torrentLink = titleAnchor.GetAttribute("href"); - release.Guid = new Uri(SiteLink + torrentLink); - release.Comments = new Uri(SearchUrl + torrentLink); - - var addedElement = qRow.Find("span.time").FirstElement(); - var addedStr = addedElement.GetAttribute("title"); - release.PublishDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.ParseExact(addedStr, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture), denmarkTz).ToLocalTime(); - - var columns = qRow.Children(); - var seedersElement = columns.Reverse().Skip(1).First(); - release.Seeders = int.Parse(seedersElement.InnerText); - - var leechersElement = columns.Last().FirstElement(); - release.Peers = release.Seeders + int.Parse(leechersElement.InnerText); - - var sizeElement = columns.Skip(2).First(); - var sizeStr = sizeElement.InnerText; - release.Size = ReleaseInfo.GetBytes(sizeStr); - - var imdbAnchor = qRow.Find(".torrentnotes a") - .FirstOrDefault(a => a.GetAttribute("href").Contains("imdb.com")); - if (imdbAnchor != null) - { - var referrerUrl = imdbAnchor.GetAttribute("href"); - release.Imdb = long.Parse(Regex.Match(referrerUrl, "tt(?[0-9]+)").Groups["imdbId"].Value); - } - - var Files = qRow.Find("td:nth-child(3) > div"); - release.Files = ParseUtil.CoerceLong(Files.Text().Split(' ')[0]); - - var Grabs = qRow.Find("td:nth-child(6)"); - release.Grabs = ParseUtil.CoerceLong(Grabs.Text()); - - if (qRow.Find("span.freeleech, img[src=\"/static/common/torrents/gratis.png\"]").Length >= 1) - release.DownloadVolumeFactor = 0; - else - release.DownloadVolumeFactor = 1; - - if (qRow.Find("img[src=\"/static/common/torrents/toxupload.png\"]").Length >= 1) - release.UploadVolumeFactor = 2; - else - release.UploadVolumeFactor = 1; - - releases.Add(release); - } - } - catch (Exception ex) - { - OnParseError(results.Content, ex); - } - return releases; - } - - public class NxtGnConfigurationData : ConfigurationData - { - public NxtGnConfigurationData() - { - Username = new StringItem { Name = "Username" }; - Password = new StringItem { Name = "Password" }; - OnlyDanishCategories = new BoolItem { Name = "Only Danish Categories" }; - } - public StringItem Username { get; private set; } - public StringItem Password { get; private set; } - public BoolItem OnlyDanishCategories { get; private set; } + return await base.PerformQuery(newQuery); } } } diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index c9fa62088..9d6ae58f3 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -177,6 +177,7 @@ + @@ -257,6 +258,7 @@ + @@ -602,4 +604,4 @@ - + \ No newline at end of file diff --git a/src/Jackett/Models/IndexerConfig/ConfigurationDataUserPasskey.cs b/src/Jackett/Models/IndexerConfig/ConfigurationDataUserPasskey.cs new file mode 100644 index 000000000..7f41230f1 --- /dev/null +++ b/src/Jackett/Models/IndexerConfig/ConfigurationDataUserPasskey.cs @@ -0,0 +1,14 @@ +namespace Jackett.Models.IndexerConfig +{ + public class ConfigurationDataUserPasskey : ConfigurationData + { + public StringItem Username { get; private set; } + public StringItem Passkey { get; private set; } + + public ConfigurationDataUserPasskey(string instructionMessageOptional = null) + { + Username = new StringItem { Name = "Username" }; + Passkey = new StringItem { Name = "Passkey" }; + } + } +}