diff --git a/src/Jackett.Common/Definitions/sharewood-api.yml b/src/Jackett.Common/Definitions/sharewood-api.yml new file mode 100644 index 000000000..ff6a499e3 --- /dev/null +++ b/src/Jackett.Common/Definitions/sharewood-api.yml @@ -0,0 +1,203 @@ +--- +id: sharewood-api +replaces: + - sharewoodapi +name: Sharewood (API) +description: "sharewood is a Semi-Private FRENCH Torrent Tracker for GENERAL" +language: fr-FR +type: semi-private +encoding: UTF-8 +requestDelay: 2.1 +certificates: + - 023A091295E81813D040DFA0FA842DF9892BF0F5 # expired 10-March-2024 note: despite a new CA issued this one still pops up occasionally +links: + - https://www.sharewood.tv/ + +caps: + categorymappings: + # categories +# - {id: 1, cat: Movies, desc: "Vidéo"} +# - {id: 1, cat: TV, desc: "Vidéo"} +# - {id: 2, cat: Audio, desc: "Audio"} +# - {id: 3, cat: PC, desc: "Application"} +# - {id: 4, cat: Books/EBook, desc: "Ebooks"} +# - {id: 5, cat: PC/Games, desc: "Jeu-Vidéo"} +# - {id: 6, cat: Other, desc: "Formation"} +# - {id: 7, cat: XXX, desc: "XXX"} + + # subcategories + - {id: 9, cat: Movies, desc: "Films"} + - {id: 10, cat: TV, desc: "Séries"} + - {id: 11, cat: Movies/Other, desc: "Films Animations"} + - {id: 12, cat: TV/Anime, desc: "Séries Animations"} + - {id: 13, cat: TV/Documentary, desc: "Documentaires"} + - {id: 14, cat: TV/Other, desc: "Emissions TV"} + - {id: 15, cat: TV/Other, desc: "Spectacles/Concerts"} + - {id: 16, cat: TV/Sport, desc: "Sports"} + - {id: 17, cat: Audio/Video, desc: "Karaoké Vidéo"} + - {id: 18, cat: Audio/Other, desc: "Karaoké"} + - {id: 20, cat: Audio, desc: "Musiques"} + - {id: 21, cat: Audio/Other, desc: "Podcasts"} + - {id: 22, cat: Audio/Other, desc: "Samples"} + - {id: 23, cat: Audio/Audiobook, desc: "Ebooks Audio"} + - {id: 24, cat: Books/EBook, desc: "BDs"} + - {id: 25, cat: Books/Comics, desc: "Comics"} + - {id: 26, cat: Books/Other, desc: "Mangas"} + - {id: 27, cat: Books, desc: "Livres"} + - {id: 28, cat: Books/Mags, desc: "Presse"} + - {id: 29, cat: PC, desc: "Applications Linux"} + - {id: 30, cat: PC/0day, desc: "Applications Windows"} + - {id: 31, cat: PC/Mac, desc: "Applications Mac"} + - {id: 34, cat: PC/Mobile-iOS, desc: "Applications Smartphone/Tablette"} + - {id: 34, cat: PC/Mobile-Android, desc: "Applications Smartphone/Tablette"} + - {id: 35, cat: PC/Mobile-Other, desc: "GPS"} + - {id: 36, cat: PC/Games, desc: "Jeux Linux"} + - {id: 37, cat: PC/Games, desc: "Jeux Windows"} + - {id: 38, cat: PC/Mac, desc: "Jeux Mac"} + - {id: 39, cat: Console/NDS, desc: "Jeux Nintendo"} + - {id: 39, cat: Console/Wii, desc: "Jeux Nintendo"} + - {id: 39, cat: Console/Wiiware, desc: "Jeux Nintendo"} + - {id: 39, cat: Console/3DS, desc: "Jeux Nintendo"} + - {id: 39, cat: Console/WiiU, desc: "Jeux Nintendo"} + - {id: 40, cat: Console/PS4, desc: "Jeux Sony"} + - {id: 41, cat: PC/Mobile-Android, desc: "Jeux Smartphone/Tablette"} + - {id: 42, cat: PC/Games, desc: "Jeux Microsoft"} + - {id: 43, cat: Other, desc: "Rétrogaming & Emulation"} + - {id: 44, cat: XXX, desc: "Films XXX"} + - {id: 45, cat: XXX/Other, desc: "XXX Hentai"} + - {id: 47, cat: XXX/ImageSet, desc: "XXX Images"} + - {id: 48, cat: XXX/Other, desc: "XXX Jeux-Vidéo"} + - {id: 49, cat: Other/Misc, desc: "Formations Vidéos"} + - {id: 50, cat: Other/Misc, desc: "Formations Logiciels"} + - {id: 51, cat: XXX/Other, desc: "XXX Ebooks"} + - {id: 52, cat: Audio/Video, desc: "Vidéos-Clips"} + + modes: + search: [q] + tv-search: [q, season, ep] + movie-search: [q] + music-search: [q] + book-search: [q] + allowrawsearch: true + +settings: + - name: passkey + type: text + label: Passkey + - name: info_passkey + type: info + label: About your Passkey + default: "Find your Passkey by accessing your Sharewood profile on the My Profile page and scrolling down to the Passkey field." + - name: freeleech + type: checkbox + label: Search freeleech only + default: false + - name: multilang + type: checkbox + label: Replace MULTi by another language in release name + default: false + - name: multilanguage + type: select + label: Replace MULTi by this language + default: FRENCH + options: + FRENCH: FRENCH + MULTi FRENCH: MULTi FRENCH + ENGLISH: ENGLISH + MULTi ENGLISH: MULTi ENGLISH + VOSTFR: VOSTFR + MULTi VOSTFR: MULTi VOSTFR + - name: vostfr + type: checkbox + label: Replace VOSTFR and SUBFRENCH with ENGLISH + default: false + +login: + path: "api/{{ .Config.passkey }}/last-torrents" + method: get + error: + - selector: ":root:contains(\"Passkey invalide\")" + - selector: ":root:contains(\"503 Service Temporarily Unavailable\")" + +search: + paths: + - path: "api/{{ .Config.passkey }}/{{ if .Keywords }}search{{ else }}last-torrents{{ end }}" + response: + type: json + + inputs: + subcategory: "{{ join .Categories \",\" }}" + name: "{{ .Keywords }}" + limit: 50 + free: "{{ if .Config.freeleech }}1{{ else }}{{ end }}" + + keywordsfilters: + - name: re_replace + args: ["[\\:\\-\\/\\|\\(\\)]+", " "] + + rows: + selector: $ + + fields: + _id: + selector: id + category: + selector: subcategory_id + title_phase1: + selector: name + title_vostfr: + text: "{{ .Result.title_phase1 }}" + filters: + - name: re_replace + args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"] + title_phase2: + text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase1 }}{{ end }}" + title_multilang: + text: "{{ .Result.title_phase2 }}" + filters: + - name: re_replace + args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"] + title: + text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase2 }}{{ end }}" + details: + selector: slug + filters: + - name: prepend + args: "/torrents/" + - name: append + args: ".{{ .Result._id }}" + download: + selector: download_url + size: + selector: size + seeders: + selector: seeders + leechers: + selector: leechers + grabs: + selector: times_completed + date: + selector: created_at + filters: + - name: append + args: " +00:00" # GMT + - name: dateparse + args: "yyyy-MM-dd HH:mm:ss zzz" + downloadvolumefactor: + # api returns 0=false, 1=true + selector: free + case: + 0: 1 # not free + 1: 0 # freeleech + uploadvolumefactor: + # api returns 0=false, 1=true + selector: doubleup + case: + 0: 1 # normal + 1: 2 # double + minimumratio: + text: 0.75 + minimumseedtime: + # 3 days (as seconds = 3 x 24 x 60 x 60) + text: 259200 +# UNIT3D diff --git a/src/Jackett.Common/Definitions/sharewood.yml b/src/Jackett.Common/Definitions/sharewood.yml deleted file mode 100644 index a5de0be49..000000000 --- a/src/Jackett.Common/Definitions/sharewood.yml +++ /dev/null @@ -1,218 +0,0 @@ ---- -id: sharewood -name: Sharewood -description: "sharewood is a Semi-Private FRENCH Torrent Tracker for GENERAL" -language: fr-FR -type: semi-private -encoding: UTF-8 -requestDelay: 4.1 -certificates: - - 023A091295E81813D040DFA0FA842DF9892BF0F5 # expired 10-March-2024 note: despite a new CA issued this one still pops up occasionally -links: - - https://www.sharewood.tv/ - -caps: - categorymappings: - - {id: "Films", cat: Movies, desc: "Films"} - - {id: "Films_Animations", cat: Movies, desc: "Films Animation"} - - {id: "Animes", cat: TV/Anime, desc: " Séries Animations"} - - {id: "Series", cat: TV, desc: "TV Series"} - - {id: "Documentaires", cat: TV/Documentary, desc: "TV Documentaires"} - - {id: "Emissions", cat: TV, desc: "TV Emissions"} - - {id: "Sports", cat: TV/Sport, desc: "TV Sports"} - - {id: "Spectacles", cat: TV, desc: "TV Spectacles/Concerts"} - - {id: "Karaoke_Video", cat: Audio/Video, desc: "Karaoké Vidéo"} - - {id: "Videos_Clips", cat: Audio/Video, desc: "TV Videos Clips"} - - {id: "Musiques", cat: Audio, desc: "Audio Musiques"} - - {id: "Karaoke", cat: Audio, desc: "Audio Karaoké"} - - {id: "Samples", cat: Audio, desc: "Audio Samples"} - - {id: "Podcasts", cat: Audio, desc: "Audio Podcasts"} - - {id: "AudioBooks", cat: Audio/Audiobook, desc: "Audio Books"} - - {id: "Windows", cat: PC/0day, desc: " Applications Windows"} - - {id: "APK", cat: PC/Mobile-Android, desc: " Applications Android"} - - {id: "GPS", cat: PC/Mobile-Other, desc: " Applications GPS"} - - {id: "Ebooks", cat: Books/EBook, desc: "Books Ebooks"} - - {id: "BDs", cat: Books/EBook, desc: "Books BDs"} - - {id: "Presse", cat: Books/Mags, desc: "Books Presse"} - - {id: "Mangas", cat: Books/Comics, desc: "Books Mangas"} - - {id: "Comics", cat: Books/Comics, desc: "Books Comics"} - - {id: "Nintendo", cat: Console/NDS, desc: "Jeux Nintendo"} - - {id: "Microsoft", cat: Console/XBox, desc: "Jeux Microsoft"} - - {id: "Linux", cat: PC/Games, desc: "Jeux Linux"} - - {id: "Mac", cat: PC/Games, desc: "Jeux Mac"} - - {id: "Retro", cat: PC/Games, desc: "Jeux Vidéos"} - - {id: "Sony", cat: Console/PSP, desc: "Jeux Sony"} - - {id: "Smartphone_Tablette", cat: Console, desc: "Jeux Smartphone/Tablette"} - - {id: "Retrogaming_Emulation", cat: Console, desc: "Jeux Rétrogaming & Emulation"} - - {id: "Formations", cat: Other, desc: "Formations"} - - {id: "Formations_Video", cat: Other, desc: "Formations Video"} - - {id: "Formations_Logiciels", cat: Other, desc: "Formations Logiciels"} - - {id: "Films_X", cat: XXX, desc: "XXX"} - - {id: "Ebooks_X", cat: XXX, desc: "XXX"} - - {id: "Hentai", cat: XXX, desc: "Hentai"} - - {id: "ImagesX", cat: XXX, desc: "ImagesX"} - - modes: - search: [q] - tv-search: [q, season, ep] - movie-search: [q] - music-search: [q] - book-search: [q] - allowrawsearch: true - -settings: - - name: username - type: text - label: Username - - name: password - type: password - label: Password - - name: freeleech - type: checkbox - label: Search freeleech only - default: false - - name: multilang - type: checkbox - label: Replace MULTi by another language in release name - default: false - - name: multilanguage - type: select - label: Replace MULTi by this language - default: FRENCH - options: - FRENCH: FRENCH - MULTi FRENCH: MULTi FRENCH - ENGLISH: ENGLISH - MULTi ENGLISH: MULTi ENGLISH - VOSTFR: VOSTFR - MULTi VOSTFR: MULTi VOSTFR - - name: vostfr - type: checkbox - label: Replace VOSTFR and SUBFRENCH with ENGLISH - default: false - - name: sort - type: select - label: Sort requested from site - default: created_at - options: - created_at: created - seeders: seeders - size: size - name: title - - name: type - type: select - label: Order requested from site - default: desc - options: - desc: desc - asc: asc - -login: - path: login - method: form - form: form[action$="/login"] - inputs: - username: "{{ .Config.username }}" - password: "{{ .Config.password }}" - remember: on - selectorinputs: - _token: - selector: input[name="_token"] - attribute: value - error: - - selector: form[action$="/login"] .text-red - - selector: h1:contains("503 Service Temporarily Unavailable") -# test: -# path: / -# selector: a[href$="/logout"] - -search: - paths: - - path: filterTorrents - inputs: - search: "{{ .Keywords }}" - sorting: "{{ .Config.sort }}" - direction: "{{ .Config.type }}" - qty: 100 - freeleech: "{{ if .Config.freeleech }}1{{ else }}{{ end }}" - - keywordsfilters: - - name: re_replace - args: ["[\\:\\-\\/\\|\\(\\)]+", " "] - - rows: - selector: div.table-responsive-line - - fields: - category: - selector: img.torrent-icon - attribute: src - filters: - - name: regexp - args: "/img/NewIcones/(.+?).png" - title_phase1: - selector: a.view-torrent - title_vostfr: - text: "{{ .Result.title_phase1 }}" - filters: - - name: re_replace - args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"] - title_phase2: - text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase1 }}{{ end }}" - title_multilang: - text: "{{ .Result.title_phase2 }}" - filters: - - name: re_replace - args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"] - title: - text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase2 }}{{ end }}" - download: - selector: a.view-torrent - attribute: href - filters: - - name: replace - args: ["/torrents/", "/download/"] - details: - selector: a.view-torrent - attribute: href - size: - selector: div.col-detail div.row div:nth-child(2) - seeders: - selector: div.bouton-s - leechers: - selector: div.bouton-l - grabs: - selector: div.bouton-c - date: - selector: div.col-detail div.row div span - filters: - - name: replace - args: ["il y a ", ""] - - name: replace - args: ["seconde", "second"] - - name: replace - args: ["heure", "hour"] - - name: replace - args: ["jour", "day"] - - name: replace - args: ["semaine", "week"] - - name: replace - args: ["mois", "month"] - - name: replace - args: ["an", "year"] - - name: append - args: " ago" - downloadvolumefactor: - case: - "span.badge-extra:contains('Freeleech')": 0 - "*": 1 - uploadvolumefactor: - case: - "span.badge-extra:contains('Double Upload')": 2 - "*": 1 - minimumratio: - text: 0.75 - minimumseedtime: - # 3 days (as seconds = 3 x 24 x 60 x 60) - text: 259200 -# UNIT3D diff --git a/src/Jackett.Common/Indexers/BaseIndexer.cs b/src/Jackett.Common/Indexers/BaseIndexer.cs index 0dbca304d..88597546c 100644 --- a/src/Jackett.Common/Indexers/BaseIndexer.cs +++ b/src/Jackett.Common/Indexers/BaseIndexer.cs @@ -316,18 +316,28 @@ namespace Jackett.Common.Indexers if (query.HasSpecifiedCategories) { var supportedCats = TorznabCaps.Categories.SupportedCategories(query.Categories); + if (supportedCats.Length == 0) { if (!isMetaIndexer) + { logger.Error($"All categories provided are unsupported in {Name}: {string.Join(",", query.Categories)}"); + } + return false; } + if (supportedCats.Length != query.Categories.Length && !isMetaIndexer) { - var unsupportedCats = query.Categories.Except(supportedCats); - logger.Warn($"Some of the categories provided are unsupported in {Name}: {string.Join(",", unsupportedCats)}"); + var unsupportedCats = query.Categories.Except(supportedCats).ToList(); + + if (unsupportedCats.Any()) + { + logger.Warn($"Some of the categories provided are unsupported in {Name}: {string.Join(",", unsupportedCats)}"); + } } } + return true; } diff --git a/src/Jackett.Common/Indexers/Definitions/Sharewood.cs b/src/Jackett.Common/Indexers/Definitions/Sharewood.cs deleted file mode 100644 index 852c64b3e..000000000 --- a/src/Jackett.Common/Indexers/Definitions/Sharewood.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Jackett.Common.Extensions; -using Jackett.Common.Models; -using Jackett.Common.Models.IndexerConfig; -using Jackett.Common.Services.Interfaces; -using Jackett.Common.Utils; -using Newtonsoft.Json.Linq; -using NLog; -using static Jackett.Common.Models.IndexerConfig.ConfigurationData; -using WebClient = Jackett.Common.Utils.Clients.WebClient; - -namespace Jackett.Common.Indexers.Definitions -{ - [ExcludeFromCodeCoverage] - public class ShareWood : IndexerBase - { - public override string Id => "sharewoodapi"; - public override string Name => "Sharewood API"; - public override string Description => "Sharewood is a Semi-Private FRENCH Torrent Tracker for GENERAL"; - public override string SiteLink { get; protected set; } = "https://www.sharewood.tv/"; - public override string Language => "fr-FR"; - public override string Type => "semi-private"; - - public override TorznabCapabilities TorznabCaps => SetCapabilities(); - - private readonly Dictionary _apiHeaders = new Dictionary - { - {"Accept", "application/json"}, - {"Content-Type", "application/json"} - }; - // API DOC: https://github.com/Jackett/Jackett/issues/10269 - private string SearchUrl => SiteLink + "api/" + configData.Passkey.Value; - private new ConfigurationDataPasskey configData => (ConfigurationDataPasskey)base.configData; - - public ShareWood(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps, ICacheService cs) - : base(configService: configService, - client: wc, - logger: l, - p: ps, - cacheService: cs, - configData: new ConfigurationDataPasskey()) - { - // requestDelay for API Limit (1 request per 4 seconds) - webclient.requestDelay = 4.1; - - var freeLeechOnly = new BoolConfigurationItem("Search freeleech only"); - configData.AddDynamic("freeleechonly", freeLeechOnly); - - var replaceMulti = new BoolConfigurationItem("Replace MULTi by another language in release name"); - configData.AddDynamic("replacemulti", replaceMulti); - - // Configure the language select option for MULTI - var languageSelect = new SingleSelectConfigurationItem("Replace MULTi by this language", new Dictionary - { - {"FRENCH", "FRENCH"}, - {"MULTi FRENCH", "MULTi FRENCH"}, - {"ENGLISH", "ENGLISH"}, - {"MULTi ENGLISH", "MULTi ENGLISH" }, - {"VOSTFR", "VOSTFR"}, - {"MULTi VOSTFR", "MULTi VOSTFR"} - }) - { Value = "FRENCH" }; - configData.AddDynamic("languageid", languageSelect); - - var replaceVostfr = new BoolConfigurationItem("Replace VOSTFR and SUBFRENCH with ENGLISH"); - configData.AddDynamic("replacevostfr", replaceVostfr); - - EnableConfigurableRetryAttempts(); - } - - private TorznabCapabilities SetCapabilities() - { - var 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 - }, - SupportsRawSearch = true - }; - - //CATEGORIES - //caps.Categories.AddCategoryMapping(1, TorznabCatType.Movies, "Vidéos"); - //caps.Categories.AddCategoryMapping(1, TorznabCatType.TV, "Vidéos"); - //caps.Categories.AddCategoryMapping(2, TorznabCatType.Audio, "Audio"); - //caps.Categories.AddCategoryMapping(3, TorznabCatType.PC, "Application"); - //caps.Categories.AddCategoryMapping(4, TorznabCatType.Books, "Ebooks"); - //caps.Categories.AddCategoryMapping(5, TorznabCatType.PCGames, "Jeu-Vidéo"); - //caps.Categories.AddCategoryMapping(6, TorznabCatType.OtherMisc, "Formation"); - //caps.Categories.AddCategoryMapping(7, TorznabCatType.XXX, "XXX"); - - //SUBCATEGORIES - caps.Categories.AddCategoryMapping(9, TorznabCatType.Movies, "Films"); - caps.Categories.AddCategoryMapping(10, TorznabCatType.TV, "Série"); - caps.Categories.AddCategoryMapping(11, TorznabCatType.MoviesOther, "Film Animation"); - caps.Categories.AddCategoryMapping(12, TorznabCatType.TVAnime, "Série Animation"); - caps.Categories.AddCategoryMapping(13, TorznabCatType.TVDocumentary, "Documentaire"); - caps.Categories.AddCategoryMapping(14, TorznabCatType.TVOther, "Emission TV"); - caps.Categories.AddCategoryMapping(15, TorznabCatType.TVOther, "Spectacle/Concert"); - caps.Categories.AddCategoryMapping(16, TorznabCatType.TVSport, "Sport"); - caps.Categories.AddCategoryMapping(17, TorznabCatType.AudioVideo, "Karaoké Vidéo"); - caps.Categories.AddCategoryMapping(18, TorznabCatType.AudioOther, "Karaoké"); - caps.Categories.AddCategoryMapping(20, TorznabCatType.Audio, "Musique"); - caps.Categories.AddCategoryMapping(21, TorznabCatType.AudioOther, "Podcast"); - caps.Categories.AddCategoryMapping(22, TorznabCatType.AudioOther, "Sample"); - caps.Categories.AddCategoryMapping(23, TorznabCatType.AudioAudiobook, "Ebook Audio"); - caps.Categories.AddCategoryMapping(24, TorznabCatType.BooksEBook, "BD"); - caps.Categories.AddCategoryMapping(25, TorznabCatType.BooksComics, "Comic"); - caps.Categories.AddCategoryMapping(26, TorznabCatType.BooksOther, "Manga"); - caps.Categories.AddCategoryMapping(27, TorznabCatType.Books, "Livre"); - caps.Categories.AddCategoryMapping(28, TorznabCatType.BooksMags, "Presse"); - caps.Categories.AddCategoryMapping(29, TorznabCatType.PC, "Application Linux"); - caps.Categories.AddCategoryMapping(30, TorznabCatType.PC0day, "Application Window"); - caps.Categories.AddCategoryMapping(31, TorznabCatType.PCMac, "Application Mac"); - caps.Categories.AddCategoryMapping(34, TorznabCatType.PCMobileiOS, "Application Smartphone/Tablette"); - caps.Categories.AddCategoryMapping(34, TorznabCatType.PCMobileAndroid, "Application Smartphone/Tablette"); - caps.Categories.AddCategoryMapping(35, TorznabCatType.PCMobileOther, "GPS"); - caps.Categories.AddCategoryMapping(36, TorznabCatType.PCGames, "Jeux Linux"); - caps.Categories.AddCategoryMapping(37, TorznabCatType.PCGames, "Jeux Windows"); - caps.Categories.AddCategoryMapping(39, TorznabCatType.ConsoleNDS, "Jeux Nintendo"); - caps.Categories.AddCategoryMapping(39, TorznabCatType.ConsoleWii, "Jeux Nintendo"); - caps.Categories.AddCategoryMapping(39, TorznabCatType.ConsoleWiiware, "Jeux Nintendo"); - caps.Categories.AddCategoryMapping(39, TorznabCatType.Console3DS, "Jeux Nintendo"); - caps.Categories.AddCategoryMapping(39, TorznabCatType.ConsoleWiiU, "Jeux Nintendo"); - caps.Categories.AddCategoryMapping(41, TorznabCatType.PCMobileAndroid, "PC/Mobile-Android"); - caps.Categories.AddCategoryMapping(42, TorznabCatType.PCGames, "Jeux Microsoft"); - caps.Categories.AddCategoryMapping(44, TorznabCatType.XXX, "XXX Films"); - caps.Categories.AddCategoryMapping(45, TorznabCatType.XXXOther, "XXX Hentai"); - caps.Categories.AddCategoryMapping(47, TorznabCatType.XXXImageSet, "XXX Images"); - caps.Categories.AddCategoryMapping(48, TorznabCatType.XXXOther, "XXX Jeu-Vidéo"); - caps.Categories.AddCategoryMapping(49, TorznabCatType.OtherMisc, "Formations Vidéos"); - caps.Categories.AddCategoryMapping(50, TorznabCatType.OtherMisc, "Formation Logiciels"); - caps.Categories.AddCategoryMapping(51, TorznabCatType.XXXOther, "XXX Ebooks"); - caps.Categories.AddCategoryMapping(52, TorznabCatType.AudioVideo, "Vidéos-Clips"); - - return caps; - } - - private string MultiRename(string term, string replacement) - { - replacement = " " + replacement + " "; - term = Regex.Replace(term, @"(?i)\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\b", replacement); - return term; - } - - private string VostfrRename(string term, string replacement) - { - term = Regex.Replace(term, @"(?i)\b(vostfr|subfrench)\b", replacement); - return term; - } - - private bool GetFreeLeech => ((BoolConfigurationItem)configData.GetDynamic("freeleechonly")).Value; - private bool GetReplaceMulti => ((BoolConfigurationItem)configData.GetDynamic("replacemulti")).Value; - private string GetLang => ((SingleSelectConfigurationItem)configData.GetDynamic("languageid")).Value; - private bool GetReplaceVostfr => ((BoolConfigurationItem)configData.GetDynamic("replacevostfr")).Value; - - public override async Task ApplyConfiguration(JToken configJson) - { - LoadValuesFromJson(configJson); - - if (configData.Passkey.Value.Length != 32) - { - throw new Exception("Invalid Passkey configured. Expected length: 32"); - } - - var releases = await PerformQuery(new TorznabQuery()); - - await ConfigureIfOK(string.Empty, releases.Any(), () => throw new Exception("Could not find releases.")); - - return IndexerConfigurationStatus.Completed; - } - - protected override async Task> PerformQuery(TorznabQuery query) - { - var releases = new List(); - - var categoryMapping = MapTorznabCapsToTrackers(query).Distinct().ToList(); - - if (!categoryMapping.Any()) - { - // NO CATEGORIES ==> RSS SEARCH - categoryMapping.Add("1000"); - } - - var term = query.GetQueryString().Trim(); - term = Regex.Replace(term, @"[\:\-\/\|\(\)]+", " "); - - foreach (var categoryId in categoryMapping) - { - var searchUrl = SearchUrl; - - var parameters = new NameValueCollection - { - { "limit", categoryId != "1000" ? "25" : "100" } - }; - - if (categoryId != "1000") - { - parameters.Set("subcategory", categoryId); - } - - if (term.IsNotNullOrWhiteSpace()) - { - parameters.Set("name", term); - searchUrl += "/search"; - } - else - { - searchUrl += "/last-torrents"; - } - - if (parameters.Count > 0) - { - searchUrl += $"?{parameters.GetQueryString()}"; - } - - var response = await RequestWithCookiesAsync(searchUrl); - if (response.Status == HttpStatusCode.Unauthorized) - { - response = await RequestWithCookiesAsync(searchUrl); - } - else if (response.Status != HttpStatusCode.OK) - { - throw new Exception($"Unknown error in search: {response.ContentString}"); - } - - try - { - var rows = JArray.Parse(response.ContentString); - foreach (var row in rows) - { - var id = row.Value("id"); - var link = new Uri($"{SearchUrl}/{id}/download"); - var slug = row.Value("slug"); - var details = new Uri($"{SiteLink}torrents/{slug}.{id}"); - - var cat = row.Value("subcategory_id"); - if (Convert.ToInt32(categoryId) != 1000) - { - // USE CATEGORIES OR SUBCATEGORIES - cat = row.Value(Convert.ToInt32(categoryId) < 8 ? "category_id" : "subcategory_id"); - } - - var dlVolumeFactor = row.Value("free") ? 0 : 1; - var ulVolumeFactor = row.Value("doubleup") ? 2 : 1; - - var title = row.Value("name"); - - //SPECIAL CASES - if (GetFreeLeech && dlVolumeFactor == 1) - { - continue; - } - - if (GetReplaceMulti) - { - title = MultiRename(title, GetLang); - } - - if (GetReplaceVostfr) - { - title = VostfrRename(title, "ENGLISH"); - } - - var release = new ReleaseInfo - { - Guid = details, - Details = details, - Link = link, - Title = title, - Category = MapTrackerCatToNewznab(cat), - PublishDate = DateTime.Parse(row.Value("created_at"), CultureInfo.InvariantCulture), - Size = ParseUtil.GetBytes(row.Value("size")), - Grabs = row.Value("times_completed"), - Seeders = row.Value("seeders"), - Peers = row.Value("leechers") + row.Value("seeders"), - DownloadVolumeFactor = dlVolumeFactor, - UploadVolumeFactor = ulVolumeFactor, - MinimumRatio = 1, - MinimumSeedTime = 259200 // 72 hours - }; - - releases.Add(release); - } - } - catch (Exception ex) - { - OnParseError(response.ContentString, ex); - } - } - return releases; - } - - public override async Task Download(Uri link) - { - var response = await RequestWithCookiesAsync(link.ToString()); - - if (response.Status == HttpStatusCode.Unauthorized) - { - response = await RequestWithCookiesAsync(link.ToString()); - } - - if (response.Status != HttpStatusCode.OK) - { - logger.Debug("Unknown error in download: {0}", response.ContentString); - - throw new Exception($"Unexpected status code: {(int)response.Status} ({response.Status})"); - } - - return response.ContentBytes; - } - } -} diff --git a/src/Jackett.Updater/Program.cs b/src/Jackett.Updater/Program.cs index 8b08e5ef1..8b02b32a2 100644 --- a/src/Jackett.Updater/Program.cs +++ b/src/Jackett.Updater/Program.cs @@ -622,6 +622,7 @@ namespace Jackett.Updater "Definitions/shareisland.yml", // switch to *-API #8682 "Definitions/sharespacedb.yml", "Definitions/shareuniversity.yml", + "Definitions/sharewood.yml", // switch to *-API #10269 "Definitions/sharingue.yml", "Definitions/shellife.yml", "Definitions/sharkpt.yml",