Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef637792c8 | ||
|
|
09a2e8c084 | ||
|
|
d3161823ad | ||
|
|
289844099c | ||
|
|
98a916b4c1 | ||
|
|
69ff142596 | ||
|
|
80fd717970 | ||
|
|
ab083cdec8 | ||
|
|
e16454f30c | ||
|
|
23c13969ae | ||
|
|
dcc3f2df63 | ||
|
|
10f7f83ecf | ||
|
|
b5e6eac3e3 | ||
|
|
9df337c3c2 | ||
|
|
d2f0e5ada7 | ||
|
|
d3aa897b1a | ||
|
|
0abcf401bb | ||
|
|
560d257f5b | ||
|
|
1a619e698a | ||
|
|
52aea3de5a | ||
|
|
8fa1268de2 | ||
|
|
5a4c1282ef | ||
|
|
3aecc53b5f | ||
|
|
328f652e1b | ||
|
|
52a56f5088 | ||
|
|
5e4c64bf68 | ||
|
|
930e9db67a | ||
|
|
27627aa79e | ||
|
|
ddb94e3f92 | ||
|
|
f1f5b7028d | ||
|
|
90959c1fc3 | ||
|
|
3f79aa62d9 | ||
|
|
cd1d30fc4f | ||
|
|
3093651f89 | ||
|
|
d969305cbe | ||
|
|
22119dab16 | ||
|
|
23958ffba7 | ||
|
|
d9673501fc | ||
|
|
f9341818da | ||
|
|
22ab8aca3d |
13
README.md
@@ -16,9 +16,11 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
#### Supported Private Trackers
|
||||
* Abnormal
|
||||
* AlphaRatio
|
||||
* AlphaReign
|
||||
* Andraste
|
||||
* AnimeBytes
|
||||
* AnimeTorrents
|
||||
* Apollo (XANAX)
|
||||
* Avistaz
|
||||
* BakaBT
|
||||
* bB
|
||||
@@ -28,17 +30,20 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* BIT-HDTV
|
||||
* BitMeTV
|
||||
* BitSoup
|
||||
* BlueTigers
|
||||
* Blu-bits
|
||||
* BTN
|
||||
* CinemaZ
|
||||
* DanishBits
|
||||
* Demonoid
|
||||
* DigitalHive
|
||||
* EoT-Forum
|
||||
* Ethor.net (Thor's Land)
|
||||
* FileList
|
||||
* Freshon
|
||||
* FunFile
|
||||
* Fuzer
|
||||
* Ghost City
|
||||
* HD4Free
|
||||
* HD-Space
|
||||
* HD-Torrents
|
||||
* Hebits
|
||||
@@ -64,8 +69,11 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Shareisland
|
||||
* Shazbat
|
||||
* SpeedCD
|
||||
* The Horror Charnel
|
||||
* The New Retro
|
||||
* TehConnection
|
||||
* Torrent Network
|
||||
* Torrent Sector Crew
|
||||
* TorrentBytes
|
||||
* TorrentDay
|
||||
* TorrentHeaven
|
||||
@@ -73,7 +81,10 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* TorrentShack
|
||||
* Torrent-Syndikat
|
||||
* TransmitheNet
|
||||
* Tspate
|
||||
* TV Chaos UK
|
||||
* TV-Vault
|
||||
* UHDBits
|
||||
* World-In-HD
|
||||
* WorldOfP2P
|
||||
* x264
|
||||
|
||||
BIN
src/Jackett/Content/logos/alphareign.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/Jackett/Content/logos/apollo.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/Jackett/Content/logos/blubits.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
BIN
src/Jackett/Content/logos/eotforum.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/Jackett/Content/logos/ethor.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
src/Jackett/Content/logos/hd4free.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
src/Jackett/Content/logos/newretro.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
src/Jackett/Content/logos/thehorrorcharnel.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src/Jackett/Content/logos/torrentsectorcrew.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
src/Jackett/Content/logos/tspate.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/Jackett/Content/logos/tvvault.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
@@ -173,7 +173,7 @@ namespace Jackett
|
||||
var headerParts = headerString.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var headers = new List<string[]>();
|
||||
var headerCount = 0;
|
||||
HttpStatusCode status = HttpStatusCode.InternalServerError;
|
||||
HttpStatusCode status = HttpStatusCode.NotImplemented;
|
||||
var cookieBuilder = new StringBuilder();
|
||||
var cookies = new List<Tuple<string, string>>();
|
||||
foreach (var headerPart in headerParts)
|
||||
@@ -216,6 +216,31 @@ namespace Jackett
|
||||
cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2);
|
||||
}
|
||||
|
||||
// add some debug output to track down the problem causing people getting InternalServerError results
|
||||
if (status == HttpStatusCode.NotImplemented || status == HttpStatusCode.InternalServerError)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnErrorMessage("got NotImplemented/InternalServerError");
|
||||
OnErrorMessage("request.Method: " + curlRequest.Method);
|
||||
OnErrorMessage("request.Url: " + curlRequest.Url);
|
||||
OnErrorMessage("request.Cookies: " + curlRequest.Cookies);
|
||||
OnErrorMessage("request.Referer: " + curlRequest.Referer);
|
||||
OnErrorMessage("request.RawPOSTDdata: " + curlRequest.RawPOSTDdata);
|
||||
OnErrorMessage("cookies: "+ cookieBuilder.ToString().Trim());
|
||||
OnErrorMessage("headerString:\n" + headerString);
|
||||
|
||||
foreach (var headerPart in headerParts)
|
||||
{
|
||||
OnErrorMessage("headerParts: "+headerPart);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnErrorMessage(string.Format("CurlHelper: error while handling NotImplemented/InternalServerError:\n{0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
var contentBytes = Combine(contentBuffers.ToArray());
|
||||
var curlResponse = new CurlResponse(headers, contentBytes, status, cookieBuilder.ToString().Trim());
|
||||
return curlResponse;
|
||||
|
||||
82
src/Jackett/Definitions/alphareign.yml
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
site: alphareign
|
||||
name: AlphaReign
|
||||
description: "A DHT only tracker"
|
||||
language: en-us
|
||||
|
||||
links:
|
||||
- https://alphareign.se
|
||||
|
||||
caps:
|
||||
categories:
|
||||
Movies: Movies
|
||||
Movies/HD: Movies/HD
|
||||
Movies/SD: Movies/SD
|
||||
TV: TV
|
||||
TV/HD: TV/HD
|
||||
TV/SD: TV/SD
|
||||
Other: Other
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /login
|
||||
method: form
|
||||
form: form[action^="/login"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- path: /login
|
||||
selector: div.auth-form > div > div.alert-danger
|
||||
|
||||
test:
|
||||
path: settings
|
||||
selector: a[href="/logout"]
|
||||
|
||||
ratio:
|
||||
text: "∞"
|
||||
|
||||
search:
|
||||
path: /
|
||||
method: get
|
||||
inputs:
|
||||
query: "{{ .Keywords }}"
|
||||
rows:
|
||||
selector: .result
|
||||
fields:
|
||||
title:
|
||||
attribute: data-name
|
||||
details:
|
||||
selector: a[href^="/torrent"]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="magnet:"]
|
||||
attribute: href
|
||||
date:
|
||||
attribute: data-added
|
||||
size:
|
||||
attribute: data-size
|
||||
seeders:
|
||||
attribute: data-seeders
|
||||
leechers:
|
||||
attribute: data-leechers
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
category:
|
||||
case:
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"HD\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"720p\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"1080p\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"SD\")": "Movies/SD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\")": "Movies"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\") + span[class=\"label label-info\"]:contains(\"HD\")": "TV/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\") + span[class=\"label label-info\"]:contains(\"SD\")": "TV/SD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\")": "TV"
|
||||
"*": "Other"
|
||||
91
src/Jackett/Definitions/apollo.yml
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
site: apollo
|
||||
name: Apollo
|
||||
description: "A music tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://xanax.rip
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Audio # Music
|
||||
2: PC # Applications
|
||||
3: Books # E-Books
|
||||
4: Audio/Audiobook # Audiobooks
|
||||
5: Movies # E-Learning Videos
|
||||
6: TV # Comedy
|
||||
7: Books/Comics # Comics
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
keeplogged: 1
|
||||
login: "Log in"
|
||||
error:
|
||||
- selector: form#loginform > span.warning
|
||||
test:
|
||||
path: torrents.php
|
||||
|
||||
ratio:
|
||||
path: torrents.php
|
||||
selector: li#stats_ratio > span
|
||||
|
||||
search:
|
||||
path: torrents.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}filter_cat[{{.}}]=1&{{end}}"
|
||||
searchstr: "{{ .Query.Keywords }}"
|
||||
order_by: time
|
||||
order_way: desc
|
||||
action: basic
|
||||
searchsubmit: 1
|
||||
rows:
|
||||
selector: table#torrent_table > tbody > tr.torrent
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="torrents.php?action=download&id="]
|
||||
attribute: href
|
||||
description:
|
||||
selector: div.group_info
|
||||
remove: span
|
||||
title:
|
||||
selector: div.group_info
|
||||
remove: span, div.tags
|
||||
category:
|
||||
selector: td.cats_col
|
||||
case:
|
||||
div.cats_music: 1
|
||||
div.cats_applications: 2
|
||||
div.cats_ebooks: 3
|
||||
div.cats_audiobooks: 4
|
||||
div.cats_elearningvideos: 5
|
||||
div.cats_comedy: 6
|
||||
div.cats_comics: 7
|
||||
comments:
|
||||
selector: a[href^="torrents.php?id="]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(3)
|
||||
date:
|
||||
selector: td:nth-child(4)
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
146
src/Jackett/Definitions/blubits.yml
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
site: blubits
|
||||
name: Blu-bits
|
||||
description: "A HD tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://blu-bits.com/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Movies
|
||||
14: Movies/BluRay # Full Blu-ray
|
||||
54: Movies/HD # HD-DVD
|
||||
16: Movies/HD # Remux
|
||||
55: Movies/HD # 2160p
|
||||
15: Movies/HD # 1080p
|
||||
19: Movies/HD # 1080i
|
||||
18: Movies/HD # 720p
|
||||
|
||||
# Documentaries
|
||||
21: Movies/BluRay # Full Blu-ray
|
||||
39: Movies/HD # Remux
|
||||
56: Movies/HD # 2160p
|
||||
23: Movies/HD # 1080p
|
||||
24: Movies/HD # 1080i
|
||||
25: Movies/HD # 720p
|
||||
|
||||
# TV Series
|
||||
27: TV/HD # Full Blu-ray
|
||||
40: TV/HD # Remux
|
||||
28: TV/HD # 1080p
|
||||
29: TV/HD # 1080i
|
||||
30: TV/HD # 720p
|
||||
|
||||
# HDTV
|
||||
35: TV/HD # 1080i
|
||||
36: TV/HD # 720p
|
||||
|
||||
# XXX
|
||||
59: XXX # Full Blu-ray
|
||||
46: XXX # 1080p
|
||||
51: XXX # 720p
|
||||
|
||||
# Music
|
||||
53: Audio/Video # Full Blu-ray
|
||||
57: Audio/Video # Remux
|
||||
45: Audio/Video # 1080p
|
||||
58: Audio/Video # 720p
|
||||
38: Audio/Lossless # Flac
|
||||
|
||||
41: TV/Sport # Sports
|
||||
42: TV/Anime # Anime
|
||||
44: PC # Windows Apps
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: table.lista > tbody > tr > td.lista > span[style="color:#FF0000;"]
|
||||
test:
|
||||
path: index.php
|
||||
selector: ul#navlist
|
||||
|
||||
ratio:
|
||||
path: index.php
|
||||
selector: "ul#navlist > li:contains(\"Ratio: \")"
|
||||
filters:
|
||||
- name: split
|
||||
args: ["\u00a0", 1]
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
inputs:
|
||||
search: "{{ .Query.Keywords }}"
|
||||
page: torrents
|
||||
options: 0
|
||||
active: 0
|
||||
rows:
|
||||
selector: div.b-content > table.lista > tbody > tr:has(a[href^="index.php?page=torrents&category="])
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["View details: ", ""]
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
comments:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
size:
|
||||
selector: p
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", ""]
|
||||
- name: regexp
|
||||
args: "\\|\\s+Size:\\s+([\\w\\d\\.,]+ \\w\\w)\\s+\\|"
|
||||
date:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "<center>Added:(.*?)</center>"
|
||||
grabs:
|
||||
selector: a[href^="index.php?page=torrent_history&id="]
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: a[title="Click here to view peers details"]:nth-child(1)
|
||||
leechers:
|
||||
selector: a[title="Click here to view peers details"]:nth-child(2)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[alt="gold"]: "0"
|
||||
img[alt="silver"]: "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[alt="2x Upload Multiplier"]: "2"
|
||||
img[alt="3x Upload Multiplier"]: "3"
|
||||
img[alt="4x Upload Multiplier"]: "4"
|
||||
img[alt="5x Upload Multiplier"]: "5"
|
||||
img[alt="6x Upload Multiplier"]: "6"
|
||||
img[alt="7x Upload Multiplier"]: "7"
|
||||
img[alt="8x Upload Multiplier"]: "8"
|
||||
img[alt="9x Upload Multiplier"]: "9"
|
||||
img[alt="10x Upload Multiplier"]: "10"
|
||||
"*": "1"
|
||||
176
src/Jackett/Definitions/eotforum.yml
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
site: eotforum
|
||||
name: EoT-Forum
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- http://eot-forum.net
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Filme
|
||||
14: Movies/SD # SD XviD
|
||||
15: Movies/SD # SD x264
|
||||
16: Movies/HD # HD
|
||||
68: Movies/HD # UHD
|
||||
17: Movies/3D # 3D
|
||||
18: Movies/DVD # DVD-R
|
||||
19: Movies # Pack
|
||||
20: Movies # International
|
||||
21: XXX # XXX
|
||||
|
||||
# Serien/TV
|
||||
23: TV/SD # SD XviD
|
||||
24: TV/SD # SD x264
|
||||
25: TV/HD # HD
|
||||
26: TV/SD # DVD-R
|
||||
27: TV # Pack
|
||||
28: TV # International
|
||||
29: TV/Sport # Sport
|
||||
|
||||
# Dokus
|
||||
31: TV/Documentary # SD XviD
|
||||
32: TV/Documentary # SD x264
|
||||
33: TV/Documentary # HD
|
||||
34: TV/Documentary # 3D
|
||||
35: TV/Documentary # Pack
|
||||
67: TV/Documentary # DVD-R
|
||||
36: TV/Documentary # International
|
||||
|
||||
# Audio
|
||||
38: Audio # Charts
|
||||
39: Audio/MP3 # MP3
|
||||
40: Audio/Lossless # Flac
|
||||
41: Audio # Pack
|
||||
42: Audio/Video # MusikVideo
|
||||
43: Audio/Audiobook # Hörbücher
|
||||
|
||||
# Spiele
|
||||
45: PC/Games # Windows
|
||||
46: PC/Mac # MacOS
|
||||
47: Console/PS4 # Sony PS
|
||||
48: Console/Xbox # Microsoft XBox
|
||||
49: Console/NDS # Nintendo
|
||||
50: PC/Games # Linux
|
||||
51: Console # Andere
|
||||
|
||||
# Software
|
||||
53: PC # Windows
|
||||
54: PC/Mac # MacOS
|
||||
55: PC # Linux
|
||||
56: PC/Phone-Android # Android
|
||||
57: PC/Phone-IOS # Apple IOS
|
||||
58: PC/Phone-Other # Andere
|
||||
|
||||
# Sonstiges
|
||||
60: Books # EBooks
|
||||
61: Other # Bilder
|
||||
62: TV/Anime # Anime
|
||||
63: Other # MISC
|
||||
64: XXX # XXX-Bilder/EBooks/Audio
|
||||
|
||||
# EOT-Specials
|
||||
66: Other # Special
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
rememberme: "forever"
|
||||
submit: "Login"
|
||||
error:
|
||||
- selector: td.lista[align="center"][colspan="2"] > span
|
||||
test:
|
||||
path: index.php
|
||||
selector: img[alt="Ratio"]
|
||||
|
||||
ratio:
|
||||
path: index.php
|
||||
selector: img[alt="Ratio"] + font
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
inputs:
|
||||
page: "torrents"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
options: "0"
|
||||
active: "0"
|
||||
gold: "0"
|
||||
rows:
|
||||
selector: table.lista > tbody > tr:has(a[href^="index.php?page=torrent-details&id="])
|
||||
dateheaders:
|
||||
selector: ":has(td.header > b)"
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Torrents vom ", ""]
|
||||
- name: replace
|
||||
args: ["Januar", "January"]
|
||||
- name: replace
|
||||
args: ["Februar", "February"]
|
||||
- name: replace
|
||||
args: ["March", "März"]
|
||||
- name: replace
|
||||
args: ["Mai", "May"]
|
||||
- name: replace
|
||||
args: ["Juni", "June"]
|
||||
- name: replace
|
||||
args: ["Juli", "July"]
|
||||
- name: replace
|
||||
args: ["Oktober", "October"]
|
||||
- name: replace
|
||||
args: ["Dezember", "December"]
|
||||
- name: dateparse
|
||||
args: "02.January.2006"
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Details anzeigen: ", ""]
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
comments:
|
||||
selector: a[href*="#comments"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(3)
|
||||
grabs:
|
||||
selector: td:nth-child(5) > font:nth-child(3)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: td:nth-child(5) > font:nth-child(1)
|
||||
leechers:
|
||||
selector: td:nth-child(5) > font:nth-child(2)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[alt="gold"]: "0"
|
||||
img[alt="silver"]: "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[alt="2x Upload Multiplier"]: "2"
|
||||
img[alt="3x Upload Multiplier"]: "3"
|
||||
img[alt="4x Upload Multiplier"]: "4"
|
||||
img[alt="5x Upload Multiplier"]: "5"
|
||||
img[alt="6x Upload Multiplier"]: "6"
|
||||
img[alt="7x Upload Multiplier"]: "7"
|
||||
img[alt="8x Upload Multiplier"]: "8"
|
||||
img[alt="9x Upload Multiplier"]: "9"
|
||||
img[alt="10x Upload Multiplier"]: "10"
|
||||
"*": "1"
|
||||
113
src/Jackett/Definitions/ethor.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
site: ethor
|
||||
name: Ethor.net (Thor's Land)
|
||||
description: "A French gerneral tracker"
|
||||
language: fr-fr
|
||||
links:
|
||||
- https://ethor.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
22: PC # Applications/Divers
|
||||
1: PC # Applications/PC ISO
|
||||
44: PC # Applications/Portable
|
||||
47: Movies/BluRay # Films/Bluray
|
||||
20: Movies/DVD # Films/DVDr
|
||||
42: Movies/HD # Films/HD Rip
|
||||
19: Movies/SD # Films/SD Rip
|
||||
5: Movies/SD # Films/VCD
|
||||
4: PC/Games # Jeux/PC
|
||||
41: Console # Jeux/Portable
|
||||
34: Console/PS4 # Jeux/PS2-PS3
|
||||
38: Console/Wii # Jeux/Wii-GC
|
||||
40: Console/Xbox # Jeux/Xbox360
|
||||
6: Audio # Musique
|
||||
37: Audio/Video # Musique/Video
|
||||
48: TV/HD # Série-Télé/Bluray
|
||||
45: TV/SD # Série-Télé/DVDr
|
||||
43: TV/HD # Série-Télé/HD Rip
|
||||
7: TV/SD # Série-Télé/SD Rip
|
||||
23: Books # E-Books
|
||||
46: Other # Évé. sportif
|
||||
36: Other # Kidz
|
||||
25: Other # Misc
|
||||
9: XXX # XXX
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
type: text
|
||||
label: Cookie
|
||||
|
||||
login:
|
||||
method: cookie
|
||||
inputs:
|
||||
cookie: "{{ .Config.cookie }}"
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
ratio:
|
||||
path: browse.php
|
||||
selector: span#ratioRatio
|
||||
|
||||
search:
|
||||
path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
advcat: "0"
|
||||
incldead: "1"
|
||||
stype: "b"
|
||||
dp: "0"
|
||||
isUserClick: "0"
|
||||
rows:
|
||||
selector: p + table > tbody > tr:has(a[href^="/details.php"])
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["/details.php", "/download.php"]
|
||||
title:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
category:
|
||||
selector: a[href^="/browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
attribute: href
|
||||
files:
|
||||
selector: a[href*="#filelist"]
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
grabs:
|
||||
selector: td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: append
|
||||
args: " -05:00" # timezone offset
|
||||
- name: dateparse
|
||||
args: "2006-01-0215:04:05 -07:00"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"img[title^=\"Freeleech: \"]": "0"
|
||||
"img[title^=\"Half Freeleech: \"]": "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
117
src/Jackett/Definitions/newretro.yml
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
site: newretro
|
||||
name: The New Retro
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- http://new-retro.ddns.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
101: TV/Anime # Filme - Animie
|
||||
102: Movies/BluRay # Filme - Bluray
|
||||
131: Movies/Other # Filme - Bollywood
|
||||
103: Movies/DVD # Filme - DVD
|
||||
104: Movies/DVD # Filme - DVD-R
|
||||
132: Movies/DVD # Filme - HD2DVD
|
||||
130: Movies # Filme - Klassiker
|
||||
105: Movies # Filme - x264
|
||||
106: Movies # Filme - XviD / DivX
|
||||
69: XXX # XXX
|
||||
124: Audio # Musik - Alben
|
||||
122: Audio/Audiobook # Musik - Hörbuch
|
||||
123: Audio # Musik - Mixe
|
||||
133: Audio/MP3 # Musik - MP3
|
||||
125: Audio/Video # Musik - Video
|
||||
113: PC # Programme - Linux
|
||||
114: PC/Mac # Programme - Mac
|
||||
115: PC # Programme - Windows
|
||||
117: TV # Allgemein - Serien
|
||||
116: TV/Documentary # Serien - Dokus
|
||||
118: TV/Sport # Serien - Sport
|
||||
119: Other # Bilder
|
||||
120: Books # Ebook
|
||||
127: Other # Für Unsere kleinsten
|
||||
121: Other # Handy Stuff
|
||||
129: Other # Sonstiges
|
||||
109: Other # Spiele - Handy
|
||||
112: Console # Spiele - Konsolen
|
||||
111: PC/Games # Spiele - Mac / Linux
|
||||
110: PC/Games # Spiele - PC
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: table.tableinborder:contains("Anmeldung Gescheitert!") > tbody > tr > td.tablea
|
||||
test:
|
||||
path: /usercp.php
|
||||
|
||||
ratio:
|
||||
path: /usercp.php
|
||||
selector: div#lmtd table > tbody > tr:contains("Ratio:") > td:nth-child(2)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
showsearch: "1"
|
||||
orderby: "added"
|
||||
sort: "desc"
|
||||
incldead: "1"
|
||||
|
||||
rows:
|
||||
selector: table.tableinborder[summary] > tbody > tr
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?torrent="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="details.php?id="]:has(b)
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(4) > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(1)
|
||||
grabs:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(3) > b
|
||||
files:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(2)
|
||||
seeders:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(1)
|
||||
leechers:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(3)
|
||||
date:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(5)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", " "]
|
||||
- name: dateparse
|
||||
args: "02.01.2006 15:04:05"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"font[color=\"red\"]:contains(\"Only Upload\")": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
101
src/Jackett/Definitions/thehorrorcharnel.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
site: thehorrorcharnel
|
||||
name: The Horror Charnel
|
||||
description: "A Horror, Cult, Sleaze, Sci-Fi & more tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://horrorcharnel.org
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Movies
|
||||
2: TV
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
|
||||
login:
|
||||
path: loginto.php
|
||||
method: form
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
use_sslvalue=: "on"
|
||||
perm_ssl: "1"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: "div#base_content > table.mainouter > tbody > tr > td.outer > table.main > tbody > tr > td:has(h2)"
|
||||
test:
|
||||
path: usercp.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: td.navi_top:contains("Deine Ratio:")
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Deine Ratio: ", ""]
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
#$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: 1
|
||||
|
||||
rows:
|
||||
selector: p + table > tbody > tr:has(a[href^="details.php?id="])
|
||||
after: 1
|
||||
fields:
|
||||
title:
|
||||
selector: a[onmouseover][href^="details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: <b>(.*?)</b>
|
||||
description:
|
||||
selector: td[colspan="13"]
|
||||
remove: a
|
||||
category:
|
||||
text: 1
|
||||
comments:
|
||||
selector: a[onmouseover][href^="details.php"]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(6)
|
||||
size:
|
||||
selector: td:nth-child(9)
|
||||
seeders:
|
||||
selector: td:nth-child(11)
|
||||
leechers:
|
||||
selector: td:nth-child(12)
|
||||
date:
|
||||
selector: td:nth-child(8)
|
||||
grabs:
|
||||
selector: td:nth-child(10)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="free.gif"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
175
src/Jackett/Definitions/torrentsectorcrew.yml
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
site: torrentsectorcrew
|
||||
name: Torrent Sector Crew
|
||||
description: "A German general tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- https://tsctracker.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Apps
|
||||
65: PC/Phone-Android # Android
|
||||
83: PC/Phone-IOS # iOS
|
||||
107: PC/0day # Linux
|
||||
48: PC/Mac # MAC
|
||||
109: PC # Sonstige
|
||||
22: PC/0day # Win
|
||||
|
||||
# Audio
|
||||
24: Audio/Audiobook # aBooks
|
||||
104: Audio # Disco's
|
||||
38: Audio/Audiobook # Hörspiel
|
||||
6: Audio # Musik
|
||||
82: Audio # Tracks
|
||||
29: Audio/Video # Videos
|
||||
|
||||
# Doku
|
||||
113: TV/Documentary # 3D
|
||||
76: TV/Documentary # HD
|
||||
78: TV/Documentary # Packs
|
||||
75: TV/Documentary # SD
|
||||
114: TV/Documentary # Sonstige
|
||||
77: TV/Documentary # Untouched
|
||||
|
||||
# Filme
|
||||
54: Movies/HD # 1080p
|
||||
5: Movies/3D # 3D
|
||||
55: Movies/HD # 720p
|
||||
111: Movies # Anime
|
||||
43: Movies/BluRay # BluRay
|
||||
20: Movies/DVD # DVDR
|
||||
120: Movies/Foreign # Int.
|
||||
119: Movies # Remux
|
||||
121: Movies/HD # UHD
|
||||
36: Movies/HD # x264
|
||||
19: Movies/SD # XviD
|
||||
|
||||
# Serien
|
||||
112: TV/Anime # Anime
|
||||
69: TV/HD # HD
|
||||
72: TV/Foreign # Int.
|
||||
68: TV # Packs
|
||||
66: TV/SD # SD
|
||||
108: TV # TV-Shows
|
||||
|
||||
# Sonstige
|
||||
117: Other # Diverses
|
||||
28: Books # eBooks
|
||||
42: TV/Sport # Sport
|
||||
103: Other # Tutorials
|
||||
9: Other # Wallpaper
|
||||
64: XXX # XXX
|
||||
|
||||
# Spiele
|
||||
115: PC/Mac # MAC
|
||||
37: Console/NDS # Nintendo
|
||||
4: PC/Games # PC
|
||||
58: Console/PS4 # PSX
|
||||
116: Other # Sonstige
|
||||
50: Console/Xbox # XBOX
|
||||
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: pin
|
||||
type: text
|
||||
label: Pin
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
|
||||
login:
|
||||
path: landing.php
|
||||
method: form
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
pin: "{{ .Config.pin }}"
|
||||
error:
|
||||
- selector: "#login_box_desc"
|
||||
test:
|
||||
path: my.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: td.navi_top:contains("Deine Ratio:")
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Deine Ratio: ", ""]
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
orderby: "added"
|
||||
sort: desc
|
||||
rows:
|
||||
selector: "h2 +p + br + table.tablebrowse > tbody > tr[style=\"height: 45px;\"], tr:contains(\"Weiter\") > td > table.tablebrowse > tbody > tr[style=\"height: 45px;\"]"
|
||||
fields:
|
||||
title:
|
||||
selector: a[title][href^="details.php"]
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href*="cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href*="&tocomm="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download_ssl.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(3)
|
||||
grabs:
|
||||
selector: td:nth-child(9)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["-mal", ""]
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
remove: br
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02.01.200615:04:05"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"font:contains(\"[OnlyUpload]\")": "0"
|
||||
"font:contains(\"[-40 Download]\")": "0.6"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
143
src/Jackett/Definitions/tspate.yml
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
site: tspate
|
||||
name: Tspate
|
||||
description: "A French gerneral tracker"
|
||||
language: fr-fr
|
||||
links:
|
||||
- https://www.tspate.me/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
50: TV/Anime # Anime: Tout
|
||||
40: PC # Apps: Windows
|
||||
41: PC/Mac # Apps: Mac
|
||||
42: PC/Phone-Android # Apps: Android
|
||||
43: PC/Phone-IOS # Apps: Ipod/Iphone
|
||||
44: PC # Apps: Autres
|
||||
45: PC # Apps: Linux
|
||||
27: Books # E-Books: Livres
|
||||
28: Books # E-Books: Audio
|
||||
29: Books/Comics # E-Books: Comics
|
||||
30: Books # E-Books: Mangas
|
||||
32: Books # E-Books: Bds
|
||||
31: Books # E-Books: Presse
|
||||
63: Books # E-Books: ePUB
|
||||
1: Movies/SD # Films: DVDRip
|
||||
2: Movies/SD # Films: DVDRip VOSTFR
|
||||
3: Movies/DVD # Films: DVD-R
|
||||
4: Movies/HD # Films: 720p
|
||||
5: Movies/HD # Films: 1080p
|
||||
6: Movies/BluRay # Films: BluRay x264 et x265
|
||||
7: Movies/3D # Films: BluRay-3D
|
||||
11: Movies/HD # Films: BDRip
|
||||
9: Movies/DVD # Films: R5
|
||||
10: Movies/SD # Films: DVDSCR
|
||||
56: Movies/HD # Films: mHD
|
||||
12: Movies/HD # Films: BRRip
|
||||
13: Movies/HD # Films: WEBRip
|
||||
58: Movies/HD # Films: WEBRip 720p
|
||||
59: Movies/HD # Films: WEBRip 1080p
|
||||
8: Movies/SD # Films: Cam / TS
|
||||
62: Movies/HD # Films: Uhd4k
|
||||
35: Console/Xbox # Jeux: XBOX
|
||||
36: Console/Wii # Jeux: WII
|
||||
37: Console/PSP # Jeux: PSP
|
||||
38: Console/NDS # Jeux: DS
|
||||
39: PC/Phone-Android # Jeux: Android
|
||||
34: Console/PS3 # Jeux: PS3
|
||||
55: PC/Mac # Jeux: Mac
|
||||
33: PC/Games # Jeux: PC
|
||||
46: Audio/MP3 # Musiques: MP3
|
||||
47: Audio/Lossless # Musiques: FLAC
|
||||
48: Audio # Musiques: WMA
|
||||
49: Audio # Musiques: Autres
|
||||
14: TV/SD # Series: HDTV
|
||||
15: TV # Series: TV VOSTFR
|
||||
16: TV # Series: TV VOSTA
|
||||
17: TV # Series: TV PACK
|
||||
18: TV/HD # Series: HDTV 1080p
|
||||
57: TV/HD # Series: TVHD VOSTFR
|
||||
20: TV # Series: TV VO
|
||||
19: TV/HD # Series: HDTV 720p
|
||||
64: TV/SD # Series: WEBRip
|
||||
60: TV/HD # Series: WEBRip 720p
|
||||
61: TV/HD # Series: WEBRip 1080p
|
||||
21: TV/SD # Series: DVD-R TV
|
||||
26: TV # Television: Television
|
||||
24: TV # Television: Spectacles
|
||||
23: TV/Sport # Television: Sports
|
||||
25: TV/Anime # Television: Animes
|
||||
22: TV/Documentary # Television: Documentaires
|
||||
51: XXX # XXX: Tous
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: account-login.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
remember: "yes"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: div#nfobar > p#msgError
|
||||
test:
|
||||
path: torrents-search.php
|
||||
|
||||
ratio:
|
||||
path: torrents-search.php
|
||||
selector: div#infobar0 > ul > li:nth-child(1) > font:last-child
|
||||
|
||||
search:
|
||||
path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
|
||||
rows:
|
||||
selector: table.ttable_headinner > tbody > tr.t-row
|
||||
after: 1
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href^="torrents.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href^="comments.php"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(8)
|
||||
seeders:
|
||||
selector: td:nth-child(9)
|
||||
leechers:
|
||||
selector: td:nth-child(10)
|
||||
date:
|
||||
selector: small > i
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Date: ", ""]
|
||||
- name: replace
|
||||
args: ["le ", ""]
|
||||
- name: dateparse
|
||||
args: "15:04:05 02-01-2006"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="images/free.gif"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
@@ -59,7 +59,8 @@ namespace Jackett.Indexers
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
if (configJson != null)
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
@@ -107,6 +108,13 @@ namespace Jackett.Indexers
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
// Occasionally the cookies become invalid, login again if that happens
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
@@ -137,9 +145,21 @@ namespace Jackett.Indexers
|
||||
var sizeStr = row.ChildElements.ElementAt(4).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Files = ParseUtil.CoerceInt(row.ChildElements.ElementAt(2).Cq().Text().Trim());
|
||||
release.Grabs = ParseUtil.CoerceInt(row.ChildElements.ElementAt(6).Cq().Text().Trim());
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text().Trim()) + release.Seeders;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("strong:contains(\"Freeleech!\")").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,10 +174,12 @@ namespace Jackett.Indexers
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qSeeders = qRow.Find("td:eq(7)");
|
||||
var qLeechers = qRow.Find("td:eq(8)");
|
||||
var qDateStr = qRow.Find("td:eq(4)");
|
||||
var qSize = qRow.Find("td:eq(5)");
|
||||
|
||||
// use negative indexes as if a user has "Wartezeit" there's an extra column after the title
|
||||
var qSeeders = qRow.Find("td:nth-last-child(4)");
|
||||
var qLeechers = qRow.Find("td:nth-last-child(3)");
|
||||
var qDateStr = qRow.Find("td:nth-last-child(7)");
|
||||
var qSize = qRow.Find("td:nth-last-child(6)");
|
||||
|
||||
var torrentId = qDetailsLink.Attr("href").Replace("&hit=1", "").Split('=')[1];
|
||||
|
||||
@@ -199,10 +201,10 @@ namespace Jackett.Indexers
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc;
|
||||
|
||||
var files = qRow.Find("td:nth-child(3)").Text();
|
||||
var files = qRow.Find("td:nth-last-child(9)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(7)").Text();
|
||||
var grabs = qRow.Find("td:nth-last-child(5)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("font[color=\"red\"]:contains(OnlyUp)").Length >= 1)
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
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.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BlueTigers : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl => SiteLink + "account-login.php";
|
||||
private string TorrentSearchUrl => SiteLink + "torrents-search.php";
|
||||
private string IndexUrl => SiteLink + "index.php";
|
||||
|
||||
private ConfigurationDataBlueTigers ConfigData
|
||||
{
|
||||
get { return (ConfigurationDataBlueTigers)configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BlueTigers(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BlueTigers",
|
||||
description: "BlueTigers - No Ratio - Private",
|
||||
link: "https://www.bluetigers.ca/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBlueTigers(@"BlueTigers can search for one or all languages.
|
||||
If you select 2 languages below, results will contain all 3 languages.
|
||||
<br> For best results change the torrents per page setting to 50 in your BlueTigers profile."))
|
||||
{
|
||||
AddCategoryMapping("14", TorznabCatType.ConsolePSP);
|
||||
AddCategoryMapping("150", TorznabCatType.ConsoleWii);
|
||||
AddCategoryMapping("150", TorznabCatType.ConsoleWiiwareVC);
|
||||
AddCategoryMapping("150", TorznabCatType.ConsoleWiiU);
|
||||
AddCategoryMapping("52", TorznabCatType.ConsoleXbox);
|
||||
AddCategoryMapping("52", TorznabCatType.ConsoleXbox360);
|
||||
AddCategoryMapping("52", TorznabCatType.ConsoleXBOX360DLC);
|
||||
AddCategoryMapping("52", TorznabCatType.ConsoleXboxOne);
|
||||
AddCategoryMapping("11", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("13", TorznabCatType.ConsolePS4);
|
||||
AddCategoryMapping("13", TorznabCatType.ConsolePS3);
|
||||
AddCategoryMapping("13", TorznabCatType.ConsolePSVita);
|
||||
AddCategoryMapping("12", TorznabCatType.Console3DS);
|
||||
AddCategoryMapping("160", TorznabCatType.PCPhoneIOS);
|
||||
AddCategoryMapping("160", TorznabCatType.PCPhoneAndroid);
|
||||
AddCategoryMapping("1", TorznabCatType.PCPhoneAndroid);
|
||||
AddCategoryMapping("29", TorznabCatType.PCMac);
|
||||
AddCategoryMapping("27", TorznabCatType.PC);
|
||||
AddCategoryMapping("41", TorznabCatType.PC);
|
||||
AddCategoryMapping("50", TorznabCatType.PC);
|
||||
AddCategoryMapping("333", TorznabCatType.BooksMagazines);
|
||||
AddCategoryMapping("38", TorznabCatType.TVDocumentary);
|
||||
AddCategoryMapping("37", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("61", TorznabCatType.Movies3D);
|
||||
AddCategoryMapping("45", TorznabCatType.XXX);
|
||||
AddCategoryMapping("59", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("222", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("22", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("60", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("56", TorznabCatType.MoviesBluRay);
|
||||
AddCategoryMapping("23", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("15", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("43", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("24", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("25", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("21", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("20", TorznabCatType.MoviesDVD);
|
||||
AddCategoryMapping("26", TorznabCatType.MoviesWEBDL);
|
||||
AddCategoryMapping("9", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("34", TorznabCatType.Other);
|
||||
AddCategoryMapping("35", TorznabCatType.Audio);
|
||||
AddCategoryMapping("36", TorznabCatType.AudioVideo);
|
||||
AddCategoryMapping("31", TorznabCatType.AudioVideo);
|
||||
AddCategoryMapping("2", TorznabCatType.TVOTHER);
|
||||
AddCategoryMapping("16", TorznabCatType.TVHD);
|
||||
AddCategoryMapping("130", TorznabCatType.TVHD);
|
||||
AddCategoryMapping("10", TorznabCatType.TVSD);
|
||||
AddCategoryMapping("131", TorznabCatType.TV);
|
||||
AddCategoryMapping("17", TorznabCatType.TV);
|
||||
AddCategoryMapping("18", TorznabCatType.TV);
|
||||
AddCategoryMapping("19", TorznabCatType.TV);
|
||||
AddCategoryMapping("58", TorznabCatType.TVSport);
|
||||
AddCategoryMapping("33", TorznabCatType.TVOTHER);
|
||||
AddCategoryMapping("34", TorznabCatType.Other);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
ConfigData.LoadValuesFromJson(configJson);
|
||||
|
||||
if (ConfigData.French.Value == false && ConfigData.English.Value == false && ConfigData.Spanish.Value == false)
|
||||
throw new ExceptionWithConfigData("Please select at least one language.", ConfigData);
|
||||
|
||||
await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value },
|
||||
{ "take_login", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, IndexUrl, SiteLink);
|
||||
Regex rgx = new Regex(@"uid=[0-9]{1,10}; pass=[a-z0-9]{1,40};");
|
||||
await ConfigureIfOK(result.Cookies, rgx.IsMatch(result.Cookies), () =>
|
||||
{
|
||||
var errorMessage = "Error while trying to login.";
|
||||
throw new ExceptionWithConfigData(errorMessage, ConfigData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
NameValueCollection qParams = new NameValueCollection();
|
||||
if (ConfigData.French.Value && !ConfigData.English.Value && !ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ConfigData.French.Value && ConfigData.English.Value && !ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "2");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ConfigData.French.Value && !ConfigData.English.Value && ConfigData.Spanish.Value)
|
||||
{
|
||||
qParams.Add("lang", "3");
|
||||
}
|
||||
else
|
||||
{
|
||||
qParams.Add("lang", "0");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<string> catList = MapTorznabCapsToTrackers(query);
|
||||
foreach (string cat in catList)
|
||||
{
|
||||
qParams.Add("c" + cat, "1");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.SanitizedSearchTerm))
|
||||
{
|
||||
qParams.Add("search", query.GetQueryString());
|
||||
}
|
||||
|
||||
string queryStr = qParams.GetQueryString();
|
||||
string searchUrl = $"{TorrentSearchUrl}?incldead=0&freeleech=0&sort=id&order=ascdesc&{queryStr}";
|
||||
|
||||
List<CQ> torrentRowList = new List<CQ>();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
try
|
||||
{
|
||||
CQ fDom = results.Content;
|
||||
var firstPageRows = fDom["table[class='ttable_headinner'] > tbody > tr:not(:First-child)"];
|
||||
torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));
|
||||
|
||||
//If a search term is used, follow upto the first 4 pages (initial plus 3 more)
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()) && fDom["a[class='boutons']"].Filter("a[href*=&page=]").Length > 0)
|
||||
{
|
||||
int pageLinkCount;
|
||||
int.TryParse(fDom["a[class='boutons']"].Filter("a[href*=&page=]").Last().Attr("href").Split(new[] { "&page=" }, StringSplitOptions.None).LastOrDefault(), out pageLinkCount);
|
||||
for (int i = 1; i < Math.Min(4, pageLinkCount + 1); i++)
|
||||
{
|
||||
var sResults = await RequestStringWithCookiesAndRetry($"{searchUrl}&page={i}");
|
||||
CQ sDom = sResults.Content;
|
||||
var additionalPageRows = sDom["table[class='ttable_headinner'] > tbody > tr:not(:First-child)"];
|
||||
torrentRowList.AddRange(additionalPageRows.Select(sRow => sRow.Cq()));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CQ tRow in torrentRowList)
|
||||
{
|
||||
long torrentId = 0;
|
||||
string idTarget = "bookmarks.php?torrent=";
|
||||
string id = tRow.Find("a[href*=" + idTarget + "]").First().Attr("href").Trim();
|
||||
if (!string.IsNullOrEmpty(id) && id.Contains(idTarget))
|
||||
{
|
||||
long.TryParse(id.Substring(id.LastIndexOf(idTarget, StringComparison.Ordinal) + idTarget.Length), out torrentId);
|
||||
}
|
||||
|
||||
if (torrentId <= 0) continue;
|
||||
|
||||
long category = 0;
|
||||
string catTarget = "torrents.php?cat=";
|
||||
string cat = tRow.Find("a[href*=" + catTarget + "]").First().Attr("href").Trim();
|
||||
if (!string.IsNullOrEmpty(cat) && cat.Contains(catTarget))
|
||||
{
|
||||
long.TryParse(cat.Substring(cat.LastIndexOf(catTarget, StringComparison.Ordinal) + catTarget.Length), out category);
|
||||
}
|
||||
|
||||
Uri guid = new Uri($"{SiteLink}torrents-details.php?hit=1&id={torrentId}");
|
||||
Uri link = new Uri($"{SiteLink}download.php?hit=1&id={torrentId}");
|
||||
Uri comments = new Uri($"{SiteLink}comments.php?type=torrent&id={torrentId}");
|
||||
string title = tRow.Find("a[href*=torrents-details.php?id=]").First().Text().Trim();
|
||||
string stats = tRow.Find("div[id=kt" + torrentId.ToString() + "]").First().Text();
|
||||
string sizeStr = new Regex("Taille:(.*)Vitesse:").Match(stats).Groups[1].ToString().Trim();
|
||||
string pubDateStr = new Regex("Ajout.:(.*)Compl.t.s").Match(stats).Groups[1].ToString().Trim();
|
||||
DateTime pubDate = DateTime.ParseExact(pubDateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
|
||||
string statistics = tRow.Find("a[href*=torrents-details.php?id=]").First().RenderSelection().Trim();
|
||||
string startTag = "<table ";
|
||||
string endTag = "</table>";
|
||||
CQ statsCq = startTag + new Regex(startTag + "(.*)" + endTag).Match(statistics).Groups[1].ToString().Trim() + endTag;
|
||||
int seeders;
|
||||
int leechers;
|
||||
int.TryParse(statsCq.Find("font[color=#05FC09]").First().Text(), out seeders);
|
||||
int.TryParse(statsCq.Find("font[color=red]").First().Text(), out leechers);
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = title;
|
||||
release.Guid = guid;
|
||||
release.Link = link;
|
||||
release.PublishDate = pubDate;
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
release.Description = title;
|
||||
release.Seeders = seeders;
|
||||
release.Peers = leechers + seeders;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.Category = MapTrackerCatToNewznab(category.ToString());
|
||||
release.Comments = comments;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using AngleSharp.Parser.Html;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Dom.Html;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
@@ -114,7 +115,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public int After { get; set; }
|
||||
//public string Remove { get; set; } // already inherited
|
||||
public string Dateheaders { get; set; }
|
||||
public selectorBlock Dateheaders { get; set; }
|
||||
}
|
||||
|
||||
public CardigannIndexer(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
@@ -271,6 +272,32 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
protected bool checkForLoginError(WebClientStringResult loginResult)
|
||||
{
|
||||
var ErrorBlocks = Definition.Login.Error;
|
||||
|
||||
if (ErrorBlocks == null)
|
||||
return true; // no error
|
||||
|
||||
var loginResultParser = new HtmlParser();
|
||||
var loginResultDocument = loginResultParser.Parse(loginResult.Content);
|
||||
foreach (errorBlock error in ErrorBlocks)
|
||||
{
|
||||
var selection = loginResultDocument.QuerySelector(error.Selector);
|
||||
if (selection != null)
|
||||
{
|
||||
string errorMessage = selection.TextContent;
|
||||
if (error.Message != null)
|
||||
{
|
||||
var errorSubMessage = loginResultDocument.QuerySelector(error.Message.Selector);
|
||||
errorMessage = errorSubMessage.TextContent;
|
||||
}
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: {0}", errorMessage.Trim()), configData);
|
||||
}
|
||||
}
|
||||
return true; // no error
|
||||
}
|
||||
|
||||
protected async Task<bool> DoLogin()
|
||||
@@ -293,26 +320,74 @@ namespace Jackett.Indexers
|
||||
configData.CookieHeader.Value = null;
|
||||
var loginResult = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink, true);
|
||||
configData.CookieHeader.Value = loginResult.Cookies;
|
||||
|
||||
if (Login.Error != null)
|
||||
|
||||
checkForLoginError(loginResult);
|
||||
}
|
||||
else if (Login.Method == "form")
|
||||
{
|
||||
var LoginUrl = SiteLink + Login.Path;
|
||||
|
||||
var FormSelector = Login.Form;
|
||||
if (FormSelector == null)
|
||||
FormSelector = "form";
|
||||
|
||||
var pairs = new Dictionary<string, string>();
|
||||
|
||||
configData.CookieHeader.Value = null;
|
||||
var landingResult = await RequestStringWithCookies(LoginUrl, null, SiteLink);
|
||||
|
||||
var htmlParser = new HtmlParser();
|
||||
var landingResultDocument = htmlParser.Parse(landingResult.Content);
|
||||
|
||||
var form = landingResultDocument.QuerySelector(FormSelector);
|
||||
if (form == null)
|
||||
{
|
||||
var loginResultParser = new HtmlParser();
|
||||
var loginResultDocument = loginResultParser.Parse(loginResult.Content);
|
||||
foreach (errorBlock error in Login.Error)
|
||||
{
|
||||
var selection = loginResultDocument.QuerySelector(error.Selector);
|
||||
if (selection != null)
|
||||
{
|
||||
string errorMessage = selection.TextContent;
|
||||
if (error.Message != null)
|
||||
{
|
||||
var errorSubMessage = loginResultDocument.QuerySelector(error.Message.Selector);
|
||||
errorMessage = errorSubMessage.TextContent;
|
||||
}
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: {0}", errorMessage.Trim()), configData);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: No form found on {0} using form selector {1}", LoginUrl, FormSelector), configData);
|
||||
}
|
||||
|
||||
var inputs = form.QuerySelectorAll("input");
|
||||
if (inputs == null)
|
||||
{
|
||||
throw new ExceptionWithConfigData(string.Format("Login failed: No inputs found on {0} using form selector {1}", LoginUrl, FormSelector), configData);
|
||||
}
|
||||
|
||||
var submitUrl = resolvePath(form.GetAttribute("action"));
|
||||
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
var name = input.GetAttribute("name");
|
||||
if (name == null)
|
||||
continue;
|
||||
|
||||
var value = input.GetAttribute("value");
|
||||
if (value == null)
|
||||
value = "";
|
||||
|
||||
pairs[name] = value;
|
||||
}
|
||||
|
||||
foreach (var Input in Definition.Login.Inputs)
|
||||
{
|
||||
var value = applyGoTemplateText(Input.Value);
|
||||
pairs[Input.Key] = value;
|
||||
}
|
||||
|
||||
// automatically solve simpleCaptchas, if used
|
||||
var simpleCaptchaPresent = landingResultDocument.QuerySelector("script[src*=\"simpleCaptcha\"]");
|
||||
if(simpleCaptchaPresent != null)
|
||||
{
|
||||
var captchaUrl = resolvePath("simpleCaptcha.php?numImages=1");
|
||||
var simpleCaptchaResult = await RequestStringWithCookies(captchaUrl.ToString(), landingResult.Cookies, LoginUrl);
|
||||
var simpleCaptchaJSON = JObject.Parse(simpleCaptchaResult.Content);
|
||||
var captchaSelection = simpleCaptchaJSON["images"][0]["hash"].ToString();
|
||||
pairs["captchaSelection"] = captchaSelection;
|
||||
pairs["submitme"] = "X";
|
||||
}
|
||||
|
||||
var loginResult = await RequestLoginAndFollowRedirect(submitUrl.ToString(), pairs, landingResult.Cookies, true, null, SiteLink, true);
|
||||
configData.CookieHeader.Value = loginResult.Cookies;
|
||||
|
||||
checkForLoginError(loginResult);
|
||||
}
|
||||
else if (Login.Method == "cookie")
|
||||
{
|
||||
@@ -354,6 +429,24 @@ namespace Jackett.Indexers
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool CheckIfLoginIsNeeded(WebClientByteResult Result, IHtmlDocument document)
|
||||
{
|
||||
if (Result.IsRedirect)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Definition.Login.Test.Selector != null)
|
||||
{
|
||||
var selection = document.QuerySelectorAll(Definition.Login.Test.Selector);
|
||||
if (selection.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
@@ -384,18 +477,17 @@ namespace Jackett.Indexers
|
||||
break;
|
||||
case "timeparse":
|
||||
case "dateparse":
|
||||
throw new NotImplementedException("Filter " + Filter.Name + " not implemented");
|
||||
/*
|
||||
TODO: implement golang time format conversion, see http://fuckinggodateformat.com/
|
||||
if args == nil {
|
||||
return filterDateParse(nil, value)
|
||||
}
|
||||
if layout, ok := args.(string); ok {
|
||||
return filterDateParse([]string{layout}, value)
|
||||
}
|
||||
return "", fmt.Errorf("Filter argument type %T was invalid", args)
|
||||
*/
|
||||
break;
|
||||
var layout = (string)Filter.Args;
|
||||
try
|
||||
{
|
||||
var Date = DateTimeUtil.ParseDateTimeGoLang(Data, layout);
|
||||
Data = Date.ToString(DateTimeUtil.RFC1123ZPattern);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Debug(ex.ToString());
|
||||
}
|
||||
break;
|
||||
case "regexp":
|
||||
var pattern = (string)Filter.Args;
|
||||
var Regexp = new Regex(pattern);
|
||||
@@ -571,8 +663,48 @@ namespace Jackett.Indexers
|
||||
{
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.Parse(results);
|
||||
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(Search.Rows.Selector);
|
||||
|
||||
// check if we need to login again
|
||||
var loginNeeded = CheckIfLoginIsNeeded(response, SearchResultDocument);
|
||||
if (loginNeeded)
|
||||
{
|
||||
logger.Info(string.Format("CardigannIndexer ({0}): Relogin required", ID));
|
||||
await DoLogin();
|
||||
await TestLogin();
|
||||
response = await RequestBytesWithCookies(searchUrl);
|
||||
results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
SearchResultDocument = SearchResultParser.Parse(results);
|
||||
}
|
||||
|
||||
var RowsDom = SearchResultDocument.QuerySelectorAll(Search.Rows.Selector);
|
||||
List<IElement> Rows = new List<IElement>();
|
||||
foreach (var RowDom in RowsDom)
|
||||
{
|
||||
Rows.Add(RowDom);
|
||||
}
|
||||
|
||||
// merge following rows for After selector
|
||||
var After = Definition.Search.Rows.After;
|
||||
if (After > 0)
|
||||
{
|
||||
for (int i = 0; i < Rows.Count; i += 1)
|
||||
{
|
||||
var CurrentRow = Rows[i];
|
||||
for (int j = 0; j < After; j += 1)
|
||||
{
|
||||
var MergeRowIndex = i + j + 1;
|
||||
var MergeRow = Rows[MergeRowIndex];
|
||||
List<INode> MergeNodes = new List<INode>();
|
||||
foreach (var node in MergeRow.QuerySelectorAll("td"))
|
||||
{
|
||||
MergeNodes.Add(node);
|
||||
}
|
||||
CurrentRow.Append(MergeNodes.ToArray());
|
||||
}
|
||||
Rows.RemoveRange(i + 1, After);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var Row in Rows)
|
||||
{
|
||||
try
|
||||
@@ -591,7 +723,15 @@ namespace Jackett.Indexers
|
||||
switch (Field.Key)
|
||||
{
|
||||
case "download":
|
||||
release.Link = resolvePath(value);
|
||||
if (value.StartsWith("magnet:"))
|
||||
{
|
||||
release.MagnetUri = new Uri(value);
|
||||
release.Link = release.MagnetUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Link = resolvePath(value);
|
||||
}
|
||||
break;
|
||||
case "details":
|
||||
var url = resolvePath(value);
|
||||
@@ -600,7 +740,10 @@ namespace Jackett.Indexers
|
||||
release.Comments = url;
|
||||
break;
|
||||
case "comments":
|
||||
release.Comments = resolvePath(value);
|
||||
var CommentsUrl = resolvePath(value);
|
||||
release.Comments = CommentsUrl;
|
||||
if (release.Guid == null)
|
||||
release.Guid = CommentsUrl;
|
||||
break;
|
||||
case "title":
|
||||
release.Title = value;
|
||||
@@ -626,7 +769,6 @@ namespace Jackett.Indexers
|
||||
release.Peers = release.Seeders;
|
||||
else
|
||||
release.Peers += release.Seeders;
|
||||
|
||||
break;
|
||||
case "date":
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||
@@ -653,6 +795,32 @@ namespace Jackett.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
// if DateHeaders is set go through the previous rows and look for the header selector
|
||||
var DateHeaders = Definition.Search.Rows.Dateheaders;
|
||||
if (DateHeaders != null)
|
||||
{
|
||||
var PrevRow = Row.PreviousElementSibling;
|
||||
string value = null;
|
||||
while (PrevRow != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = handleSelector(DateHeaders, PrevRow);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
PrevRow = PrevRow.PreviousElementSibling;
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
throw new Exception(string.Format("No date header row found for {0}", release.ToString()));
|
||||
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(value);
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -49,7 +49,9 @@ namespace Jackett.Indexers
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
if (configJson != null)
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
@@ -92,6 +94,14 @@ namespace Jackett.Indexers
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
// Occasionally the cookies become invalid, login again if that happens
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
|
||||
349
src/Jackett/Indexers/HD4Free.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
using AngleSharp.Parser.Html;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class HD4Free : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "ajax/initial_recall.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string TakeLoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
|
||||
new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public HD4Free(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "HD4Free",
|
||||
description: "A HD trackers",
|
||||
link: "https://hd4free.xyz/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
{
|
||||
AddCategoryMapping(42, TorznabCatType.MoviesSD); // LEGi0N 480p
|
||||
AddCategoryMapping(17, TorznabCatType.MoviesHD); // LEGi0N 720p
|
||||
AddCategoryMapping(16, TorznabCatType.MoviesHD); // LEGi0N 1080p
|
||||
AddCategoryMapping(84, TorznabCatType.Movies3D); // LEGi0N 3D 1080p
|
||||
AddCategoryMapping(31, TorznabCatType.MoviesOther); // LEGi0N REMUX
|
||||
AddCategoryMapping(70, TorznabCatType.MoviesBluRay); // LEGi0N BD BD25 & BD50
|
||||
AddCategoryMapping(55, TorznabCatType.Movies); // LEGi0N Movie/TV PACKS
|
||||
AddCategoryMapping(60, TorznabCatType.Other); // shadz shack
|
||||
AddCategoryMapping(85, TorznabCatType.MoviesHD); // MarGe 720p
|
||||
AddCategoryMapping(86, TorznabCatType.MoviesHD); // MarGe 1080p
|
||||
AddCategoryMapping(73, TorznabCatType.MoviesBluRay); // GF44 BD-50
|
||||
AddCategoryMapping(74, TorznabCatType.MoviesBluRay); // GF44 BD-25
|
||||
AddCategoryMapping(88, TorznabCatType.MoviesBluRay); // taterzero BD50
|
||||
AddCategoryMapping(89, TorznabCatType.MoviesBluRay); // taterzero BD25
|
||||
AddCategoryMapping(90, TorznabCatType.Movies3D); // taterzero 3D BD
|
||||
AddCategoryMapping(39, TorznabCatType.MoviesBluRay); // Bluray REMUX
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesBluRay); // Bluray
|
||||
AddCategoryMapping(75, TorznabCatType.MoviesBluRay); // Bluray 25
|
||||
AddCategoryMapping(36, TorznabCatType.MoviesHD); // Encodes 720p
|
||||
AddCategoryMapping(35, TorznabCatType.MoviesHD); // Encodes 1080p
|
||||
AddCategoryMapping(45, TorznabCatType.Movies3D); // 1080p 3D Encodes
|
||||
AddCategoryMapping(77, TorznabCatType.MoviesHD); // WEB-DL 720p
|
||||
AddCategoryMapping(78, TorznabCatType.MoviesHD); // WEB-DL 1080p
|
||||
AddCategoryMapping(83, TorznabCatType.MoviesDVD); // DVD 5/9's
|
||||
AddCategoryMapping(47, TorznabCatType.Movies); // General x264
|
||||
AddCategoryMapping(58, TorznabCatType.Movies); // General XViD
|
||||
AddCategoryMapping(66, TorznabCatType.Movies); // x265 HEVC
|
||||
AddCategoryMapping(34, TorznabCatType.MoviesHD); // 4K
|
||||
AddCategoryMapping(61, TorznabCatType.Movies); // MOViE PACKS
|
||||
AddCategoryMapping(44, TorznabCatType.TVHD); // HDTV 720p
|
||||
AddCategoryMapping(43, TorznabCatType.TVHD); // HDTV 1080p
|
||||
AddCategoryMapping(41, TorznabCatType.TVHD); // WEB-DL 720p TV
|
||||
AddCategoryMapping(40, TorznabCatType.TVHD); // WEB-DL 1080p TV
|
||||
AddCategoryMapping(52, TorznabCatType.TVHD); // 720p TV BluRay
|
||||
AddCategoryMapping(53, TorznabCatType.TVHD); // 1080p TV BluRay
|
||||
AddCategoryMapping(62, TorznabCatType.TVHD); // HDTV Packs
|
||||
AddCategoryMapping(82, TorznabCatType.TVSD); // SDTV TV PACKS
|
||||
AddCategoryMapping(63, TorznabCatType.PC0day); // Apps Windows
|
||||
AddCategoryMapping(57, TorznabCatType.PCMac); // Appz Mac
|
||||
AddCategoryMapping(72, TorznabCatType.AudioAudiobook); // Audio Books
|
||||
AddCategoryMapping(71, TorznabCatType.Books); // Ebooks
|
||||
AddCategoryMapping(46, TorznabCatType.AudioLossless); // FLAC/Lossless
|
||||
AddCategoryMapping(81, TorznabCatType.AudioMP3); // MP3 Music
|
||||
AddCategoryMapping(87, TorznabCatType.AudioVideo); // HD MUSiC ViDEOS
|
||||
AddCategoryMapping(32, TorznabCatType.Other); // Covers And Artwork
|
||||
AddCategoryMapping(50, TorznabCatType.XXX); // Porn XXX
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, configData.CookieHeader.Value);
|
||||
CQ cq = loginPage.Content;
|
||||
string recaptchaSiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = recaptchaSiteKey;
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "returnto" , "/" },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value },
|
||||
{ "submitme", "Login" }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
// Cookie was manually supplied
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (!results.Any())
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(TakeLoginUrl, pairs, null, true, SiteLink, LoginUrl);
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var messageEl = dom["table.main > tbody > tr > td > table > tbody > tr > td"];
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
if (string.IsNullOrWhiteSpace(errorMessage))
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
List<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
|
||||
var pairs = new Dictionary<string, string>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
pairs.Add("draw", "1");
|
||||
pairs.Add("columns[0][data]", "");
|
||||
pairs.Add("columns[0][name]", "");
|
||||
pairs.Add("columns[0][searchable]", "false");
|
||||
pairs.Add("columns[0][orderable]", "false");
|
||||
pairs.Add("columns[0][search][value]", "");
|
||||
pairs.Add("columns[0][search][regex]", "false");
|
||||
pairs.Add("columns[1][data]", "id");
|
||||
pairs.Add("columns[1][name]", "");
|
||||
pairs.Add("columns[1][searchable]", "false");
|
||||
pairs.Add("columns[1][orderable]", "true");
|
||||
pairs.Add("columns[1][search][value]", "");
|
||||
pairs.Add("columns[1][search][regex]", "false");
|
||||
pairs.Add("columns[2][data]", "cat");
|
||||
pairs.Add("columns[2][name]", "");
|
||||
pairs.Add("columns[2][searchable]", "false");
|
||||
pairs.Add("columns[2][orderable]", "true");
|
||||
pairs.Add("columns[2][search][value]", "");
|
||||
pairs.Add("columns[2][search][regex]", "false");
|
||||
pairs.Add("columns[3][data]", "name");
|
||||
pairs.Add("columns[3][name]", "");
|
||||
pairs.Add("columns[3][searchable]", "true");
|
||||
pairs.Add("columns[3][orderable]", "true");
|
||||
pairs.Add("columns[3][search][value]", "");
|
||||
pairs.Add("columns[3][search][regex]", "false");
|
||||
pairs.Add("columns[4][data]", "username");
|
||||
pairs.Add("columns[4][name]", "");
|
||||
pairs.Add("columns[4][searchable]", "true");
|
||||
pairs.Add("columns[4][orderable]", "true");
|
||||
pairs.Add("columns[4][search][value]", "");
|
||||
pairs.Add("columns[4][search][regex]", "false");
|
||||
pairs.Add("columns[5][data]", "cat-image");
|
||||
pairs.Add("columns[5][name]", "");
|
||||
pairs.Add("columns[5][searchable]", "false");
|
||||
pairs.Add("columns[5][orderable]", "true");
|
||||
pairs.Add("columns[5][search][value]", "");
|
||||
pairs.Add("columns[5][search][regex]", "false");
|
||||
pairs.Add("columns[6][data]", "cat-name");
|
||||
pairs.Add("columns[6][name]", "");
|
||||
pairs.Add("columns[6][searchable]", "false");
|
||||
pairs.Add("columns[6][orderable]", "true");
|
||||
pairs.Add("columns[6][search][value]", "");
|
||||
pairs.Add("columns[6][search][regex]", "false");
|
||||
pairs.Add("columns[7][data]", "imdbid");
|
||||
pairs.Add("columns[7][name]", "");
|
||||
pairs.Add("columns[7][searchable]", "true");
|
||||
pairs.Add("columns[7][orderable]", "true");
|
||||
pairs.Add("columns[7][search][value]", "");
|
||||
pairs.Add("columns[7][search][regex]", "false");
|
||||
pairs.Add("columns[8][data]", "genre");
|
||||
pairs.Add("columns[8][name]", "");
|
||||
pairs.Add("columns[8][searchable]", "false");
|
||||
pairs.Add("columns[8][orderable]", "true");
|
||||
pairs.Add("columns[8][search][value]", "");
|
||||
pairs.Add("columns[8][search][regex]", "false");
|
||||
pairs.Add("columns[9][data]", "added");
|
||||
pairs.Add("columns[9][name]", "");
|
||||
pairs.Add("columns[9][searchable]", "false");
|
||||
pairs.Add("columns[9][orderable]", "true");
|
||||
pairs.Add("columns[9][search][value]", "");
|
||||
pairs.Add("columns[9][search][regex]", "false");
|
||||
pairs.Add("columns[10][data]", "size");
|
||||
pairs.Add("columns[10][name]", "");
|
||||
pairs.Add("columns[10][searchable]", "false");
|
||||
pairs.Add("columns[10][orderable]", "true");
|
||||
pairs.Add("columns[10][search][value]", "");
|
||||
pairs.Add("columns[10][search][regex]", "false");
|
||||
pairs.Add("columns[11][data]", "rating");
|
||||
pairs.Add("columns[11][name]", "");
|
||||
pairs.Add("columns[11][searchable]", "false");
|
||||
pairs.Add("columns[11][orderable]", "true");
|
||||
pairs.Add("columns[11][search][value]", "");
|
||||
pairs.Add("columns[11][search][regex]", "false");
|
||||
pairs.Add("columns[12][data]", "comments");
|
||||
pairs.Add("columns[12][name]", "");
|
||||
pairs.Add("columns[12][searchable]", "false");
|
||||
pairs.Add("columns[12][orderable]", "true");
|
||||
pairs.Add("columns[12][search][value]", "");
|
||||
pairs.Add("columns[12][search][regex]", "false");
|
||||
pairs.Add("columns[13][data]", "numfiles");
|
||||
pairs.Add("columns[13][name]", "");
|
||||
pairs.Add("columns[13][searchable]", "false");
|
||||
pairs.Add("columns[13][orderable]", "true");
|
||||
pairs.Add("columns[13][search][value]", "");
|
||||
pairs.Add("columns[13][search][regex]", "false");
|
||||
pairs.Add("columns[14][data]", "seeders");
|
||||
pairs.Add("columns[14][name]", "");
|
||||
pairs.Add("columns[14][searchable]", "false");
|
||||
pairs.Add("columns[14][orderable]", "true");
|
||||
pairs.Add("columns[14][search][value]", "");
|
||||
pairs.Add("columns[14][search][regex]", "false");
|
||||
pairs.Add("columns[15][data]", "leechers");
|
||||
pairs.Add("columns[15][name]", "");
|
||||
pairs.Add("columns[15][searchable]", "false");
|
||||
pairs.Add("columns[15][orderable]", "true");
|
||||
pairs.Add("columns[15][search][value]", "");
|
||||
pairs.Add("columns[15][search][regex]", "false");
|
||||
pairs.Add("columns[16][data]", "to_go");
|
||||
pairs.Add("columns[16][name]", "");
|
||||
pairs.Add("columns[16][searchable]", "false");
|
||||
pairs.Add("columns[16][orderable]", "true");
|
||||
pairs.Add("columns[16][search][value]", "");
|
||||
pairs.Add("columns[16][search][regex]", "false");
|
||||
pairs.Add("columns[17][data]", "genre");
|
||||
pairs.Add("columns[17][name]", "");
|
||||
pairs.Add("columns[17][searchable]", "true");
|
||||
pairs.Add("columns[17][orderable]", "true");
|
||||
pairs.Add("columns[17][search][value]", "");
|
||||
pairs.Add("columns[17][search][regex]", "false");
|
||||
pairs.Add("order[0][column]", "9");
|
||||
pairs.Add("order[0][dir]", "desc");
|
||||
pairs.Add("start", "0");
|
||||
pairs.Add("length", "100");
|
||||
pairs.Add("visible", "1");
|
||||
pairs.Add("uid", "-1");
|
||||
pairs.Add("genre", "");
|
||||
|
||||
pairs.Add("cats", string.Join(",+", MapTorznabCapsToTrackers(query)));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
pairs.Add("search[value]", searchString);
|
||||
pairs.Add("search[regex]", "false");
|
||||
}
|
||||
|
||||
var results = await PostDataWithCookiesAndRetry(searchUrl, pairs);
|
||||
|
||||
try
|
||||
{
|
||||
var json = JObject.Parse(results.Content);
|
||||
foreach (var row in json["data"])
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 72 * 24 * 60 * 60;
|
||||
|
||||
var hParser = new HtmlParser();
|
||||
var hName = hParser.Parse(row["name"].ToString());
|
||||
var hComments = hParser.Parse(row["comments"].ToString());
|
||||
var hNumfiles = hParser.Parse(row["numfiles"].ToString());
|
||||
var hSeeders = hParser.Parse(row["seeders"].ToString());
|
||||
var hLeechers = hParser.Parse(row["leechers"].ToString());
|
||||
|
||||
var hDetailsLink = hName.QuerySelector("a[href^=\"details.php?id=\"]");
|
||||
var hCommentsLink = hComments.QuerySelector("a");
|
||||
var hDownloadLink = hName.QuerySelector("a[title=\"Download Torrent\"]");
|
||||
|
||||
release.Title = hDetailsLink.TextContent;
|
||||
release.Comments = new Uri(SiteLink + hCommentsLink.GetAttribute("href"));
|
||||
release.Link = new Uri(SiteLink + hDownloadLink.GetAttribute("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
release.Description = row["genre"].ToString();
|
||||
|
||||
var poster = row["poster"].ToString();
|
||||
if(!string.IsNullOrWhiteSpace(poster))
|
||||
{
|
||||
release.BannerUrl = new Uri(SiteLink + poster);
|
||||
}
|
||||
|
||||
release.Size = ReleaseInfo.GetBytes(row["size"].ToString());
|
||||
var imdbId = row["imdbid"].ToString();
|
||||
if (imdbId.StartsWith("tt"))
|
||||
release.Imdb = ParseUtil.CoerceLong(imdbId.Substring(2));
|
||||
|
||||
var added = row["added"].ToString().Replace("<br>", " ");
|
||||
release.PublishDate = DateTimeUtil.FromUnknown(added);
|
||||
|
||||
var catid = row["catid"].ToString();
|
||||
release.Category = MapTrackerCatToNewznab(catid);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(hSeeders.QuerySelector("a").TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(hLeechers.QuerySelector("a").TextContent) + release.Seeders;
|
||||
|
||||
release.Files = ParseUtil.CoerceInt(hNumfiles.QuerySelector("a").TextContent);
|
||||
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,8 @@ namespace Jackett.Indexers
|
||||
else
|
||||
release.Title = titleParts[0].Trim();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[title][href^=\"details.php\"]");
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Link = new Uri(SiteLink + qRow.Find("a").Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
|
||||
@@ -69,7 +69,9 @@ namespace Jackett.Indexers
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
if (configJson != null)
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
@@ -99,6 +101,13 @@ namespace Jackett.Indexers
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
// Occasionally the cookies become invalid, login again if that happens
|
||||
if (results.Content.Contains("You do not have permission to access this page."))
|
||||
{
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace Jackett.Indexers
|
||||
var torrentId = downloadAnchorHref.Substring(downloadAnchorHref.LastIndexOf('=') + 1);
|
||||
var qFiles = row.QuerySelector("td:nth-last-child(6)");
|
||||
var files = ParseUtil.CoerceLong(qFiles.TextContent);
|
||||
var publishDate = DateTime.ParseExact(row.QuerySelector(".time.tooltip").Attributes["title"].Value, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
|
||||
var publishDate = DateTime.ParseExact(row.QuerySelector(".time.tooltip").Attributes["title"].Value, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
var torrentData = row.QuerySelectorAll(".number_column"); // Size (xx.xx GB[ (Max)]) Snatches (xx) Seeders (xx) Leechers (xx)
|
||||
|
||||
if (torrentData.Length != 4)
|
||||
|
||||
150
src/Jackett/Indexers/TVVault.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using AngleSharp.Parser.Html;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class TVVault : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "torrents.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TVVault(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "TV-Vault",
|
||||
description: "A TV tracker for old shows.",
|
||||
link: "https://tv-vault.me/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.TV);
|
||||
AddCategoryMapping(2, TorznabCatType.Movies);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "keeplogged", "1" },
|
||||
{ "login", "Log+In!" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private string StripSearchString(string term)
|
||||
{
|
||||
// Search does not support searching with episode numbers so strip it if we have one
|
||||
// Ww AND filter the result later to archive the proper result
|
||||
term = Regex.Replace(term, @"[S|E]\d\d", string.Empty);
|
||||
return term.Trim();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("searchstr", StripSearchString(searchString));
|
||||
queryCollection.Add("order_by", "s3");
|
||||
queryCollection.Add("order_way", "desc");
|
||||
queryCollection.Add("disablegrouping", "1");
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookies(searchUrl);
|
||||
try
|
||||
{
|
||||
string RowsSelector = "table.torrent_table > tbody > tr.torrent";
|
||||
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.Parse(results.Content);
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
|
||||
foreach (var Row in Rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 0;
|
||||
|
||||
var qDetailsLink = Row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
var DescStr = qDetailsLink.NextSibling;
|
||||
var Files = Row.QuerySelector("td:nth-child(3)");
|
||||
var Added = Row.QuerySelector("td:nth-child(4)");
|
||||
var Size = Row.QuerySelector("td:nth-child(5)").FirstChild;
|
||||
var Grabs = Row.QuerySelector("td:nth-child(6)");
|
||||
var Seeders = Row.QuerySelector("td:nth-child(7)");
|
||||
var Leechers = Row.QuerySelector("td:nth-child(8)");
|
||||
var FreeLeech = Row.QuerySelector("strong.freeleech_normal");
|
||||
|
||||
var TorrentIdParts = qDetailsLink.GetAttribute("href").Split('=');
|
||||
var TorrentId = TorrentIdParts[TorrentIdParts.Length - 1];
|
||||
var DLLink = "torrents.php?action=download&id=" + TorrentId.ToString();
|
||||
|
||||
release.Description = DescStr.TextContent.Trim();
|
||||
release.Title = qDetailsLink.TextContent + " " + release.Description;
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(Added.TextContent);
|
||||
release.Category = TvCategoryParser.ParseTvShowQuality(release.Description);
|
||||
|
||||
release.Link = new Uri(SiteLink + DLLink);
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(Seeders.TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(Leechers.TextContent) + release.Seeders;
|
||||
release.Size = ReleaseInfo.GetBytes(Size.TextContent);
|
||||
release.Grabs = ReleaseInfo.GetBytes(Grabs.TextContent);
|
||||
release.Files = ReleaseInfo.GetBytes(Files.TextContent);
|
||||
|
||||
if (FreeLeech != null)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
@@ -97,6 +98,10 @@ namespace Jackett.Indexers
|
||||
var captchaImage = await RequestBytesWithCookies(CaptchaUrl, loginPage.Cookies);
|
||||
configData.CaptchaImage.Value = captchaImage.Content;
|
||||
}
|
||||
else
|
||||
{
|
||||
configData.CaptchaImage.Value = new byte[0];
|
||||
}
|
||||
configData.CaptchaCookie.Value = loginPage.Cookies;
|
||||
return configData;
|
||||
}
|
||||
@@ -121,7 +126,7 @@ namespace Jackett.Indexers
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(IndexUrl, pairs, configData.CaptchaCookie.Value, true, null, IndexUrl, true);
|
||||
if (result.Content == null || !result.Content.Contains("login_complete"))
|
||||
if (result.Content == null || (!result.Content.Contains("login_complete") && !result.Content.Contains("index.php?strWebValue=account&strWebAction=logout")))
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table > tbody > tr > td[valign=top][width=100%]"].Html();
|
||||
@@ -172,10 +177,10 @@ namespace Jackett.Indexers
|
||||
queryCollection.Add("dirs" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
logger.Error(searchUrl);
|
||||
logger.Error(CookieHeader);
|
||||
|
||||
var response = await RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
var TitleRegexp = new Regex(@"^return buildTable\('(.*?)',\s+");
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
@@ -189,7 +194,7 @@ namespace Jackett.Indexers
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=details]").First();
|
||||
release.Title = qDetailsLink.Text();
|
||||
release.Title = TitleRegexp.Match(qDetailsLink.Attr("onmouseover")).Groups[1].Value;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=search&dir=]").First();
|
||||
var qDLLink = qRow.Find("a[href^=index.php?strWebValue=torrent&strWebAction=download&id=]").First();
|
||||
|
||||
@@ -172,47 +172,58 @@ namespace Jackett.Indexers
|
||||
// If we have no query use the RSS Page as their server is slow enough at times!
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
|
||||
var rssPage = await RequestStringWithCookiesAndRetry(string.Format(RSSUrl, configData.RSSKey.Value));
|
||||
if (rssPage.Content.EndsWith("\0")) {
|
||||
rssPage.Content = rssPage.Content.Substring(0, rssPage.Content.Length - 1);
|
||||
}
|
||||
rssPage.Content = RemoveInvalidXmlChars(rssPage.Content);
|
||||
var rssDoc = XDocument.Parse(rssPage.Content);
|
||||
|
||||
foreach (var item in rssDoc.Descendants("item"))
|
||||
try
|
||||
{
|
||||
var title = item.Descendants("title").First().Value;
|
||||
var description = item.Descendants("description").First().Value;
|
||||
var link = item.Descendants("link").First().Value;
|
||||
var category = item.Descendants("category").First().Value;
|
||||
var date = item.Descendants("pubDate").First().Value;
|
||||
if (rssPage.Content.EndsWith("\0")) {
|
||||
rssPage.Content = rssPage.Content.Substring(0, rssPage.Content.Length - 1);
|
||||
}
|
||||
rssPage.Content = RemoveInvalidXmlChars(rssPage.Content);
|
||||
var rssDoc = XDocument.Parse(rssPage.Content);
|
||||
|
||||
var torrentIdMatch = Regex.Match(link, "(?<=id=)(\\d)*");
|
||||
var torrentId = torrentIdMatch.Success ? torrentIdMatch.Value : string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(torrentId))
|
||||
throw new Exception("Missing torrent id");
|
||||
|
||||
var infoMatch = Regex.Match(description, @"Category:\W(?<cat>.*)\W\/\WSeeders:\W(?<seeders>[\d\,]*)\W\/\WLeechers:\W(?<leechers>[\d\,]*)\W\/\WSize:\W(?<size>[\d\.]*\W\S*)");
|
||||
if (!infoMatch.Success)
|
||||
throw new Exception("Unable to find info");
|
||||
|
||||
var release = new ReleaseInfo
|
||||
foreach (var item in rssDoc.Descendants("item"))
|
||||
{
|
||||
Title = title,
|
||||
Description = title,
|
||||
Guid = new Uri(string.Format(DownloadUrl, torrentId)),
|
||||
Comments = new Uri(string.Format(CommentUrl, torrentId)),
|
||||
PublishDate = DateTime.ParseExact(date, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31
|
||||
Link = new Uri(string.Format(DownloadUrl, torrentId)),
|
||||
Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value),
|
||||
Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value),
|
||||
Size = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value),
|
||||
Category = MapTrackerCatToNewznab(category)
|
||||
};
|
||||
var title = item.Descendants("title").First().Value;
|
||||
var description = item.Descendants("description").First().Value;
|
||||
var link = item.Descendants("link").First().Value;
|
||||
var category = item.Descendants("category").First().Value;
|
||||
var date = item.Descendants("pubDate").First().Value;
|
||||
|
||||
release.Peers += release.Seeders;
|
||||
releases.Add(release);
|
||||
var torrentIdMatch = Regex.Match(link, "(?<=id=)(\\d)*");
|
||||
var torrentId = torrentIdMatch.Success ? torrentIdMatch.Value : string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(torrentId))
|
||||
throw new Exception("Missing torrent id");
|
||||
|
||||
var infoMatch = Regex.Match(description, @"Category:\W(?<cat>.*)\W\/\WSeeders:\W(?<seeders>[\d\,]*)\W\/\WLeechers:\W(?<leechers>[\d\,]*)\W\/\WSize:\W(?<size>[\d\.]*\W\S*)");
|
||||
if (!infoMatch.Success)
|
||||
throw new Exception("Unable to find info");
|
||||
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
Title = title,
|
||||
Description = title,
|
||||
Guid = new Uri(string.Format(DownloadUrl, torrentId)),
|
||||
Comments = new Uri(string.Format(CommentUrl, torrentId)),
|
||||
PublishDate = DateTime.ParseExact(date, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31
|
||||
Link = new Uri(string.Format(DownloadUrl, torrentId)),
|
||||
Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value),
|
||||
Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value),
|
||||
Size = ReleaseInfo.GetBytes(infoMatch.Groups["size"].Value),
|
||||
Category = MapTrackerCatToNewznab(category)
|
||||
};
|
||||
|
||||
release.Peers += release.Seeders;
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error("XSpeeds: Error while parsing the RSS feed:");
|
||||
logger.Error(rssPage.Content);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -166,8 +166,10 @@
|
||||
<Compile Include="Controllers\DownloadController.cs" />
|
||||
<Compile Include="Engine.cs" />
|
||||
<Compile Include="Indexers\AnimeTorrents.cs" />
|
||||
<Compile Include="Indexers\TVVault.cs" />
|
||||
<Compile Include="Indexers\BJShare.cs" />
|
||||
<Compile Include="Indexers\CardigannIndexer.cs" />
|
||||
<Compile Include="Indexers\HD4Free.cs" />
|
||||
<Compile Include="Indexers\myAmity.cs" />
|
||||
<Compile Include="Indexers\TorrentNetwork.cs" />
|
||||
<Compile Include="Indexers\Andraste.cs" />
|
||||
@@ -187,7 +189,6 @@
|
||||
<Compile Include="Indexers\Xthor.cs" />
|
||||
<Compile Include="Indexers\AlphaRatio.cs" />
|
||||
<Compile Include="Indexers\BitSoup.cs" />
|
||||
<Compile Include="Indexers\BlueTigers.cs" />
|
||||
<Compile Include="Indexers\CinemaZ.cs" />
|
||||
<Compile Include="Indexers\Avistaz.cs" />
|
||||
<Compile Include="Indexers\BaseIndexer.cs" />
|
||||
@@ -373,15 +374,48 @@
|
||||
<None Include="CurlSharp.dll.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Content\logos\alphareign.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\animetorrents.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\apollo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\bjshare.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\blubits.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\eotforum.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\ethor.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\hd4free.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\newretro.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\shareisland.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\thehorrorcharnel.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\torrentsectorcrew.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\tspate.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\tvvault.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\uhdbits.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -400,6 +434,33 @@
|
||||
<Content Include="Definitions\shareisland.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\newretro.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\torrentsectorcrew.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\thehorrorcharnel.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\tspate.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\eotforum.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\ethor.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\alphareign.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\apollo.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Definitions\blubits.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
@@ -535,9 +596,6 @@
|
||||
<Content Include="Content\logos\bitsoup.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\bluetigers.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Content\logos\broadcastthenet.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Cliver;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -81,6 +82,8 @@ namespace Jackett.Utils
|
||||
if (time.EndsWith("AM"))
|
||||
{
|
||||
time = time.Substring(0, time.Length - 2);
|
||||
if(time.StartsWith("12")) // 12:15 AM becomes 00:15
|
||||
time = "00" + time.Substring(2);
|
||||
}
|
||||
else if (time.EndsWith("PM"))
|
||||
{
|
||||
@@ -110,74 +113,172 @@ namespace Jackett.Utils
|
||||
public static Regex tomorrowRegexp = new Regex(@"(?i)\btomorrow([\s,]+|$)", RegexOptions.Compiled);
|
||||
public static Regex yesterdayRegexp = new Regex(@"(?i)\byesterday([\s,]+|$)", RegexOptions.Compiled);
|
||||
public static Regex missingYearRegexp = new Regex(@"^\d{1,2}-\d{1,2}\b", RegexOptions.Compiled);
|
||||
public static Regex missingYearRegexp2 = new Regex(@"^(\d{1,2}\s+\w{3})\s+(\d{1,2}\:\d{1,2}.*)$", RegexOptions.Compiled); // 1 Jan 10:30
|
||||
|
||||
public static DateTime FromUnknown(string str)
|
||||
{
|
||||
str = ParseUtil.NormalizeSpace(str);
|
||||
Match match;
|
||||
try {
|
||||
str = ParseUtil.NormalizeSpace(str);
|
||||
Match match;
|
||||
|
||||
// ... ago
|
||||
match = timeAgoRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var timeago = str;
|
||||
return FromTimeAgo(timeago);
|
||||
}
|
||||
if(str.ToLower().Contains("now"))
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// Today ...
|
||||
match = todayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
return dt;
|
||||
}
|
||||
// ... ago
|
||||
match = timeAgoRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var timeago = str;
|
||||
return FromTimeAgo(timeago);
|
||||
}
|
||||
|
||||
// Yesterday ...
|
||||
match = yesterdayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt -= TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
}
|
||||
// Today ...
|
||||
match = todayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Tomorrow ...
|
||||
match = tomorrowRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt += TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
// Yesterday ...
|
||||
match = yesterdayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt -= TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Tomorrow ...
|
||||
match = tomorrowRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt += TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// try parsing the str as an unix timestamp
|
||||
var unixTimeStamp = long.Parse(str);
|
||||
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
dt = dt.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
return dt;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// it wasn't a timestamp, continue....
|
||||
}
|
||||
|
||||
// add missing year
|
||||
match = missingYearRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[0].Value;
|
||||
string newDate = date+"-"+DateTime.Now.Year.ToString();
|
||||
str = str.Replace(date, newDate);
|
||||
}
|
||||
|
||||
// add missing year 2
|
||||
match = missingYearRegexp2.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[1].Value;
|
||||
var time = match.Groups[2].Value;
|
||||
str = date + " " + DateTime.Now.Year.ToString() + " " + time;
|
||||
}
|
||||
return FromFuzzyTime(str);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(string.Format("DateTime parsing failed for \"{0}\": {1}", str, ex.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
// converts a date/time string to a DateTime object using a GoLang layout
|
||||
public static DateTime ParseDateTimeGoLang(string date, string layout)
|
||||
{
|
||||
date = ParseUtil.NormalizeSpace(date);
|
||||
var pattern = layout;
|
||||
|
||||
// year
|
||||
pattern = pattern.Replace("2006", "yyyy");
|
||||
pattern = pattern.Replace("06", "yy");
|
||||
|
||||
// month
|
||||
pattern = pattern.Replace("January", "MMMM");
|
||||
pattern = pattern.Replace("Jan", "MMM");
|
||||
pattern = pattern.Replace("01", "MM");
|
||||
|
||||
// day
|
||||
pattern = pattern.Replace("Monday", "dddd");
|
||||
pattern = pattern.Replace("Mon", "ddd");
|
||||
pattern = pattern.Replace("02", "dd");
|
||||
//pattern = pattern.Replace("_2", ""); // space padding not supported nativly by C#?
|
||||
pattern = pattern.Replace("2", "d");
|
||||
|
||||
// hours/minutes/seconds
|
||||
pattern = pattern.Replace("05", "ss");
|
||||
|
||||
pattern = pattern.Replace("15", "HH");
|
||||
pattern = pattern.Replace("03", "hh");
|
||||
pattern = pattern.Replace("3", "h");
|
||||
|
||||
pattern = pattern.Replace("04", "mm");
|
||||
pattern = pattern.Replace("4", "m");
|
||||
|
||||
pattern = pattern.Replace("5", "s");
|
||||
|
||||
// month again
|
||||
pattern = pattern.Replace("1", "M");
|
||||
|
||||
// fractional seconds
|
||||
pattern = pattern.Replace(".0000", "ffff");
|
||||
pattern = pattern.Replace(".000", "fff");
|
||||
pattern = pattern.Replace(".00", "ff");
|
||||
pattern = pattern.Replace(".0", "f");
|
||||
|
||||
pattern = pattern.Replace(".9999", "FFFF");
|
||||
pattern = pattern.Replace(".999", "FFF");
|
||||
pattern = pattern.Replace(".99", "FF");
|
||||
pattern = pattern.Replace(".9", "F");
|
||||
|
||||
// AM/PM
|
||||
pattern = pattern.Replace("PM", "tt");
|
||||
pattern = pattern.Replace("pm", "tt"); // not sure if this works
|
||||
|
||||
// timezones
|
||||
// these might need further tuning
|
||||
//pattern = pattern.Replace("MST", "");
|
||||
//pattern = pattern.Replace("Z07:00:00", "");
|
||||
pattern = pattern.Replace("Z07:00", "'Z'zzz");
|
||||
pattern = pattern.Replace("Z07", "'Z'zz");
|
||||
//pattern = pattern.Replace("Z070000", "");
|
||||
//pattern = pattern.Replace("Z0700", "");
|
||||
pattern = pattern.Replace("Z07:00", "'Z'zzz");
|
||||
pattern = pattern.Replace("Z07", "'Z'zz");
|
||||
//pattern = pattern.Replace("-07:00:00", "");
|
||||
pattern = pattern.Replace("-07:00", "zzz");
|
||||
//pattern = pattern.Replace("-0700", "zz");
|
||||
pattern = pattern.Replace("-07", "zz");
|
||||
|
||||
try
|
||||
{
|
||||
// try parsing the str as an unix timestamp
|
||||
var unixTimeStamp = long.Parse(str);
|
||||
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
dt = dt.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
return dt;
|
||||
return DateTime.ParseExact(date, pattern, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (FormatException)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// it wasn't a timestamp, continue....
|
||||
throw new Exception(string.Format("Error while parsing DateTime \"{0}\", using layout \"{1}\" ({2}): {3}", date, layout, pattern, ex.ToString()));
|
||||
}
|
||||
|
||||
// add missing year
|
||||
match = missingYearRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[0].Value;
|
||||
string newDate = date+"-"+DateTime.Now.Year.ToString();
|
||||
str = str.Replace(date, newDate);
|
||||
}
|
||||
return FromFuzzyTime(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||