mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-17 17:34:09 +02:00
sharewood-api: migrate to API (#15628)
This commit is contained in:
203
src/Jackett.Common/Definitions/sharewood-api.yml
Normal file
203
src/Jackett.Common/Definitions/sharewood-api.yml
Normal file
@@ -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 <a href=\"https://www.sharewood.tv/\" target=\"_blank\">Sharewood</a> profile on the <i>My Profile</i> page and scrolling down to the <b>Passkey</b> 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
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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<string, string> _apiHeaders = new Dictionary<string, string>
|
||||
{
|
||||
{"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<string, string>
|
||||
{
|
||||
{"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>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
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<IndexerConfigurationStatus> 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<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
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<string>("id");
|
||||
var link = new Uri($"{SearchUrl}/{id}/download");
|
||||
var slug = row.Value<string>("slug");
|
||||
var details = new Uri($"{SiteLink}torrents/{slug}.{id}");
|
||||
|
||||
var cat = row.Value<string>("subcategory_id");
|
||||
if (Convert.ToInt32(categoryId) != 1000)
|
||||
{
|
||||
// USE CATEGORIES OR SUBCATEGORIES
|
||||
cat = row.Value<string>(Convert.ToInt32(categoryId) < 8 ? "category_id" : "subcategory_id");
|
||||
}
|
||||
|
||||
var dlVolumeFactor = row.Value<bool>("free") ? 0 : 1;
|
||||
var ulVolumeFactor = row.Value<bool>("doubleup") ? 2 : 1;
|
||||
|
||||
var title = row.Value<string>("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<string>("created_at"), CultureInfo.InvariantCulture),
|
||||
Size = ParseUtil.GetBytes(row.Value<string>("size")),
|
||||
Grabs = row.Value<long>("times_completed"),
|
||||
Seeders = row.Value<long>("seeders"),
|
||||
Peers = row.Value<long>("leechers") + row.Value<long>("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<byte[]> 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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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",
|
||||
|
Reference in New Issue
Block a user