From 8b6fb880334eb173fda6935196d69cafb42d40c0 Mon Sep 17 00:00:00 2001 From: montana123 Date: Fri, 24 Sep 2021 21:59:58 +0200 Subject: [PATCH] [Bit-Titan] C# Implementation (#12329) resolves #10281 --- src/Jackett.Common/Definitions/bit-titan.yml | 241 ------------------- src/Jackett.Common/Indexers/BitTitan.cs | 234 ++++++++++++++++++ src/Jackett.Updater/Program.cs | 1 + 3 files changed, 235 insertions(+), 241 deletions(-) delete mode 100644 src/Jackett.Common/Definitions/bit-titan.yml create mode 100644 src/Jackett.Common/Indexers/BitTitan.cs diff --git a/src/Jackett.Common/Definitions/bit-titan.yml b/src/Jackett.Common/Definitions/bit-titan.yml deleted file mode 100644 index f4931d2d9..000000000 --- a/src/Jackett.Common/Definitions/bit-titan.yml +++ /dev/null @@ -1,241 +0,0 @@ ---- -id: bit-titan -name: BiT-TiTAN -description: "BiT-TiTAN is a GERMAN Private Torrent Tracker for MOVIES / TV / GENERAL" -language: de-DE -type: private -encoding: iso-8859-1 -links: - - https://bit-titan.net/ - -caps: - categorymappings: - - {id: 1010, cat: Movies/UHD, desc: "Movies 2160p"} - - {id: 1020, cat: Movies/HD, desc: "Movies 1080p"} - - {id: 1030, cat: Movies/HD, desc: "Movies 720p"} - - {id: 1040, cat: Movies/HD, desc: "Movies x264"} - - {id: 1050, cat: Movies/HD, desc: "Movies x265"} - - {id: 1060, cat: Movies/SD, desc: "Movies XviD"} - - {id: 1070, cat: Movies/3D, desc: "Movies 3D"} - - {id: 1080, cat: Movies/DVD, desc: "Movies DVD"} - - {id: 1090, cat: Movies/BluRay, desc: "Movies BluRay"} - - {id: 1100, cat: Movies/DVD, desc: "Movies HD2DVD"} - - {id: 1110, cat: Movies/Foreign, desc: "Movies International"} - - {id: 1120, cat: Movies/HD, desc: "Movies HD Packs"} - - {id: 1130, cat: Movies/SD, desc: "Movies SD Packs"} - - {id: 2010, cat: TV/UHD, desc: "TV 2160p"} - - {id: 2020, cat: TV/HD, desc: "TV 1080p"} - - {id: 2030, cat: TV/HD, desc: "TV 720p"} - - {id: 2040, cat: TV/HD, desc: "TV x264"} - - {id: 2050, cat: TV/HD, desc: "TV x265"} - - {id: 2060, cat: TV/SD, desc: "TV XviD"} - - {id: 2070, cat: TV/HD, desc: "TV HD Packs"} - - {id: 2080, cat: TV/SD, desc: "TV SD Packs"} - - {id: 2090, cat: TV/Foreign, desc: "TV International"} - - {id: 3010, cat: TV/Documentary, desc: "Docu 2160p"} - - {id: 3020, cat: TV/Documentary, desc: "Docu 1080p"} - - {id: 3030, cat: TV/Documentary, desc: "Docu 720p"} - - {id: 3040, cat: TV/Documentary, desc: "Docu x264"} - - {id: 3050, cat: TV/Documentary, desc: "Docu x265"} - - {id: 3060, cat: TV/Documentary, desc: "Docu XviD"} - - {id: 3070, cat: TV/Documentary, desc: "Docu HD Packs"} - - {id: 3080, cat: TV/Documentary, desc: "Docu SD Packs"} - - {id: 3090, cat: TV/Documentary, desc: "Docu International"} - - {id: 4010, cat: TV/Sport, desc: "Sport 2160p"} - - {id: 4020, cat: TV/Sport, desc: "Sport 1080p"} - - {id: 4030, cat: TV/Sport, desc: "Sport 720p"} - - {id: 4040, cat: TV/Sport, desc: "Sport SD Sport"} - - {id: 4050, cat: TV/Sport, desc: "Sport HD Packs"} - - {id: 4060, cat: TV/Sport, desc: "Sport SD Packs"} - - {id: 5010, cat: XXX, desc: "XXX 2160p"} - - {id: 5020, cat: XXX, desc: "XXX 1080p"} - - {id: 5030, cat: XXX, desc: "XXX 720p"} - - {id: 5040, cat: XXX, desc: "XXX x264"} - - {id: 5050, cat: XXX, desc: "XXX x265"} - - {id: 5060, cat: XXX, desc: "XXX XviD"} - - {id: 5070, cat: XXX, desc: "XXX HD Packs"} - - {id: 5080, cat: XXX, desc: "XXX SD Packs"} - - {id: 5090, cat: XXX, desc: "XXX Sonstiges"} - - {id: 6010, cat: PC/Games, desc: "Games Windows"} - - {id: 6020, cat: Console, desc: "Games Linux"} - - {id: 6030, cat: PC/Mac, desc: "Games MacOS"} - - {id: 6040, cat: PC/Mobile-Android, desc: "Games Android"} - - {id: 6050, cat: Console/XBox, desc: "Games Xbox"} - - {id: 6060, cat: Console/PSP, desc: "Games PlayStation"} - - {id: 6070, cat: Console/NDS, desc: "Games Nintendo"} - - {id: 6080, cat: Console, desc: "Games Sonstige"} - - {id: 7010, cat: PC/0day, desc: "Software Windows"} - - {id: 7020, cat: PC, desc: "Software Linux"} - - {id: 7030, cat: PC/Mac, desc: "Software MacOS"} - - {id: 7040, cat: PC/Mobile-Android, desc: "Software Android"} - - {id: 8010, cat: Audio/MP3, desc: "Music MP3-Album"} - - {id: 8020, cat: Audio/MP3, desc: "Music MP3-Charts"} - - {id: 8030, cat: Audio/MP3, desc: "Music MP3-Sampler"} - - {id: 8040, cat: Audio/MP3, desc: "Music MP3-Single"} - - {id: 8050, cat: Audio/Lossless, desc: "Music FLAC-Album"} - - {id: 8060, cat: Audio/Lossless, desc: "Music FLAC-Charts"} - - {id: 8070, cat: Audio/Lossless, desc: "Music FLAC-Sampler"} - - {id: 8080, cat: Audio/Lossless, desc: "Music FLAC-Single"} - - {id: 8090, cat: Audio/Video, desc: "Music Video"} - - {id: 9010, cat: Audio/Audiobook, desc: "Books A-Book"} - - {id: 9020, cat: Books/EBook, desc: "Books E-Book"} - - {id: 9030, cat: Books, desc: "Books E-Paper"} - - {id: 9040, cat: Books, desc: "Books E-Learning"} - - {id: 9060, cat: TV/Anime, desc: "Anime HD"} - - {id: 9070, cat: TV/Anime, desc: "Anime SD"} - - {id: 9080, cat: TV/Anime, desc: "Anime Pack"} - - {id: 9999, cat: Other, desc: "unsort"} - - modes: - search: [q] - tv-search: [q, season, ep] - movie-search: [q] - music-search: [q] - book-search: [q] - -settings: - - name: username - type: text - label: Username - - name: password - type: password - label: Password - - name: freeleech - type: checkbox - label: Search freeleech only - default: false - - name: sort - type: select - label: Sort requested from site - default: 1 - options: - 1: created - 2: seeders - 5: size - 9: title - - name: type - type: select - label: Order requested from site - default: 1 - options: - 1: desc - 2: asc - -login: - path: login.php - method: form - form: form[action$="login.php"] - cookies: ["JAVA=OK"] # avoid jscheck redirect - captcha: - type: image - selector: img[src*="captcha_math.php"] - input: stringCaptcha - inputs: - username: "{{ .Config.username }}" - password: "{{ .Config.password }}" - error: - - selector: div#login_error - test: - path: index.php -# selector: a[href="logout.php"] # ajax does not return a full page so cant test this. - -search: - paths: - - path: ajax_browse.php - method: post - inputs: - # allCats=1000&categories[]=1010&categories[]=1020&search=&limit=1&searchIn=0&orderBy=1&order=1 - $raw: "{{ if .Categories }}{{ range .Categories }}categories[]={{.}}&{{end}}{{ else }}{{ end }}" - # cat: (empty) all, 1000 film, 2000 tv, 3000 docs, 4000 sport, 5000 xxx, 6000 games, 7000 apps, 8000 music, 9000 other - allCats: "" - search: "{{ .Keywords }}" - # 1 25, 2 50, 3 75, 4 100 - limit: 4 - # searchin: 0 all, 1 active, 2 dead, 3 highlights, 4 bookmarks, 5 uploads, 10 bots, 11 onlyupload, 12 multiplier, 13 %download, 14 freeleech - # note: freeleech on this site means download and upload is not counted, whereas OU means download is free and upload is counted - # 14 yields no freeleech, but 11 returns results - searchin: "{{ if .Config.freeleech }}11{{ else }}0{{ end }}" - orderBy: "{{ .Config.sort }}" - order: "{{ .Config.type }}" - headers: - x-requested-with: ["XMLHttpRequest"] - - rows: - selector: table.tableinborder tr:has(.catPic) - - fields: - category: - selector: td.catPic > img - attribute: src - filters: - - name: regexp - args: (\d+).png$ - title: - selector: a[href^="details.php?id="] - details: - selector: a[href^="details.php?id="] - attribute: href - download: - # a[onlick="downloadTorrent(383718);"] --> a[href="download.php?torrent=383559"] - selector: td.tdl > a - attribute: onclick - filters: - - name: regexp - args: (\d+) - - name: prepend - args: "download.php?torrent=" - poster: - selector: div[data-image] - attribute: data-image - seeders: - selector: td.peers:has(i[title="Seeders"]) - leechers: - selector: td.peers:has(i[title="Leechers"]) - grabs: - selector: td.peers:has(i[title="Snatchers"]) - # 2 flavours of dates - date: - #  Heute 13:30:04 - #  Gestern 22:44:23 - selector: td.added:not(:contains(".")) - optional: true - filters: - - name: replace - args: ["\u00a0", ""] - - name: replace - args: ["Heute", "Today"] - - name: replace - args: ["Gestern", "Yesterday"] - - name: append - args: " +01:00" # CET - - name: fuzzytime - date: - #  24.05.2019 20:15:38 - selector: td.added:contains(".") - optional: true - filters: - - name: append - args: " +01:00" # CET - - name: dateparse - args: "02.01.2006 15:04:05 -07:00" - size: - selector: td.size - downloadvolumefactor: - case: - "span:contains(\"OU\")": 0 # only upload is counted - "span:contains(\"FL\")": 0 # freeleech neither dl or ul is counted (identity unconfirmed) - "span:contains(\"%25\")": .25 # 75% free - "span:contains(\"%50\")": .5 # 50% free - "span:contains(\"%75\")": .75 # 25% free - "*": 1 - uploadvolumefactor: - case: - "span:contains(\"x2\")": 2 - "span:contains(\"x5\")": 5 - "span:contains(\"x10\")": 10 - "span:contains(\"FL\")": 0 # freeleech neither dl or ul is counted - "*": 1 - minimumseedtime: - # 2 day (as seconds = 2 x 24 x 60 x 60) - text: 172800 -# engine n/a diff --git a/src/Jackett.Common/Indexers/BitTitan.cs b/src/Jackett.Common/Indexers/BitTitan.cs new file mode 100644 index 000000000..cf648f528 --- /dev/null +++ b/src/Jackett.Common/Indexers/BitTitan.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Jackett.Common.Models; +using Jackett.Common.Models.IndexerConfig; +using Jackett.Common.Services.Interfaces; +using Jackett.Common.Utils; +using Jackett.Common.Utils.Clients; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NLog; +using static Jackett.Common.Models.IndexerConfig.ConfigurationData; + +namespace Jackett.Common.Indexers +{ + [ExcludeFromCodeCoverage] + public class BitTitan : BaseWebIndexer + { + private string APIBASE => SiteLink + "api.php"; + private string DETAILS => SiteLink + "details.php"; + + private new ConfigurationDataAPIKey configData => (ConfigurationDataAPIKey)base.configData; + public BitTitan(IIndexerConfigurationService configService, WebClient wc, Logger l, + IProtectionService ps, ICacheService cs) + : base(id: "bit-titan", + name: "BiT-TiTAN", + description: "BiT-TiTAN is a GERMAN Private Torrent Tracker for MOVIES / TV / GENERAL", + link: "https://bit-titan.net/", + caps: new TorznabCapabilities + { + TvSearchParams = new List + { + TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep + }, + MovieSearchParams = new List + { + MovieSearchParam.Q + }, + MusicSearchParams = new List + { + MusicSearchParam.Q + }, + BookSearchParams = new List + { + BookSearchParam.Q + } + }, + configService: configService, + client: wc, + logger: l, + p: ps, + cacheService: cs, + configData: new ConfigurationDataAPIKey()) + { + Encoding = Encoding.UTF8; + Language = "de-DE"; + Type = "private"; + + configData.AddDynamic("keyInfo", new DisplayInfoConfigurationItem(String.Empty, "Find or Generate a new key here.")); + configData.AddDynamic("freeleech", new BoolConfigurationItem("Search freeleech only") { Value = false }); + + // Configure the category mappings + AddCategoryMapping(1010, TorznabCatType.MoviesUHD, "Movies 2160p"); + AddCategoryMapping(1020, TorznabCatType.MoviesHD, "Movies 1080p"); + AddCategoryMapping(1030, TorznabCatType.MoviesHD, "Movies 720p"); + AddCategoryMapping(1040, TorznabCatType.MoviesHD, "Movies x264"); + AddCategoryMapping(1050, TorznabCatType.MoviesHD, "Movies x265"); + AddCategoryMapping(1060, TorznabCatType.MoviesSD, "Movies XviD"); + AddCategoryMapping(1070, TorznabCatType.Movies3D, "Movies 3D"); + AddCategoryMapping(1080, TorznabCatType.MoviesDVD, "Movies DVD"); + AddCategoryMapping(1090, TorznabCatType.MoviesBluRay, "Movies BluRay"); + AddCategoryMapping(1100, TorznabCatType.MoviesDVD, "Movies HD2DVD"); + AddCategoryMapping(1110, TorznabCatType.MoviesForeign, "Movies International"); + AddCategoryMapping(1120, TorznabCatType.MoviesHD, "Movies HD Packs"); + AddCategoryMapping(1130, TorznabCatType.MoviesSD, "Movies SD Packs"); + AddCategoryMapping(2010, TorznabCatType.TVUHD, "TV 2160p"); + AddCategoryMapping(2020, TorznabCatType.TVHD, "TV 1080p"); + AddCategoryMapping(2030, TorznabCatType.TVHD, "TV 720p"); + AddCategoryMapping(2040, TorznabCatType.TVHD, "TV x264"); + AddCategoryMapping(2050, TorznabCatType.TVHD, "TV x265"); + AddCategoryMapping(2060, TorznabCatType.TVSD, "TV XviD"); + AddCategoryMapping(2070, TorznabCatType.TVHD, "TV HD Packs"); + AddCategoryMapping(2080, TorznabCatType.TVSD, "TV SD Packs"); + AddCategoryMapping(2090, TorznabCatType.TVForeign, "TV International"); + AddCategoryMapping(3010, TorznabCatType.TVDocumentary, "Docu 2160p"); + AddCategoryMapping(3020, TorznabCatType.TVDocumentary, "Docu 1080p"); + AddCategoryMapping(3030, TorznabCatType.TVDocumentary, "Docu 720p"); + AddCategoryMapping(3040, TorznabCatType.TVDocumentary, "Docu x264"); + AddCategoryMapping(3050, TorznabCatType.TVDocumentary, "Docu x265"); + AddCategoryMapping(3060, TorznabCatType.TVDocumentary, "Docu XviD"); + AddCategoryMapping(3070, TorznabCatType.TVDocumentary, "Docu HD Packs"); + AddCategoryMapping(3080, TorznabCatType.TVDocumentary, "Docu SD Packs"); + AddCategoryMapping(3090, TorznabCatType.TVDocumentary, "Docu International"); + AddCategoryMapping(4010, TorznabCatType.TVSport, "Sport 2160p"); + AddCategoryMapping(4020, TorznabCatType.TVSport, "Sport 1080p"); + AddCategoryMapping(4030, TorznabCatType.TVSport, "Sport 720p"); + AddCategoryMapping(4040, TorznabCatType.TVSport, "Sport SD Sport"); + AddCategoryMapping(4050, TorznabCatType.TVSport, "Sport HD Packs"); + AddCategoryMapping(4060, TorznabCatType.TVSport, "Sport SD Packs"); + AddCategoryMapping(5010, TorznabCatType.XXX, "XXX 2160p"); + AddCategoryMapping(5020, TorznabCatType.XXX, "XXX 1080p"); + AddCategoryMapping(5030, TorznabCatType.XXX, "XXX 720p"); + AddCategoryMapping(5040, TorznabCatType.XXX, "XXX x264"); + AddCategoryMapping(5050, TorznabCatType.XXX, "XXX x265"); + AddCategoryMapping(5060, TorznabCatType.XXX, "XXX XviD"); + AddCategoryMapping(5070, TorznabCatType.XXX, "XXX HD Packs"); + AddCategoryMapping(5080, TorznabCatType.XXX, "XXX SD Packs"); + AddCategoryMapping(5090, TorznabCatType.XXX, "XXX Sonstiges"); + AddCategoryMapping(6010, TorznabCatType.PCGames, "Games Windows"); + AddCategoryMapping(6020, TorznabCatType.Console, "Games Linux"); + AddCategoryMapping(6030, TorznabCatType.PCMac, "Games MacOS"); + AddCategoryMapping(6040, TorznabCatType.PCMobileAndroid, "Games Android"); + AddCategoryMapping(6050, TorznabCatType.ConsoleXBox, "Games Xbox"); + AddCategoryMapping(6060, TorznabCatType.ConsolePSP, "Games PlayStation"); + AddCategoryMapping(6070, TorznabCatType.ConsoleNDS, "Games Nintendo"); + AddCategoryMapping(6080, TorznabCatType.Console, "Games Sonstige"); + AddCategoryMapping(7010, TorznabCatType.PC0day, "Software Windows"); + AddCategoryMapping(7020, TorznabCatType.PC, "Software Linux"); + AddCategoryMapping(7030, TorznabCatType.PCMac, "Software MacOS"); + AddCategoryMapping(7040, TorznabCatType.PCMobileAndroid, "Software Android"); + AddCategoryMapping(8010, TorznabCatType.AudioMP3, "Music MP3-Album"); + AddCategoryMapping(8020, TorznabCatType.AudioMP3, "Music MP3-Charts"); + AddCategoryMapping(8030, TorznabCatType.AudioMP3, "Music MP3-Sampler"); + AddCategoryMapping(8040, TorznabCatType.AudioMP3, "Music MP3-Single"); + AddCategoryMapping(8050, TorznabCatType.AudioLossless, "Music FLAC-Album"); + AddCategoryMapping(8060, TorznabCatType.AudioLossless, "Music FLAC-Charts"); + AddCategoryMapping(8070, TorznabCatType.AudioLossless, "Music FLAC-Sampler"); + AddCategoryMapping(8080, TorznabCatType.AudioLossless, "Music FLAC-Single"); + AddCategoryMapping(8090, TorznabCatType.AudioVideo, "Music Video"); + AddCategoryMapping(9010, TorznabCatType.AudioAudiobook, "Books A-Book"); + AddCategoryMapping(9020, TorznabCatType.BooksEBook, "Books E-Book"); + AddCategoryMapping(9030, TorznabCatType.Books, "Books E-Paper"); + AddCategoryMapping(9040, TorznabCatType.Books, "Books E-Learning"); + AddCategoryMapping(9060, TorznabCatType.TVAnime, "Anime HD"); + AddCategoryMapping(9070, TorznabCatType.TVAnime, "Anime SD"); + AddCategoryMapping(9080, TorznabCatType.TVAnime, "Anime Pack"); + AddCategoryMapping(9999, TorznabCatType.Other, "unsort"); + } + + + public override async Task ApplyConfiguration(JToken configJson) + { + LoadValuesFromJson(configJson); + + IsConfigured = false; + try + { + var results = await PerformQuery(new TorznabQuery()); + if (results.Count() == 0) + throw new Exception("Testing returned no results!"); + IsConfigured = true; + SaveConfig(); + } + catch (Exception e) + { + throw new ExceptionWithConfigData(e.Message, configData); + } + + return IndexerConfigurationStatus.Completed; + } + + protected override async Task> PerformQuery(TorznabQuery query) + { + var releases = new List(); + + var apiKey = configData.Key.Value; + var searchUrl = $"{APIBASE}?apiKey={apiKey}"; + + var cats = MapTorznabCapsToTrackers(query); + if (cats.Count > 0) + searchUrl += "&categories=" + string.Join(",", cats); + + searchUrl += "&search=" + query.SanitizedSearchTerm; + + searchUrl += "&downloadLink=1"; + + searchUrl += "&limit=4"; + + if (((BoolConfigurationItem)configData.GetDynamic("freeleech")).Value) + searchUrl += "&searchIn=9"; + + var results = await RequestWithCookiesAndRetryAsync(searchUrl); + + try + { + var rows = (JArray)((JObject)JsonConvert.DeserializeObject(results.ContentString))["results"]; + foreach (var row in rows) + { + var title = row["name"].ToString(); + if (!query.MatchQueryStringAND(title)) + continue; + + var torrentId = row["id"].ToString(); + var details = new Uri(DETAILS + "?id=" + torrentId); + var link = new Uri(row["download"].ToString()); + var publishDate = DateTime.Parse(row["added"].ToString()); + var seeders = (int)row["seeds"]; + var cat = MapTrackerCatToNewznab(row["category"].ToString()); + + var release = new ReleaseInfo + { + Title = title, + Details = details, + Guid = details, + Link = link, + PublishDate = publishDate, + Category = cat, + Size = (long)row["size"], + Grabs = (int)row["snatchers"], + Seeders = seeders, + Peers = seeders + (int)row["leechers"], + Imdb = null, + UploadVolumeFactor = (int)row["uploadFactor"], + DownloadVolumeFactor = (int)row["downloadFactor"], + MinimumRatio = 1, + MinimumSeedTime = 172800 // 2 days + }; + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results.ContentString, ex); + } + + return releases; + } + } +} diff --git a/src/Jackett.Updater/Program.cs b/src/Jackett.Updater/Program.cs index 580576fdb..e2ecc75a3 100644 --- a/src/Jackett.Updater/Program.cs +++ b/src/Jackett.Updater/Program.cs @@ -275,6 +275,7 @@ namespace Jackett.Updater "Definitions/bithq.yml", "Definitions/bigtower.yml", "Definitions/bitme.yml", + "Definitions/bit-titan.yml", // migrated to C# #10281 "Definitions/bittorrentam.yml", "Definitions/blubits.yml", "Definitions/brobits.yml",