mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-15 08:24:14 +02:00
Compare commits
327 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9f03f8129a | ||
![]() |
51aa6fdf13 | ||
![]() |
f2f602dcc5 | ||
![]() |
a217381668 | ||
![]() |
4bd7befb50 | ||
![]() |
56074155e9 | ||
![]() |
86a5a9cd25 | ||
![]() |
1792ed276e | ||
![]() |
4d1e4b59c8 | ||
![]() |
e1ff4b9e12 | ||
![]() |
86400e1b8a | ||
![]() |
b3d53d1c01 | ||
![]() |
471d494b3b | ||
![]() |
6e7d983fc3 | ||
![]() |
fd1073d0b5 | ||
![]() |
7815615112 | ||
![]() |
a6d8e68ca9 | ||
![]() |
9593ba2414 | ||
![]() |
8c95045a4a | ||
![]() |
667317e513 | ||
![]() |
0026a4f26e | ||
![]() |
2fac90df9f | ||
![]() |
424e7b773d | ||
![]() |
d25506e595 | ||
![]() |
a83e90e9ce | ||
![]() |
ea93182d96 | ||
![]() |
d60ee24111 | ||
![]() |
40e5c999b3 | ||
![]() |
6175b805d2 | ||
![]() |
5b6ba8d75f | ||
![]() |
10b0f0b1ce | ||
![]() |
a81ef63075 | ||
![]() |
2dc5edbb55 | ||
![]() |
636be458a6 | ||
![]() |
64283f137c | ||
![]() |
ec1c9a9461 | ||
![]() |
d9a806d236 | ||
![]() |
ebd294b602 | ||
![]() |
6081094b73 | ||
![]() |
115dfd20e7 | ||
![]() |
038fe2866b | ||
![]() |
aec40373d3 | ||
![]() |
c9b8d27139 | ||
![]() |
d0793ebcba | ||
![]() |
00e4e8109e | ||
![]() |
bcc1dc1948 | ||
![]() |
1e67996df0 | ||
![]() |
947bed0a46 | ||
![]() |
0dd9842e84 | ||
![]() |
42728e2694 | ||
![]() |
44d14dc19c | ||
![]() |
2e95c491a1 | ||
![]() |
acfc776462 | ||
![]() |
b6692a7dab | ||
![]() |
34fc2a0d15 | ||
![]() |
07938dcef8 | ||
![]() |
847c5dfdcb | ||
![]() |
bd47603f0d | ||
![]() |
8b86146715 | ||
![]() |
5343c9109b | ||
![]() |
687acae90b | ||
![]() |
871e540957 | ||
![]() |
b0ce0b5350 | ||
![]() |
bc965b1a0c | ||
![]() |
89b60c4c15 | ||
![]() |
47a84775c5 | ||
![]() |
8de0b0cbad | ||
![]() |
b95fbd76a7 | ||
![]() |
6b44cc9b74 | ||
![]() |
0612c70ca1 | ||
![]() |
060972475f | ||
![]() |
28bbeec462 | ||
![]() |
099adadbdc | ||
![]() |
43511c6ecb | ||
![]() |
aa3e9c6fde | ||
![]() |
e53cdcb909 | ||
![]() |
fd14ad3f93 | ||
![]() |
ca1af97e42 | ||
![]() |
4970219ea7 | ||
![]() |
54c5b66ecf | ||
![]() |
8ae09d453d | ||
![]() |
f2e7ec25c8 | ||
![]() |
7f9fff4683 | ||
![]() |
13f2eea298 | ||
![]() |
d6f5a1b242 | ||
![]() |
ab37f0c2c7 | ||
![]() |
9710b37064 | ||
![]() |
54f7568111 | ||
![]() |
06758964c0 | ||
![]() |
b56552e0f4 | ||
![]() |
ef8653f7d2 | ||
![]() |
d5a6987390 | ||
![]() |
8fb92ca05c | ||
![]() |
11c7015c17 | ||
![]() |
8bc5d813b7 | ||
![]() |
012f5f05cc | ||
![]() |
cd65ec7a71 | ||
![]() |
45826df4fe | ||
![]() |
0645bab613 | ||
![]() |
4e409dfb50 | ||
![]() |
52a39b7a71 | ||
![]() |
5278b9fb47 | ||
![]() |
1ecafe3667 | ||
![]() |
190a415907 | ||
![]() |
ca609a9e62 | ||
![]() |
b72ade7b27 | ||
![]() |
e7098d01c5 | ||
![]() |
b04ee56612 | ||
![]() |
72a18e9b73 | ||
![]() |
6be64bbe36 | ||
![]() |
cf3848a54f | ||
![]() |
1a14e8dd4b | ||
![]() |
a25eb7f951 | ||
![]() |
9437cd54d3 | ||
![]() |
be55c5e4a6 | ||
![]() |
f1d774aa07 | ||
![]() |
c109133fcc | ||
![]() |
3538fdfaf7 | ||
![]() |
3468e7d404 | ||
![]() |
ec4afda184 | ||
![]() |
67b1835264 | ||
![]() |
aee64aa589 | ||
![]() |
687e6e237f | ||
![]() |
b48dd5e930 | ||
![]() |
5ee6833610 | ||
![]() |
c998ba3762 | ||
![]() |
2d4f7ab0e9 | ||
![]() |
676d03eb88 | ||
![]() |
68351a480b | ||
![]() |
33e35f1bd3 | ||
![]() |
e82b54994e | ||
![]() |
6f7ecbfb7b | ||
![]() |
c4aa49eb32 | ||
![]() |
32aae44ffc | ||
![]() |
7883534c5e | ||
![]() |
b58c9fb718 | ||
![]() |
99d8f63f9e | ||
![]() |
635e8240d2 | ||
![]() |
117a670aa3 | ||
![]() |
f49c58a1fa | ||
![]() |
2492f1b797 | ||
![]() |
d6781f67b2 | ||
![]() |
2e0c22eb6d | ||
![]() |
211e152863 | ||
![]() |
f7bf4060ea | ||
![]() |
8c953bbf01 | ||
![]() |
4e91761fdf | ||
![]() |
53f8465e67 | ||
![]() |
5818b914a3 | ||
![]() |
0680d39d90 | ||
![]() |
4ca6676be0 | ||
![]() |
3d85e751b7 | ||
![]() |
965da06214 | ||
![]() |
e8bc2816ef | ||
![]() |
28ed7cc8a5 | ||
![]() |
089d9f2e3d | ||
![]() |
9abce7a586 | ||
![]() |
53162b4bd3 | ||
![]() |
80d78a027b | ||
![]() |
b4eda2ed54 | ||
![]() |
4d8d21a815 | ||
![]() |
f3290800d8 | ||
![]() |
22a858c076 | ||
![]() |
823419c032 | ||
![]() |
908d3f64f4 | ||
![]() |
3843d68766 | ||
![]() |
4b599f391c | ||
![]() |
6d8239caab | ||
![]() |
dae37f273a | ||
![]() |
1615bff2d0 | ||
![]() |
b303befbb9 | ||
![]() |
e243c11cc0 | ||
![]() |
c860bca320 | ||
![]() |
a60c1fca36 | ||
![]() |
23f55ef33a | ||
![]() |
5ad2c7a371 | ||
![]() |
e4c729a588 | ||
![]() |
3df0218347 | ||
![]() |
601783aef6 | ||
![]() |
f09c5722be | ||
![]() |
e73ae99e38 | ||
![]() |
10c5c99385 | ||
![]() |
ac5af81344 | ||
![]() |
823563c84f | ||
![]() |
47410c5eb6 | ||
![]() |
af135f4ae9 | ||
![]() |
3eeced3a04 | ||
![]() |
8ea99b548d | ||
![]() |
ae73e8188d | ||
![]() |
9c5cda72da | ||
![]() |
fb1e24799d | ||
![]() |
5721948434 | ||
![]() |
d7b6f413be | ||
![]() |
959ec4667d | ||
![]() |
20433db169 | ||
![]() |
54465798e9 | ||
![]() |
313147d224 | ||
![]() |
84bd947eca | ||
![]() |
366abc4431 | ||
![]() |
2f7fa2f063 | ||
![]() |
205f6cac12 | ||
![]() |
f602b3db24 | ||
![]() |
0d72f1f228 | ||
![]() |
508125e68f | ||
![]() |
cfb714e13c | ||
![]() |
b9dcfd1b02 | ||
![]() |
a1b2dc67b8 | ||
![]() |
2207c5a961 | ||
![]() |
2caa09bb1e | ||
![]() |
68906f6e40 | ||
![]() |
2cf3cf15e3 | ||
![]() |
88202c1f7f | ||
![]() |
6293c787e7 | ||
![]() |
f67fda3bf4 | ||
![]() |
0a70f91bf1 | ||
![]() |
88945be5d7 | ||
![]() |
c81dd24fe7 | ||
![]() |
af94dd2757 | ||
![]() |
0a07738c5b | ||
![]() |
e05783a25a | ||
![]() |
27d4ab3967 | ||
![]() |
04b3efbbc2 | ||
![]() |
ccd5347be2 | ||
![]() |
e1c5f3ed6d | ||
![]() |
c1fb41204c | ||
![]() |
b50733054f | ||
![]() |
e324773c91 | ||
![]() |
6a4c34d5df | ||
![]() |
bc34b9f176 | ||
![]() |
96af05fbef | ||
![]() |
842d9e79ce | ||
![]() |
086d8b32e9 | ||
![]() |
a6390f2bc5 | ||
![]() |
1ab0827ae7 | ||
![]() |
db0f651f33 | ||
![]() |
7f163c3945 | ||
![]() |
cb53867b37 | ||
![]() |
b5b907c9ed | ||
![]() |
2f8ef4aec2 | ||
![]() |
e5929c850c | ||
![]() |
86771a87c6 | ||
![]() |
5df131140f | ||
![]() |
8002483e68 | ||
![]() |
ce84264490 | ||
![]() |
fca6ac0dbc | ||
![]() |
da61eb8988 | ||
![]() |
ef22d43f46 | ||
![]() |
f63f1361ce | ||
![]() |
547c9174b1 | ||
![]() |
cb292bbf06 | ||
![]() |
784c41b83a | ||
![]() |
42fbe9270a | ||
![]() |
8067f1948e | ||
![]() |
143cc6e8d0 | ||
![]() |
628ab0ca82 | ||
![]() |
cc13d7edf6 | ||
![]() |
6ea2c18384 | ||
![]() |
fb316d9068 | ||
![]() |
fda730dcad | ||
![]() |
8d921202fa | ||
![]() |
615794a4bf | ||
![]() |
5eed9d7038 | ||
![]() |
e6b135e151 | ||
![]() |
dbde3b469a | ||
![]() |
7841bcab06 | ||
![]() |
efe1346d41 | ||
![]() |
f162902b36 | ||
![]() |
a752683965 | ||
![]() |
683bd6e2d4 | ||
![]() |
13426fe7ec | ||
![]() |
d87d0f87b1 | ||
![]() |
85ec169755 | ||
![]() |
9cc40144a9 | ||
![]() |
42beb6018e | ||
![]() |
2d0e82159d | ||
![]() |
08ad94a2f5 | ||
![]() |
0eebea0ef7 | ||
![]() |
e4888b83d8 | ||
![]() |
37f8066901 | ||
![]() |
92f976916f | ||
![]() |
530ad7ecf5 | ||
![]() |
c1bc750059 | ||
![]() |
364061fde0 | ||
![]() |
1b4826f966 | ||
![]() |
580eacdb18 | ||
![]() |
270b8c9041 | ||
![]() |
9f7590783d | ||
![]() |
63dceed010 | ||
![]() |
ee3e0dd0e1 | ||
![]() |
e19d59c4ab | ||
![]() |
d29132a540 | ||
![]() |
0037811fb5 | ||
![]() |
1f1b8d0074 | ||
![]() |
dae6aeb4b5 | ||
![]() |
9bfb249425 | ||
![]() |
0bbcecc1c3 | ||
![]() |
a871e35449 | ||
![]() |
9961031b0e | ||
![]() |
c2d3214c01 | ||
![]() |
13e14f3e62 | ||
![]() |
ffe435051b | ||
![]() |
5e00b2ec33 | ||
![]() |
a73fbccd22 | ||
![]() |
48cdc3bb5e | ||
![]() |
535f71c18c | ||
![]() |
9ceeb71448 | ||
![]() |
457449866e | ||
![]() |
427428d09b | ||
![]() |
82b203c9c3 | ||
![]() |
9677c2deee | ||
![]() |
9000745877 | ||
![]() |
2fb413123c | ||
![]() |
6316b82600 | ||
![]() |
2746d797ee | ||
![]() |
7e5a7ad153 | ||
![]() |
e947059da1 | ||
![]() |
65aaaa3d77 | ||
![]() |
c2b2c94e1b | ||
![]() |
e8465f23b9 | ||
![]() |
09d37ab232 | ||
![]() |
f413355610 | ||
![]() |
5a97cd7dd2 | ||
![]() |
7b354d1582 | ||
![]() |
b964a33ae2 | ||
![]() |
dfa513da24 | ||
![]() |
dcee9128f9 | ||
![]() |
30a8e1b4c5 |
BIN
.github/cookies-chrome.png
vendored
Normal file
BIN
.github/cookies-chrome.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 480 KiB |
BIN
.github/cookies-firefox.png
vendored
Normal file
BIN
.github/cookies-firefox.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 439 KiB |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -200,3 +200,4 @@ FakesAssemblies/
|
||||
/TestResults
|
||||
*.DS_Store
|
||||
.idea/
|
||||
launchSettings.json
|
||||
|
@@ -2,8 +2,8 @@
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "Jackett"
|
||||
#define MyAppVersion GetFileVersion("BuildOutput\FullFramework\Windows\Jackett\Jackett.Common.dll")
|
||||
#define MyAppPublisher "Jackett Inc."
|
||||
#define MyAppVersion GetFileVersion(MyFileForVersion)
|
||||
#define MyAppPublisher "Jackett"
|
||||
#define MyAppURL "https://github.com/Jackett/Jackett"
|
||||
#define MyAppExeName "JackettTray.exe"
|
||||
|
||||
@@ -22,9 +22,11 @@ AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
OutputBaseFilename=Jackett.Installer.Windows
|
||||
SetupIconFile=src\Jackett.Console\jackett.ico
|
||||
UninstallDisplayIcon={commonappdata}\Jackett\JackettConsole.exe
|
||||
OutputBaseFilename={#MyOutputFilename}
|
||||
SetupIconFile=src\Jackett.Tray\jackett.ico
|
||||
UninstallDisplayIcon={commonappdata}\Jackett\{#MyAppExeName}
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
UninstallDisplayName={#MyAppName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
DisableDirPage=yes
|
||||
@@ -37,9 +39,7 @@ Name: "windowsService"; Description: "Install as a Windows Service"
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "BuildOutput\FullFramework\Windows\Jackett\JackettTray.exe"; DestDir: "{commonappdata}\Jackett"; Flags: ignoreversion
|
||||
Source: "BuildOutput\FullFramework\Windows\Jackett\JackettUpdater.exe"; DestDir: "{commonappdata}\Jackett"; Flags: ignoreversion
|
||||
Source: "BuildOutput\FullFramework\Windows\Jackett\*"; DestDir: "{commonappdata}\Jackett"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "{#MySourceFolder}\*"; DestDir: "{commonappdata}\Jackett"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||
|
||||
[Icons]
|
||||
@@ -48,16 +48,15 @@ Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||
Name: "{commondesktop}\{#MyAppName}"; Filename: "{commonappdata}\Jackett\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Uninstall"; Flags: waituntilterminated runhidden;
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--ReserveUrls"; Flags: waituntilterminated runhidden;
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Install"; Flags: waituntilterminated runhidden; Tasks: windowsService
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Start"; Flags: waituntilterminated runhidden; Tasks: windowsService
|
||||
Filename: "{commonappdata}\Jackett\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
[Run]
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Uninstall"; Flags: waituntilterminated;
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--ReserveUrls"; Flags: waituntilterminated;
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--MigrateSettings"; Flags: waituntilterminated;
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Install"; Flags: waituntilterminated; Tasks: windowsService
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Start"; Flags: waituntilterminated; Tasks: windowsService
|
||||
|
||||
[UninstallRun]
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Uninstall"; Flags: waituntilterminated skipifdoesntexist
|
||||
Filename: "{commonappdata}\Jackett\JackettConsole.exe"; Parameters: "--Uninstall"; Flags: waituntilterminated skipifdoesntexist runhidden
|
||||
Filename: "{sys}\taskkill.exe"; Parameters: "/f /im {#MyAppExeName}"; Flags: waituntilterminated skipifdoesntexist runhidden
|
||||
Filename: "{sys}\taskkill.exe"; Parameters: "/f /im JackettConsole.exe"; Flags: waituntilterminated skipifdoesntexist runhidden
|
||||
|
||||
|
||||
|
32
README.md
32
README.md
@@ -2,7 +2,6 @@
|
||||
|
||||
[](https://github.com/Jackett/Jackett/issues)
|
||||
[](https://github.com/Jackett/Jackett/pulls)
|
||||
[](https://www.bountysource.com/teams/jackett)
|
||||
[](https://ci.appveyor.com/project/Jackett/jackett)
|
||||
[](https://github.com/Jackett/Jackett/releases/latest)
|
||||
[](https://hub.docker.com/r/linuxserver/jackett/)
|
||||
@@ -17,8 +16,8 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
|
||||
|
||||
#### Supported Systems
|
||||
* Windows using .NET 4.5.2 [Download here](https://www.microsoft.com/net/framework/versions/net452).
|
||||
* Linux and macOS using Mono 4.6.0 and above. [Download here](http://www.mono-project.com/download/). Earlier versions of mono may work but some trackers may fail to negotiate SSL correctly, and others may cause Jackett to crash when used.
|
||||
* Windows using .NET 4.6.1 or above [Download here](https://www.microsoft.com/net/framework/versions/net461).
|
||||
* Linux and macOS using Mono 5.8 or above. [Download here](http://www.mono-project.com/download/).
|
||||
|
||||
### Supported Public Trackers
|
||||
* 1337x
|
||||
@@ -37,12 +36,15 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Horrible Subs
|
||||
* Idope
|
||||
* Il Corsaro Nero <!-- maintained by bonny1992 -->
|
||||
* Il Corsaro Blu
|
||||
* Isohunt2
|
||||
* KickAssTorrent
|
||||
* KickAssTorrent (thekat.se clone)
|
||||
* LimeTorrents
|
||||
* MagnetDL
|
||||
* MejorTorrent <!-- maintained by ivandelabeldad -->
|
||||
* NextTorrent
|
||||
* Newpct (aka: tvsinpagar, descargas2020, torrentlocura, torrentrapid, etc)
|
||||
* Nyaa.si
|
||||
* Nyaa-Pantsu
|
||||
* Nyoo
|
||||
@@ -91,7 +93,6 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Abnormal
|
||||
* Acid-Lounge
|
||||
* AlphaRatio
|
||||
* Andraste
|
||||
* AnimeBytes
|
||||
* AnimeTorrents
|
||||
* AOX
|
||||
@@ -125,6 +126,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* BroadcastTheNet
|
||||
* BrokenStones
|
||||
* BTNext
|
||||
* BTXpress
|
||||
* Carpathians
|
||||
* CCFBits
|
||||
* CGPeers
|
||||
@@ -158,11 +160,11 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Fuzer
|
||||
* GayTorrent.ru
|
||||
* GazelleGames
|
||||
* GFTracker
|
||||
* Gfxnews
|
||||
* GFXPeers
|
||||
* GigaTorrents
|
||||
* GimmePeers <!-- maintained by jamesb2147 -->
|
||||
* Girotottent
|
||||
* GODS [![(invite needed)][inviteneeded]](#)
|
||||
* Greek Team
|
||||
* HacheDe
|
||||
@@ -170,6 +172,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* HD-Forever
|
||||
* HD-Only
|
||||
* HD-Space
|
||||
* HD-Spain
|
||||
* HD-Torrents
|
||||
* HD-Bits.com
|
||||
* HDBits
|
||||
@@ -209,7 +212,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* notwhat.cd
|
||||
* Ourbits
|
||||
* Passione Torrent <!-- maintained by bonny1992 -->
|
||||
* PassThePopcorn [![(invite needed)][inviteneeded]](#)
|
||||
* PassThePopcorn
|
||||
* PirateTheNet
|
||||
* PiXELHD
|
||||
* PolishSource
|
||||
@@ -219,8 +222,10 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Psytorrents
|
||||
* PTFiles
|
||||
* PuntoTorrent
|
||||
* Racing4Everyone (R4E)
|
||||
* Redacted (PassTheHeadphones)
|
||||
* RevolutionTT
|
||||
* RGU
|
||||
* RoDVD
|
||||
* SceneFZ
|
||||
* SceneReactor
|
||||
@@ -237,10 +242,9 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* SportsCult
|
||||
* SportHD
|
||||
* Superbits
|
||||
* Synthesiz3r
|
||||
* Tasmanit
|
||||
* TBPlus
|
||||
* TehConnection
|
||||
* TehConnection.me
|
||||
* TenYardTracker
|
||||
* The Empire
|
||||
* The Geeks
|
||||
@@ -268,7 +272,7 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
* Torrents.Md
|
||||
* TorrentSeeds
|
||||
* Torrent-Syndikat
|
||||
* TorrentWTF
|
||||
* TOrrent-tuRK (TORK)
|
||||
* TorViet
|
||||
* ToTheGlory
|
||||
* TranceTraffic
|
||||
@@ -316,7 +320,7 @@ When installed as a service the tray icon acts as a way to open/start/stop Jacke
|
||||
Jackett can also be run from the command line if you would like to see log messages (Ensure the server isn't already running from the tray/service). This can be done by using "JackettConsole.exe" (for Command Prompt), found in the Jackett data folder: "%ProgramData%\Jackett".
|
||||
|
||||
## Installation on Linux
|
||||
1. Install [Mono 4.6](http://www.mono-project.com/download/#download-lin) or better (using the latest stable release for your distribution is recommended)
|
||||
1. Install [Mono 5.8](http://www.mono-project.com/download/#download-lin) or better (using the latest stable release is recommended)
|
||||
* Follow the instructions on the mono website and install the `mono-devel` and the `ca-certificates-mono` packages.
|
||||
* On Red Hat/CentOS/openSUSE/Fedora the `mono-locale-extras` package is also required.
|
||||
2. Install libcurl:
|
||||
@@ -329,10 +333,12 @@ Detailed instructions for [Ubuntu 14.x](http://www.htpcguides.com/install-jacket
|
||||
|
||||
If you want to run it with a user without a /home directory you need to add `Environment=XDG_CONFIG_HOME=/path/to/folder` to your systemd file, this folder will be used to store your config files.
|
||||
|
||||
Mono must be compiled with the Roslyn compiler (default), using MCS will cause "An error has occurred." errors (See https://github.com/Jackett/Jackett/issues/2704).
|
||||
|
||||
## Installation on macOS
|
||||
|
||||
### Prerequisites
|
||||
Install [Mono 4.6](http://www.mono-project.com/download/#download-mac) or better (using the latest pkg installer is recommended).
|
||||
Install [Mono 5.8](http://www.mono-project.com/download/#download-mac) or better (using the latest pkg installer is recommended).
|
||||
* Setup ssl support by running `curl -sS https://curl.haxx.se/ca/cacert.pem | cert-sync --user /dev/stdin`
|
||||
|
||||
### Install as service
|
||||
@@ -398,7 +404,9 @@ All contributions are welcome just send a pull request. Jackett's framework all
|
||||
## Building from source
|
||||
|
||||
### Windows
|
||||
* Open the Jackett solution in Visual Studio 2017
|
||||
* Install the .NET Core [SDK](https://www.microsoft.com/net/download/windows)
|
||||
* Open the Jackett solution in Visual Studio 2017 (version 15.7 or above)
|
||||
* Right click on the Jackett solution and click 'Rebuild Solution' to restore nuget packages
|
||||
* Select Jackett.Console as startup project
|
||||
* Build/Start the project
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
version: 0.8.{build}
|
||||
version: 0.9.{build}
|
||||
skip_tags: true
|
||||
image: Visual Studio 2017
|
||||
configuration: Release
|
||||
@@ -27,6 +27,7 @@ deploy:
|
||||
description: $(release_description)
|
||||
auth_token:
|
||||
secure: hOg+16YTIbq4kO9u4D1YVOTbWDqgCX6mAQYMbnmBBSw2CiUsZh7OKbupoUb3FtWa
|
||||
artifact: /^(?:(?![Ee]xperimental).)*$/
|
||||
draft: true
|
||||
on:
|
||||
branch: master
|
||||
|
170
build.cake
170
build.cake
@@ -6,7 +6,7 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
var configuration = Argument("configuration", "Debug");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PREPARATION
|
||||
@@ -36,8 +36,8 @@ Task("Clean")
|
||||
.IsDependentOn("Info")
|
||||
.Does(() =>
|
||||
{
|
||||
CleanDirectories("./src/**/obj" + configuration);
|
||||
CleanDirectories("./src/**/bin" + configuration);
|
||||
CleanDirectories("./src/**/obj");
|
||||
CleanDirectories("./src/**/bin");
|
||||
CleanDirectories("./BuildOutput");
|
||||
CleanDirectories("./" + artifactsDirName);
|
||||
CleanDirectories("./" + testResultsDirName);
|
||||
@@ -56,7 +56,11 @@ Task("Build")
|
||||
.IsDependentOn("Restore-NuGet-Packages")
|
||||
.Does(() =>
|
||||
{
|
||||
MSBuild("./src/Jackett.sln", settings => settings.SetConfiguration(configuration));
|
||||
var buildSettings = new MSBuildSettings()
|
||||
.SetConfiguration(configuration)
|
||||
.UseToolVersion(MSBuildToolVersion.VS2017);
|
||||
|
||||
MSBuild("./src/Jackett.sln", buildSettings);
|
||||
});
|
||||
|
||||
Task("Run-Unit-Tests")
|
||||
@@ -84,9 +88,7 @@ Task("Copy-Files-Full-Framework")
|
||||
var windowsOutput = windowsBuildFullFramework + "/Jackett";
|
||||
|
||||
CopyDirectory("./src/Jackett.Console/bin/" + configuration, windowsOutput);
|
||||
CopyFiles("./src/Jackett.Service/bin/" + configuration + "/JackettService.*", windowsOutput);
|
||||
CopyFiles("./src/Jackett.Tray/bin/" + configuration + "/JackettTray.*", windowsOutput);
|
||||
CopyFiles("./src/Jackett.Updater/bin/" + configuration + "/JackettUpdater.*", windowsOutput);
|
||||
CopyFiles("./src/Jackett.Updater/bin/" + configuration + "/net452" + "/JackettUpdater.*", windowsOutput); //builds against multiple frameworks
|
||||
CopyFiles("./Upstart.config", windowsOutput);
|
||||
CopyFiles("./LICENSE", windowsOutput);
|
||||
CopyFiles("./README.md", windowsOutput);
|
||||
@@ -115,49 +117,118 @@ Task("Check-Packaging-Platform")
|
||||
}
|
||||
});
|
||||
|
||||
Task("Package-Windows-Installer-Full-Framework")
|
||||
.IsDependentOn("Check-Packaging-Platform")
|
||||
.Does(() =>
|
||||
{
|
||||
InnoSetup("./Installer.iss", new InnoSetupSettings {
|
||||
OutputDirectory = workingDir + "/" + artifactsDirName
|
||||
});
|
||||
});
|
||||
|
||||
Task("Package-Files-Full-Framework-Windows")
|
||||
.IsDependentOn("Check-Packaging-Platform")
|
||||
.Does(() =>
|
||||
{
|
||||
Zip(windowsBuildFullFramework, $"./{artifactsDirName}/Jackett.Binaries.Windows.zip");
|
||||
Information(@"Full Framework Windows Binaries Zipping Completed");
|
||||
});
|
||||
|
||||
Task("Package-Files-Full-Framework-Mono")
|
||||
.IsDependentOn("Check-Packaging-Platform")
|
||||
.Does(() =>
|
||||
{
|
||||
var cygMonoBuildPath = RelativeWinPathToCygPath(monoBuildFullFramework);
|
||||
var tarFileName = "Jackett.Binaries.Mono.tar";
|
||||
var tarArguments = @"-cvf " + cygMonoBuildPath + "/" + tarFileName + " -C " + cygMonoBuildPath + " Jackett --mode ='755'";
|
||||
var gzipArguments = @"-k " + cygMonoBuildPath + "/" + tarFileName;
|
||||
|
||||
RunCygwinCommand("Tar", tarArguments);
|
||||
RunCygwinCommand("Gzip", gzipArguments);
|
||||
|
||||
MoveFile($"{monoBuildFullFramework}/{tarFileName}.gz", $"./{artifactsDirName}/{tarFileName}.gz");
|
||||
Gzip(monoBuildFullFramework, $"./{artifactsDirName}", "Jackett", "Jackett.Binaries.Mono.tar.gz");
|
||||
Information(@"Full Framework Mono Binaries Gzip Completed");
|
||||
});
|
||||
|
||||
Task("Package-Full-Framework")
|
||||
.IsDependentOn("Package-Windows-Installer-Full-Framework")
|
||||
.IsDependentOn("Package-Files-Full-Framework-Windows")
|
||||
.IsDependentOn("Package-Files-Full-Framework-Mono")
|
||||
.Does(() =>
|
||||
{
|
||||
Information("Full Framwork Packaging Completed");
|
||||
Information("Full Framework Packaging Completed");
|
||||
});
|
||||
|
||||
Task("Kestrel-Full-Framework")
|
||||
.IsDependentOn("Package-Full-Framework")
|
||||
.Does(() =>
|
||||
{
|
||||
CleanDirectories("./src/**/obj");
|
||||
CleanDirectories("./src/**/bin");
|
||||
|
||||
NuGetRestore("./src/Jackett.sln");
|
||||
|
||||
var buildSettings = new MSBuildSettings()
|
||||
.SetConfiguration(configuration)
|
||||
.UseToolVersion(MSBuildToolVersion.VS2017);
|
||||
|
||||
MSBuild("./src/Jackett.sln", buildSettings);
|
||||
});
|
||||
|
||||
Task("Experimental-Kestrel-Windows-Full-Framework")
|
||||
.IsDependentOn("Kestrel-Full-Framework")
|
||||
.Does(() =>
|
||||
{
|
||||
string serverProjectPath = "./src/Jackett.Server/Jackett.Server.csproj";
|
||||
string buildOutputPath = "./BuildOutput/Experimental/net461/win7-x86/Jackett";
|
||||
|
||||
DotNetCorePublish(serverProjectPath, "net461", "win7-x86");
|
||||
|
||||
CopyFiles("./src/Jackett.Service/bin/" + configuration + "/JackettService.*", buildOutputPath);
|
||||
CopyFiles("./src/Jackett.Tray/bin/" + configuration + "/JackettTray.*", buildOutputPath);
|
||||
CopyFiles("./src/Jackett.Updater/bin/" + configuration + "/net461" + "/JackettUpdater.*", buildOutputPath); //builds against multiple frameworks
|
||||
|
||||
Zip("./BuildOutput/Experimental/net461/win7-x86", $"./{artifactsDirName}/Jackett.Binaries.Windows.zip");
|
||||
|
||||
//InnoSetup
|
||||
string sourceFolder = MakeAbsolute(Directory(buildOutputPath)).ToString();
|
||||
|
||||
InnoSetupSettings settings = new InnoSetupSettings();
|
||||
settings.OutputDirectory = workingDir + "/" + artifactsDirName;
|
||||
settings.Defines = new Dictionary<string, string>
|
||||
{
|
||||
{ "MyFileForVersion", sourceFolder + "/Jackett.Common.dll" },
|
||||
{ "MySourceFolder", sourceFolder },
|
||||
{ "MyOutputFilename", "Jackett.Installer.Windows" },
|
||||
};
|
||||
|
||||
InnoSetup("./Installer.iss", settings);
|
||||
});
|
||||
|
||||
Task("Experimental-Kestrel-Mono-Full-Framework")
|
||||
.IsDependentOn("Kestrel-Full-Framework")
|
||||
.Does(() =>
|
||||
{
|
||||
string serverProjectPath = "./src/Jackett.Server/Jackett.Server.csproj";
|
||||
string buildOutputPath = "./BuildOutput/Experimental/net461/linux-x64/Jackett";
|
||||
|
||||
DotNetCorePublish(serverProjectPath, "net461", "linux-x64");
|
||||
|
||||
CopyFiles("./src/Jackett.Updater/bin/" + configuration + "/net461" + "/JackettUpdater.*", buildOutputPath); //builds against multiple frameworks
|
||||
|
||||
//There is an issue with Mono 5.8 (fixed in Mono 5.12) where its expecting to use its own patched version of System.Net.Http.dll, instead of the version supplied in folder
|
||||
//https://github.com/dotnet/corefx/issues/19914
|
||||
//https://bugzilla.xamarin.com/show_bug.cgi?id=60315
|
||||
//The workaround is to delete System.Net.Http.dll and patch the .exe.config file
|
||||
|
||||
DeleteFile(buildOutputPath + "/System.Net.Http.dll");
|
||||
|
||||
var configFile = File(buildOutputPath + "/JackettConsole.exe.config");
|
||||
XmlPoke(configFile, "configuration/runtime/*[name()='assemblyBinding']/*[name()='dependentAssembly']/*[name()='assemblyIdentity'][@name='System.Net.Http']/../*[name()='bindingRedirect']/@newVersion", "4.0.0.0");
|
||||
|
||||
Gzip("./BuildOutput/Experimental/net461/linux-x64", $"./{artifactsDirName}", "Jackett", "Experimental.Jackett.Binaries.Mono.tar.gz");
|
||||
});
|
||||
|
||||
Task("Experimental-DotNetCore")
|
||||
.IsDependentOn("Kestrel-Full-Framework")
|
||||
.Does(() =>
|
||||
{
|
||||
string serverProjectPath = "./src/Jackett.Server/Jackett.Server.csproj";
|
||||
|
||||
DotNetCorePublish(serverProjectPath, "netcoreapp2.1", "win-x86");
|
||||
DotNetCorePublish(serverProjectPath, "netcoreapp2.1", "linux-x64");
|
||||
DotNetCorePublish(serverProjectPath, "netcoreapp2.1", "osx-x64");
|
||||
|
||||
Zip("./BuildOutput/Experimental/netcoreapp2.1/win-x86", $"./{artifactsDirName}/Experimental.netcoreapp.win-x86.zip");
|
||||
Zip("./BuildOutput/Experimental/netcoreapp2.1/osx-x64", $"./{artifactsDirName}/Experimental.netcoreapp.osx-x64.zip");
|
||||
Gzip("./BuildOutput/Experimental/netcoreapp2.1/linux-x64", $"./{artifactsDirName}", "Jackett", "Experimental.netcoreapp.linux-x64.tar.gz");
|
||||
});
|
||||
|
||||
Task("Experimental")
|
||||
.IsDependentOn("Experimental-Kestrel-Windows-Full-Framework")
|
||||
.IsDependentOn("Experimental-Kestrel-Mono-Full-Framework")
|
||||
//.IsDependentOn("Experimental-DotNetCore")
|
||||
.Does(() =>
|
||||
{
|
||||
Information("Experimental builds completed");
|
||||
});
|
||||
|
||||
Task("Appveyor-Push-Artifacts")
|
||||
.IsDependentOn("Package-Full-Framework")
|
||||
.IsDependentOn("Experimental")
|
||||
.Does(() =>
|
||||
{
|
||||
if (AppVeyor.IsRunningOnAppVeyor)
|
||||
@@ -263,10 +334,35 @@ private void RunCygwinCommand(string utility, string utilityArguments)
|
||||
private string RelativeWinPathToCygPath(string relativePath)
|
||||
{
|
||||
var cygdriveBase = "/cygdrive/" + workingDir.ToString().Replace(":", "").Replace("\\", "/");
|
||||
var cygPath = cygdriveBase + relativePath.Replace(".", "");
|
||||
var cygPath = cygdriveBase + relativePath.TrimStart('.');
|
||||
return cygPath;
|
||||
}
|
||||
|
||||
private void Gzip(string sourceFolder, string outputDirectory, string tarCdirectoryOption, string outputFileName)
|
||||
{
|
||||
var cygSourcePath = RelativeWinPathToCygPath(sourceFolder);
|
||||
var tarFileName = outputFileName.Remove(outputFileName.Length - 3, 3);
|
||||
var tarArguments = @"-cvf " + cygSourcePath + "/" + tarFileName + " -C " + cygSourcePath + $" {tarCdirectoryOption} --mode ='755'";
|
||||
var gzipArguments = @"-k " + cygSourcePath + "/" + tarFileName;
|
||||
|
||||
RunCygwinCommand("Tar", tarArguments);
|
||||
RunCygwinCommand("Gzip", gzipArguments);
|
||||
|
||||
MoveFile($"{sourceFolder}/{tarFileName}.gz", $"{outputDirectory}/{tarFileName}.gz");
|
||||
}
|
||||
|
||||
private void DotNetCorePublish(string projectPath, string framework, string runtime)
|
||||
{
|
||||
var settings = new DotNetCorePublishSettings
|
||||
{
|
||||
Framework = framework,
|
||||
Runtime = runtime,
|
||||
OutputDirectory = $"./BuildOutput/Experimental/{framework}/{runtime}/Jackett"
|
||||
};
|
||||
|
||||
DotNetCorePublish(projectPath, settings);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// TASK TARGETS
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@@ -87,6 +87,7 @@ function loadJackettSettings() {
|
||||
$("#jackett-prerelease").attr('checked', data.prerelease);
|
||||
$("#jackett-logging").attr('checked', data.logging);
|
||||
$("#jackett-omdbkey").val(data.omdbkey);
|
||||
$("#jackett-omdburl").val(data.omdburl);
|
||||
var password = data.password;
|
||||
$("#jackett-adminpwd").val(password);
|
||||
if (password != null && password != '') {
|
||||
@@ -497,10 +498,14 @@ function populateConfigItems(configForm, config) {
|
||||
hasReacaptcha = true;
|
||||
captchaItem = config[i];
|
||||
}
|
||||
else if (config[i].id === 'cookieheader' && hasReacaptcha) { // inject cookie into captcha item
|
||||
captchaItem.cookieheader = config[i].value;
|
||||
console.log(captchaItem);
|
||||
}
|
||||
}
|
||||
|
||||
var setupItemTemplate = Handlebars.compile($("#setup-item").html());
|
||||
if (hasReacaptcha && !window.jackettIsLocal) {
|
||||
if (hasReacaptcha && !window.jackettIsLocal && false) { // disable this for now, use inline cookie (below)
|
||||
var setupValueTemplate = Handlebars.compile($("#setup-item-nonlocalrecaptcha").html());
|
||||
captchaItem.value_element = setupValueTemplate(captchaItem);
|
||||
var template = setupItemTemplate(captchaItem);
|
||||
@@ -509,11 +514,22 @@ function populateConfigItems(configForm, config) {
|
||||
|
||||
for (var i = 0; i < config.length; i++) {
|
||||
var item = config[i];
|
||||
var setupValueTemplate = Handlebars.compile($("#setup-item-" + item.type).html());
|
||||
item.value_element = setupValueTemplate(item);
|
||||
var template = setupItemTemplate(item);
|
||||
$formItemContainer.append(template);
|
||||
if ((item.id === 'username' || item.id === 'password') && hasReacaptcha) {
|
||||
continue; // skip username/password if there's a recaptcha
|
||||
}
|
||||
if (item.type != 'recaptcha') {
|
||||
var setupValueTemplate = Handlebars.compile($("#setup-item-" + item.type).html());
|
||||
item.value_element = setupValueTemplate(item);
|
||||
var template = setupItemTemplate(item);
|
||||
$formItemContainer.append(template);
|
||||
}
|
||||
if (item.type === 'recaptcha') {
|
||||
// inject cookie dialog until recaptcha can be solved again
|
||||
var setupValueTemplate = Handlebars.compile($("#setup-item-nonlocalrecaptcha").html());
|
||||
captchaItem.value_element = setupValueTemplate(captchaItem);
|
||||
var template = setupItemTemplate(captchaItem);
|
||||
$formItemContainer.append(template);
|
||||
/*
|
||||
var jackettrecaptcha = $('.jackettrecaptcha');
|
||||
jackettrecaptcha.data("version", item.version);
|
||||
switch (item.version) {
|
||||
@@ -543,6 +559,7 @@ function populateConfigItems(configForm, config) {
|
||||
});
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1134,6 +1151,7 @@ function bindUIButtons() {
|
||||
var jackett_prerelease = $("#jackett-prerelease").is(':checked');
|
||||
var jackett_logging = $("#jackett-logging").is(':checked');
|
||||
var jackett_omdb_key = $("#jackett-omdbkey").val();
|
||||
var jackett_omdb_url = $("#jackett-omdburl").val();
|
||||
|
||||
var jackett_proxy_url = $("#jackett-proxy-url").val();
|
||||
var jackett_proxy_type = $("#jackett-proxy-type").val();
|
||||
@@ -1150,6 +1168,7 @@ function bindUIButtons() {
|
||||
logging: jackett_logging,
|
||||
basepathoverride: jackett_basepathoverride,
|
||||
omdbkey: jackett_omdb_key,
|
||||
omdburl: jackett_omdb_url,
|
||||
proxy_type: jackett_proxy_type,
|
||||
proxy_url: jackett_proxy_url,
|
||||
proxy_port: jackett_proxy_port,
|
||||
|
@@ -30,7 +30,7 @@
|
||||
<script type="text/javascript" src="../bootstrap/bootstrap.min.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../libs/bootstrap-notify.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../libs/bootstrap-multiselect.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
|
||||
<!--<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>-->
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../bootstrap/bootstrap.min.css?changed=2017083001">
|
||||
<link rel="stylesheet" type="text/css" href="../animate.css?changed=2017083001">
|
||||
@@ -94,11 +94,11 @@
|
||||
<hr />
|
||||
<h3>Jackett Configuration</h3>
|
||||
<div class="text-center">
|
||||
<div class="btn-group">
|
||||
<div class="btn-toolbar">
|
||||
<button id="change-jackett-port" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-wrench"></i> Apply server settings <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="view-jackett-logs" class="btn btn-danger btn-sm">
|
||||
<button id="view-jackett-logs" class="btn btn-success btn-sm">
|
||||
<i class="fa fa-rss"></i> View logs <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="trigger-updater" class="btn btn-warning btn-sm">
|
||||
@@ -177,6 +177,10 @@
|
||||
<span class="input-header">OMDB API key: </span>
|
||||
<input id="jackett-omdbkey" class="form-control input-right" type="text" value="" placeholder="">
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<span class="input-header">OMDB API Url: </span>
|
||||
<input id="jackett-omdburl" class="form-control input-right" type="text" value="" placeholder="Blank for default">
|
||||
</div>
|
||||
<hr />
|
||||
<div id="footer">
|
||||
<a href="https://github.com/Jackett/Jackett" target="_blank" title="Jackett on GitHub">Jackett</a> Version <span id="app-version"></span>
|
||||
@@ -189,7 +193,7 @@
|
||||
<div class="setup-item-recaptcha">
|
||||
<p>This site requires you to solve a ReCaptcha. It's no longer possible to solve the captcha in Jackett. Please enter the cookie for the site manually. <a href="https://github.com/Jackett/Jackett/wiki/Finding-cookies" target="_blank">See here</a> on how get the cookies.</p>
|
||||
<div class="setup-item-label">Full cookie header</div>
|
||||
<input class="form-control" type="text" value="" />
|
||||
<input class="form-control" type="text" value="{{cookieheader}}" />
|
||||
</div>
|
||||
</script>
|
||||
<script id="setup-item" type="text/x-handlebars-template">
|
||||
@@ -659,6 +663,6 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="../libs/api.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=2017110602"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20180709"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -120,11 +120,17 @@
|
||||
paths:
|
||||
# present trending results if there are no search parms supplied
|
||||
- path: "{{if .Keywords}}/search/{{ .Keywords}}/1/{{else}}/trending{{end}}"
|
||||
keywordsfilters:
|
||||
- name: replace # use this as a workaround till #893 is implemented
|
||||
args: ["Greys Anatomy", "Grey's Anatomy"]
|
||||
rows:
|
||||
selector: tr:has(a[href^="/torrent/"])
|
||||
fields:
|
||||
title:
|
||||
selector: td[class^="coll-1"] a[href^="/torrent/"]
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Grey's Anatomy", "Greys Anatomy"]
|
||||
category:
|
||||
optional: true
|
||||
selector: td[class^="coll-1"] a[href^="/sub/"]
|
||||
|
@@ -48,9 +48,6 @@
|
||||
- selector: span.errormsg
|
||||
test:
|
||||
path: index.php
|
||||
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
|
||||
search:
|
||||
paths:
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
site: Bittorrentfiles
|
||||
name: Bittorrentfiles
|
||||
description: "Bittorrentfiles is a Private GERMAN tracker"
|
||||
@@ -135,22 +135,22 @@
|
||||
selector: a[href^="/download.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(5) > a
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2)
|
||||
selector: td:nth-child(6)
|
||||
size:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(6)
|
||||
selector: td:nth-child(8)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
seeders:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2)
|
||||
selector: td:nth-child(3) > a
|
||||
leechers:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(3)
|
||||
selector: td:nth-child(4)
|
||||
date:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(10)
|
||||
selector: td:nth-child(9)
|
||||
filters:
|
||||
- name: split
|
||||
args: ["by", 0]
|
||||
@@ -168,4 +168,4 @@
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[alt="2xU"]: "2"
|
||||
"*": "1"
|
||||
"*": "1"
|
||||
|
@@ -38,10 +38,13 @@
|
||||
type: info
|
||||
label: Category Id Note
|
||||
default: "You can filter your searches by using any of the following category numbers (comma delimited):<br>1 :Anime - Sub<br>2 :Anime - Raw<br>3 :Anime - Dub<br>4 :LA - Sub<br>5 :LA - Raw<br>6 :Light Novel<br>7 :Manga - TLed<br>8 :Manga - Raw<br>9 :♫ - Lossy<br>10 :♫ - Lossless<br>11 :♫ - Video<br>12 :Games<br>13 :Applications<br>14 :Pictures<br>15 :Adult Video<br>16 :Other"
|
||||
|
||||
- name: lang-id
|
||||
type: text
|
||||
label: Language Id
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "?{{if .Config.cat-id}}id={{.Config.cat-id }}&{{else}}{{end}}q={{if .Keywords}}{{.Keywords}}{{else}}{{end}}"
|
||||
- path: "?{{if .Config.cat-id}}id={{.Config.cat-id }}&{{else}}{{end}}{{if .Config.lang-id}}lang_id={{.Config.lang-id}}&{{else}}{{end}}q={{if .Keywords}}{{.Keywords}}{{else}}{{end}}"
|
||||
rows:
|
||||
selector: div.table-responsive > table > tbody > tr
|
||||
fields:
|
||||
|
@@ -40,6 +40,18 @@
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: info
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the 'Torrentliste' setting to "Platzsparendes Layout mit PopUp für zusätzliche Informationen" in your profile.
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
@@ -63,9 +75,13 @@
|
||||
|
||||
rows:
|
||||
selector: table.tableinborder > tbody > tr:has(a[href^="details.php"])
|
||||
fields:
|
||||
fields: # note: two alternative layouts available
|
||||
title:
|
||||
selector: a[href^="details.php"]
|
||||
title:
|
||||
optional: true
|
||||
selector: a[href^="details.php"][title]
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
@@ -79,29 +95,31 @@
|
||||
selector: a[href^=" /gettorrent/ssl/"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(2)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(2), a[href*="&filelist=1"]
|
||||
grabs:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(3) > b:nth-child(1)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(3) > b:nth-child(1), a[href*="&tosnatchers=1"]
|
||||
size:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(1)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(1), td:nth-child(7):has(br)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
seeders:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(1)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(1), a[href*="&toseeders=1"]
|
||||
leechers:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(3)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(3), a[href*="&todlers=1"]
|
||||
date:
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(5)
|
||||
selector: td:nth-child(2) > table > tbody > tr:nth-child(2) > td:nth-child(5), td:nth-child(5):has(br)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [" ", ""]
|
||||
- name: append
|
||||
args: " +2:00"
|
||||
- name: replace
|
||||
args: ["\xA0", " "]
|
||||
args: ["\xA0", ""]
|
||||
- name: dateparse
|
||||
args: "02.01.2006 15:04:05 -07:00"
|
||||
args: "02.01.200615:04:05 -07:00"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="/pic/free.gif"]: "0"
|
||||
|
@@ -72,7 +72,7 @@
|
||||
rows:
|
||||
selector: tr.browse_color, tr.freeleech_color, tr[id^="kdescr"]
|
||||
after: 1
|
||||
fields:
|
||||
fields: # some users (rank specific?) have an extra column (td:nth-child(4)) with bookmark features
|
||||
banner:
|
||||
selector: a[href^="details.php?id="][onmouseover]
|
||||
attribute: onmouseover
|
||||
@@ -98,20 +98,20 @@
|
||||
selector: a[href^="download.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(4)
|
||||
selector: a[href^="filelist.php"]
|
||||
size:
|
||||
selector: td:nth-child(7)
|
||||
selector: td:nth-last-child(6)
|
||||
grabs:
|
||||
selector: td:nth-child(8)
|
||||
selector: td:nth-last-child(5)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: ([\d,]+)
|
||||
seeders:
|
||||
selector: td:nth-child(9)
|
||||
selector: td:nth-last-child(4)
|
||||
leechers:
|
||||
selector: td:nth-child(10)
|
||||
selector: td:nth-last-child(3)
|
||||
date:
|
||||
selector: td:nth-child(6)
|
||||
selector: td:nth-last-child(7)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"a.info > b:contains(\"[FREE]\")": "0"
|
||||
|
@@ -44,15 +44,8 @@
|
||||
sorting: created_at
|
||||
direction: desc
|
||||
qty: 100
|
||||
preprocessingfilters:
|
||||
- name: jsonjoinarray
|
||||
args: ["$.result", ""]
|
||||
- name: prepend
|
||||
args: "<table>"
|
||||
- name: append
|
||||
args: "</table>"
|
||||
rows:
|
||||
selector: tr
|
||||
selector: table > tbody > tr
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="/categories/"]
|
||||
@@ -63,37 +56,24 @@
|
||||
title:
|
||||
selector: a.view-torrent
|
||||
download:
|
||||
selector: a[href*="/download_check/"]
|
||||
selector: a[href*="/download/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["/download_check/", "/download/"]
|
||||
details:
|
||||
selector: a.view-torrent
|
||||
attribute: href
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="://www.imdb.com/title/"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
selector: td:nth-child(4)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
leechers:
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: ([\d\.]+)
|
||||
date:
|
||||
selector: time
|
||||
attribute: datetime
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00"
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04:05 -07:00"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"i[data-original-title=\"100% Free\"]": "0"
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "list/{{if .Keywords}}{{.Keywords}}{{else}}movie{{end}}.html"
|
||||
- path: "list/{{if .Keywords}}{{.Keywords}}{{else}}movie{{end}}/1-1-0.html"
|
||||
rows:
|
||||
selector: .rs
|
||||
fields:
|
||||
|
@@ -1,19 +1,22 @@
|
||||
---
|
||||
site: tehconnection
|
||||
name: TehConnection
|
||||
description: "Movies Tracker"
|
||||
site: btxpress
|
||||
name: BTXpress
|
||||
description: "HD Tracker Movies/TV/Music"
|
||||
language: en-us
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://tehconnection.me/
|
||||
|
||||
- https://btxpress.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: Movies, desc: "Movies"}
|
||||
- {id: 2, cat: TV, desc: "TV"}
|
||||
- {id: 3, cat: Audio, desc: "Music"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
login:
|
||||
@@ -33,6 +36,7 @@
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}categories[]={{.}}&{{end}}"
|
||||
search: "{{if .Query.IMDBID}}{{else}}{{ .Keywords }}{{end}}"
|
||||
description: ""
|
||||
uploader: ""
|
||||
imdb: "{{ .Query.IMDBIDShort }}"
|
||||
tvdb: ""
|
||||
@@ -68,10 +72,6 @@
|
||||
details:
|
||||
selector: a.view-torrent
|
||||
attribute: href
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="://www.imdb.com/title/"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
seeders:
|
@@ -45,10 +45,7 @@
|
||||
- selector: table:contains("Login failed!")
|
||||
test:
|
||||
path: index.php
|
||||
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
|
@@ -6,8 +6,12 @@
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://cpasbiens1.com/
|
||||
- http://www.cpabien.io/
|
||||
legacylinks:
|
||||
- http://www.cpasbiens.cc/
|
||||
- http://www.cpabien.cm/
|
||||
- http://cpabien.cm/
|
||||
- http://cpasbiens1.com/
|
||||
- http://cpabien.mx/
|
||||
- https://www.cpabien.bz/
|
||||
- http://www.cpabien.bz/
|
||||
@@ -30,13 +34,13 @@
|
||||
settings: []
|
||||
|
||||
download:
|
||||
selector: "#telecharger a"
|
||||
|
||||
selector: div.btn-download a
|
||||
attribute: href
|
||||
search:
|
||||
paths:
|
||||
- path: "/search.php?t={{ .Keywords }}"
|
||||
- path: "{{ if .Keywords }}recherche/{{ .Keywords }}{{else}}derniers/{{end}}"
|
||||
rows:
|
||||
selector: div[class^='ligne']
|
||||
selector: table.table-corps tbody tr td
|
||||
fields:
|
||||
site_date:
|
||||
selector: a
|
||||
|
@@ -38,16 +38,15 @@
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
type: text
|
||||
label: Cookie
|
||||
|
||||
login:
|
||||
path: /login-page
|
||||
method: form
|
||||
form: form[action^="/login"]
|
||||
method: cookie
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
persistent_login: "1"
|
||||
error:
|
||||
- selector: div.error
|
||||
cookie: "{{ .Config.cookie }}"
|
||||
test:
|
||||
path: /torrents
|
||||
|
||||
|
@@ -128,7 +128,18 @@
|
||||
"2" : "Argent (Silver)"
|
||||
"3" : "Or (Gold)"
|
||||
"4" : "Argent & Or (Both)"
|
||||
|
||||
- name: multilang
|
||||
type: checkbox
|
||||
label: Replace MULTI by another language in release name
|
||||
default: false
|
||||
- name: multilanguage
|
||||
type: select
|
||||
label: Replace MULTI by this language
|
||||
default: FRENCH
|
||||
options:
|
||||
FRENCH : "FRENCH"
|
||||
MULTI.FRENCH : "MULTI.FRENCH"
|
||||
ENGLISH: "ENGLISH"
|
||||
login:
|
||||
path: tracker/index.php?page=login
|
||||
method: post
|
||||
@@ -168,8 +179,15 @@
|
||||
rows:
|
||||
selector: table > tbody > tr > td > table.lista > tbody > tr:has(td[onmouseover="this.className='post'"])
|
||||
fields:
|
||||
title:
|
||||
title_phase1:
|
||||
selector: a[onmouseover][href^="index.php?page=torrent-details&id="]
|
||||
title_multilang:
|
||||
text: "{{ .Result.title_phase1 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["[\\.\\s\\[\\-][Mm][Uu][Ll][Tt][Ii][\\.\\s\\]\\-]", ".{{ .Config.multilanguage }}."]
|
||||
title:
|
||||
text: "{{if .Config.multilang }}{{ .Result.title_multilang }}{{else}}{{ .Result.title_phase1 }}{{end}}"
|
||||
details:
|
||||
selector: a[onmouseover][href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
@@ -189,23 +207,23 @@
|
||||
- name: querystring
|
||||
args: "category"
|
||||
date:
|
||||
selector: td:nth-of-type(4)
|
||||
selector: td:nth-of-type(5)
|
||||
filters:
|
||||
- name: append
|
||||
args: " -04:00"
|
||||
- name: dateparse
|
||||
args: "02/01/2006 15:04:05 -07:00"
|
||||
seeders:
|
||||
selector: td:nth-of-type(6)
|
||||
leechers:
|
||||
selector: td:nth-of-type(7)
|
||||
grabs:
|
||||
leechers:
|
||||
selector: td:nth-of-type(8)
|
||||
grabs:
|
||||
selector: td:nth-of-type(9)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
size:
|
||||
selector: td:nth-of-type(10)
|
||||
selector: td:nth-of-type(11)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="images/freeleech.gif"]: "0"
|
||||
|
@@ -115,6 +115,8 @@
|
||||
|
||||
rows:
|
||||
selector: table > tbody > tr:has(img[src*="/pic/categories/"])
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
title:
|
||||
# using attribute title from td(3) because the text from td(2) a(2) can be abbreviated
|
||||
|
@@ -52,16 +52,25 @@
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings: []
|
||||
settings:
|
||||
- name: downloadlink
|
||||
type: select
|
||||
label: Download link
|
||||
default: "magnet:"
|
||||
options:
|
||||
"https://etorrent.click/" : "eTorrent.click"
|
||||
"magnet:": "magnet"
|
||||
|
||||
download:
|
||||
selector: a[href^="magnet:"]
|
||||
selector: a[href^="{{ .Config.downloadlink }}"]
|
||||
|
||||
search:
|
||||
path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
sort: "id"
|
||||
order: "desc"
|
||||
incldead: "1"
|
||||
keywordsfilters:
|
||||
- name: replace
|
||||
@@ -103,4 +112,4 @@
|
||||
downloadvolumefactor:
|
||||
text: "0"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
text: "1"
|
||||
|
@@ -87,6 +87,7 @@
|
||||
- selector: div.myFrame:has(font.error)
|
||||
test:
|
||||
path: torrents-search.php
|
||||
selector: a.logout
|
||||
|
||||
search:
|
||||
paths:
|
||||
|
@@ -47,11 +47,33 @@
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}buscar/descargas/{{ .Config.category }}/{{ .Keywords }}?search=Buscar{{else}}descargas{{end}}"
|
||||
- path: "{{ if .Keywords }}buscar/descargas/{{ .Config.category }}/{{ .Keywords }}?search=Buscar&page=2{{else}}descargas{{end}}"
|
||||
keywordsfilters:
|
||||
- name: re_replace #remove S/EXX from search string
|
||||
args: ["([SE]\\d{1,2})", ""]
|
||||
rows:
|
||||
selector: table#descargas > tbody > tr:has(td:has(a[href^="magnet:?"]))
|
||||
fields:
|
||||
title:
|
||||
selector: td.tit a
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["\\/", " "]
|
||||
- name: re_replace
|
||||
args: ["\\(", ""]
|
||||
- name: re_replace
|
||||
args: ["\\)", ""]
|
||||
- name: re_replace
|
||||
args: ["([A-z]*) temporada", "S$1"]
|
||||
- name: re_replace
|
||||
args: ["S[pP]rimera", "S1"]
|
||||
- name: re_replace
|
||||
args: ["S[sS]egunda", "S2"]
|
||||
- name: re_replace
|
||||
args: ["S[tT]ercera", "S3"]
|
||||
- name: re_replace
|
||||
args: ["S([0-9]+) - Episodio ([0-9]+)", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["- Episodio ([0-9]*)", "E$1"]
|
||||
details:
|
||||
selector: td.tit a
|
||||
attribute: href
|
||||
|
225
src/Jackett.Common/Definitions/girotorrent.yml
Normal file
225
src/Jackett.Common/Definitions/girotorrent.yml
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
site: girotorrent
|
||||
name: Girotorrent
|
||||
description: "Girotorrent is an ITALIAN Private site for TV / MOVIES / GENERAL"
|
||||
language: it-it
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://girotorrent.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
# LIBREDICOLA
|
||||
- {id: 13, cat: Books, desc: "Giornali e Riviste"}
|
||||
- {id: 15, cat: Books, desc: "Ebook"}
|
||||
- {id: 16, cat: Books, desc: "Fumetti"}
|
||||
- {id: 70, cat: Books, desc: "Manuali e Guide"}
|
||||
- {id: 72, cat: Audio/Audiobook, desc: "Audiolibri"}
|
||||
# CINEMA
|
||||
- {id: 17, cat: Movies/Other, desc: "Movie Cam-Ts"}
|
||||
- {id: 18, cat: Movies/Other, desc: "Movie Screener"}
|
||||
- {id: 61, cat: Movies/Other, desc: "Movie R5-R6"}
|
||||
- {id: 19, cat: Movies/Other, desc: "Movie DVDRip"}
|
||||
- {id: 20, cat: Movies/Other, desc: "Movie BDRip"}
|
||||
- {id: 60, cat: Movies/Other, desc: "Movie BluRay"}
|
||||
- {id: 63, cat: Movies/Other, desc: "Movie WEBDLRip"}
|
||||
# VIDEOTECA
|
||||
- {id: 22, cat: Movies/SD, desc: "Movie BDRip"}
|
||||
- {id: 23, cat: Movies/SD, desc: "Movie DvdRip"}
|
||||
- {id: 23, cat: Movies/SD, desc: "Movie WEBRip"}
|
||||
- {id: 24, cat: Movies/DVD, desc: "Movie DVD-R 5"}
|
||||
- {id: 25, cat: Movies/DVD, desc: "Movie DVD-R 9"}
|
||||
- {id: 26, cat: Movies/HD, desc: "Movie Blu-Ray HD"}
|
||||
- {id: 27, cat: Movies/3D, desc: "Movie 3D-SBS"}
|
||||
- {id: 96, cat: Movies/HD, desc: "Movie x265 HEVC"}
|
||||
- {id: 28, cat: Movies/Foreign, desc: "Movie Subbet-ita"}
|
||||
- {id: 73, cat: Movies/SD, desc: "Movie MP4"}
|
||||
- {id: 29, cat: Movies/Foreign, desc: "Movie Ligua Originale"}
|
||||
# ANIMAZIONE
|
||||
- {id: 32, cat: TV/Anime, desc: "Anime Disney"}
|
||||
- {id: 33, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 34, cat: TV/Anime, desc: "Anime Altri Cartoni"}
|
||||
# TELEVISIONE
|
||||
- {id: 36, cat: TV, desc: "TV Serie TV"}
|
||||
- {id: 77, cat: TV, desc: "TV Reality"}
|
||||
- {id: 37, cat: TV, desc: "TV Film TV"}
|
||||
- {id: 59, cat: TV, desc: "TV Sport"}
|
||||
- {id: 38, cat: TV, desc: "TV Concerti-Spettacoli"}
|
||||
- {id: 39, cat: TV, desc: "TV Teatro-Cabaret"}
|
||||
- {id: 40, cat: TV/Documentary, desc: "Tv Documentario"}
|
||||
# MUSICA
|
||||
- {id: 42, cat: Audio, desc: "Musica CD Singoli"}
|
||||
- {id: 43, cat: Audio, desc: "Musica Italiana"}
|
||||
- {id: 44, cat: Audio, desc: "Musica Straniera"}
|
||||
- {id: 45, cat: Audio, desc: "Musica Compilation"}
|
||||
- {id: 46, cat: Audio, desc: "Musica Video Clip"}
|
||||
- {id: 58, cat: Audio, desc: "Musica Discografie"}
|
||||
# SALA GIOCHI
|
||||
- {id: 47, cat: PC/Games, desc: "PC Games"}
|
||||
- {id: 48, cat: PC/Games, desc: "PC Giochi PS2-PS3"}
|
||||
- {id: 49, cat: PC/Games, desc: "PC Giochi Nintendo Wii"}
|
||||
- {id: 50, cat: PC/Games, desc: "PC Giochi Xbox"}
|
||||
- {id: 52, cat: PC/Games, desc: "PC Giochi DS-DS3"}
|
||||
# SOFTWARE
|
||||
- {id: 54, cat: PC, desc: "PC Programmi Windows"}
|
||||
- {id: 55, cat: PC/Mac, desc: "PC Mac"}
|
||||
- {id: 69, cat: PC, desc: "PC Portable"}
|
||||
- {id: 56, cat: PC, desc: "PC Linux"}
|
||||
# CELLULARI-PALMARI
|
||||
- {id: 71, cat: PC/Phone-Android, desc: "Android APP"}
|
||||
- {id: 74, cat: Other, desc: "Varie"}
|
||||
- {id: 75, cat: Other, desc: "Immagini Wallpaper"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
login:
|
||||
path: /index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: div.error
|
||||
test:
|
||||
path: /index.php
|
||||
selector: a[href="logout.php"]
|
||||
|
||||
download:
|
||||
before:
|
||||
path: "thanks.php"
|
||||
method: "post"
|
||||
inputs:
|
||||
infohash: "{{ .DownloadUri.Query.id }}"
|
||||
thanks: "1"
|
||||
rndval: "1487013827343"
|
||||
selector: a[href^="download.php?id="]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /index.php
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["S[0-9]{2}([^E]|$)", ""] # remove season tag without episode (search doesn't support it)
|
||||
- name: diacritics
|
||||
args: replace
|
||||
# most ITA TV torrents are in XXxYY format, so we search without S/E prefixes and filter later
|
||||
- name: re_replace
|
||||
args: ["S0?(\\d{1,2})", " $1 "]
|
||||
- name: re_replace
|
||||
args: ["E(\\d{2,3})", " $1 "]
|
||||
inputs:
|
||||
search: "{{ .Keywords }}"
|
||||
category: "{{range .Categories}}{{.}};{{end}}"
|
||||
page: "torrents"
|
||||
active: 0
|
||||
rows:
|
||||
selector: div.b-content > table > tbody > tr > td > table.lista > tbody > tr:has(a[href^="index.php?page=torrent-details&id="])
|
||||
#http://girotorrent.org/index.php?page=torrent-details&id=73d93dccf84ea3a8b614a3113acfd9eea186d730
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="index.php?page=downloadcheck&id="]
|
||||
attribute: href
|
||||
title: # shortened title?
|
||||
selector: a[onmouseover][href^="index.php?page=torrent-details&id="]
|
||||
# normalize to SXXEYY format
|
||||
filters:
|
||||
- name: re_replace # replace special characters with " " (space)
|
||||
args: ["[^a-zA-Z0-9]|\\.", " "]
|
||||
# normalize to SXXEYY format
|
||||
- name: re_replace
|
||||
args: ["(\\d{2})x(\\d{2})", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{1})x(\\d{2})", "S0$1E$2"]
|
||||
- name: re_replace #Stagione X --> S0X
|
||||
args: ["Stagione (\\d{0,1}\\s)", "S0$1"]
|
||||
- name: re_replace #Stagione XX --> SXX
|
||||
args: ["Stagione (\\d{2}\\s)", "S$1"]
|
||||
- name: re_replace #/ Episodio [YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sEpisodio|\\s\\/\\sEpisodi|\\sEpisodio|\\s\\|\\sEpisodio|\\sEpisodi)\\s\\[", "E"]
|
||||
- name: re_replace #/ Completa [episodi YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sCompleta\\s\\[episodi\\s)", "E"]
|
||||
- name: re_replace #remove di YY] | remove /YY]
|
||||
args: ["(\\sdi\\s\\d{1,2}|\\/\\d{1,2})\\]", " "]
|
||||
- name: re_replace #remove various
|
||||
args: ["(Serie completa|Completa|\\[in pausa\\])", ""]
|
||||
# fine prova
|
||||
title: # long titles?
|
||||
optional: true
|
||||
selector: a[title][href^="index.php?page=torrent-details"]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Vedi Dettagli: ", ""]
|
||||
# inizio prova
|
||||
- name: re_replace # replace special characters with " " (space)
|
||||
args: ["[^a-zA-Z0-9]|\\.", " "]
|
||||
# normalize to SXXEYY format
|
||||
- name: re_replace
|
||||
args: ["(\\d{2})x(\\d{2})", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{1})x(\\d{2})", "S0$1E$2"]
|
||||
- name: re_replace #Stagione X --> S0X
|
||||
args: ["Stagione (\\d{0,1}\\s)", "S0$1"]
|
||||
- name: re_replace #Stagione XX --> SXX
|
||||
args: ["Stagione (\\d{2}\\s)", "S$1"]
|
||||
- name: re_replace #/ Episodio [YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sEpisodio|\\s\\/\\sEpisodi|\\sEpisodio|\\s\\|\\sEpisodio|\\sEpisodi)\\s\\[", "E"]
|
||||
- name: re_replace #/ Completa [episodi YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sCompleta\\s\\[episodi\\s)", "E"]
|
||||
- name: re_replace #remove di YY] | remove /YY]
|
||||
args: ["(\\sdi\\s\\d{1,2}|\\/\\d{1,2})\\]", " "]
|
||||
- name: re_replace #remove various
|
||||
args: ["(Serie completa|Completa|\\[in pausa\\])", ""]
|
||||
# fine prova
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
details:
|
||||
selector: a[onmouseover][href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
banner:
|
||||
optional: true
|
||||
selector: a[onmouseover][href^="index.php?page=torrent-details&id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
size:
|
||||
selector: td:nth-child(11)
|
||||
date:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02/01/2006"
|
||||
grabs:
|
||||
selector: td:nth-child(9)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[alt="Free Leech"]: "0"
|
||||
img[alt="Gold 100% Free"]: "0"
|
||||
img[alt="Silver 50% Free"]: "0.5"
|
||||
img[alt="Bronze 25% Free"]: "0.75"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
uploadvolumefactor:
|
||||
optional: true
|
||||
selector: img[alt$="x Upload Multiplier"]
|
||||
attribute: alt
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["x Upload Multiplier", ""]
|
@@ -6,8 +6,9 @@
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.gktorrent.net/
|
||||
- https://www.gktorrent.org/
|
||||
legacylinks:
|
||||
- http://www.gktorrent.net/
|
||||
- https://www.gktorrent.com/ # they're forcing http
|
||||
- http://www.gktorrent.com/
|
||||
|
||||
|
@@ -33,9 +33,6 @@
|
||||
- selector: table.main:contains("Login Failed!")
|
||||
test:
|
||||
path: my.php
|
||||
|
||||
download:
|
||||
selector: a[href^="/download.php"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
|
@@ -61,7 +61,6 @@
|
||||
search:
|
||||
paths:
|
||||
- path: /torrents.php
|
||||
method: post
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}cat{{.}}=1&{{end}}"
|
||||
search: "{{if .Query.IMDBID}}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
|
@@ -82,7 +82,6 @@
|
||||
search:
|
||||
paths:
|
||||
- path: /torrents.php
|
||||
method: post
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}cat{{.}}=1&{{end}}"
|
||||
search: "{{if .Query.IMDBID}}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
|
@@ -91,6 +91,11 @@
|
||||
download:
|
||||
selector: td:nth-child(11) > a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Config.sitelink }}"
|
||||
- name: replace # https download links are redirected to http causing invalid cookies => invalid passkeys
|
||||
args: ["https", "http"]
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
remove: br
|
||||
@@ -103,3 +108,9 @@
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"font:contains(\"(FreeLeech)\")": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
|
142
src/Jackett.Common/Definitions/hdspain.yml
Normal file
142
src/Jackett.Common/Definitions/hdspain.yml
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
site: hdspain
|
||||
name: HD-Spain
|
||||
description: "HD-Spain is a SPANISH site for HD content"
|
||||
language: es-es
|
||||
type: private
|
||||
encoding: ISO-8859-1
|
||||
links:
|
||||
- https://www.hd-spain.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1 , cat: Movies/HD, desc: "Películas"}
|
||||
- {id: 5 , cat: Movies/HD, desc: "Pelíc. Anim."}
|
||||
- {id: 4 , cat: TV/HD, desc: "Series"}
|
||||
- {id: 3 , cat: TV/HD, desc: "Series Anim."}
|
||||
- {id: 6 , cat: TV/Documentary, desc: "Documentales"}
|
||||
- {id: 11, cat: TV/Sport, desc: "Deportes"}
|
||||
- {id: 7 , cat: Audio/Video, desc: "Música/Espec."}
|
||||
- {id: 9 , cat: TV/OTHER, desc: "Programas TV"}
|
||||
- {id: 8 , cat: Audio/Lossless, desc: "Audios"}
|
||||
- {id: 10, cat: XXX/x264, desc: "XXX"}
|
||||
modes:
|
||||
search: [q]
|
||||
|
||||
login:
|
||||
path: index.php
|
||||
method: form
|
||||
inputs:
|
||||
usuario: "{{ .Config.username }}"
|
||||
contrasena: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: p.error
|
||||
test:
|
||||
path: index.php
|
||||
selector: .tcabecera
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["S0?(\\d{1,2})E(\\d{1,2})", "$1x$2"]
|
||||
inputs:
|
||||
sec: listado
|
||||
ord: 9
|
||||
b: "{{ .Keywords }}"
|
||||
ver: "0"
|
||||
relanz: "0"
|
||||
$raw: "{{range .Categories}}&cat[]={{.}}{{end}}"
|
||||
rows:
|
||||
selector: "table.listatorrents tr:not(:first-child)"
|
||||
fields:
|
||||
category:
|
||||
selector: td.categorias a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title:
|
||||
selector: td.titulo a[id]
|
||||
filters:
|
||||
- name: append
|
||||
args: " [spanish]"
|
||||
details:
|
||||
selector: td.titulo a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td.tamano
|
||||
seeders:
|
||||
selector: td.usuarios.seeds a
|
||||
leechers:
|
||||
selector: td.usuarios.leechers a
|
||||
grabs:
|
||||
selector: td.usuarios.completados
|
||||
date:
|
||||
optional: true
|
||||
selector: td.fecha
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Lunes", "Monday"]
|
||||
- name: replace
|
||||
args: ["Martes", "Tuesday"]
|
||||
- name: re_replace
|
||||
args: ["Miércoles", "Wednesday"]
|
||||
- name: replace
|
||||
args: ["Jueves", "Thursday"]
|
||||
- name: replace
|
||||
args: ["Viernes", "Friday"]
|
||||
- name: re_replace
|
||||
args: ["Sábado", "Saturday"]
|
||||
- name: replace
|
||||
args: ["Domingo", "Sunday"]
|
||||
- name: replace
|
||||
args: ["Enero", "January"]
|
||||
- name: replace
|
||||
args: ["Febrero", "February"]
|
||||
- name: replace
|
||||
args: ["Marzo", "March"]
|
||||
- name: replace
|
||||
args: ["Abril", "April"]
|
||||
- name: replace
|
||||
args: ["Mayo", "May"]
|
||||
- name: replace
|
||||
args: ["Junio", "June"]
|
||||
- name: replace
|
||||
args: ["Julio", "July"]
|
||||
- name: replace
|
||||
args: ["Agosto", "August"]
|
||||
- name: replace
|
||||
args: ["Septiembre", "September"]
|
||||
- name: replace
|
||||
args: ["Octubre", "October"]
|
||||
- name: replace
|
||||
args: ["Noviembre", "November"]
|
||||
- name: replace
|
||||
args: ["Diciembre", "December"]
|
||||
- name: dateparse
|
||||
args: "Monday 2 January 2006, 15:04"
|
||||
download:
|
||||
selector: td.descargar a
|
||||
attribute: href
|
||||
|
||||
downloadvolumefactor:
|
||||
text: "1"
|
||||
downloadvolumefactor:
|
||||
optional: true
|
||||
selector: td.descargar a b strong
|
||||
filters:
|
||||
- name: replace
|
||||
args: [" X2", ""]
|
||||
- name: replace
|
||||
args: ["Freeleech", "0"]
|
||||
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
uploadvolumefactor:
|
||||
optional: true
|
||||
selector: td.descargar a b strong
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Freeleech X2", "2"]
|
@@ -114,7 +114,7 @@
|
||||
selector: td:has(a[href$="filelist=1#filelist"])
|
||||
remove: a
|
||||
date:
|
||||
selector: td > font:contains("Added on")
|
||||
selector: td > font:has(i.fa-clock-o)
|
||||
remove: b
|
||||
filters:
|
||||
- name: replace
|
||||
|
160
src/Jackett.Common/Definitions/ilcorsaroblu.yml
Normal file
160
src/Jackett.Common/Definitions/ilcorsaroblu.yml
Normal file
@@ -0,0 +1,160 @@
|
||||
---
|
||||
site: ilcorsaroblu
|
||||
name: Il Corsaro Blu
|
||||
description: "Il Corsaro Blu is an ITALIAN Public site for TV / MOVIES / GENERAL"
|
||||
language: it-it
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.ilcorsaroblu.info/
|
||||
legacylinks:
|
||||
- http://ilcorsaroblu.org/
|
||||
- https://www.ilcorsaroblu.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
# Adult
|
||||
- {id: 12, cat: XXX, desc: "Adult"}
|
||||
# Applicazioni
|
||||
- {id: 5, cat: PC/Phone-Android, desc: "Android"}
|
||||
# Books
|
||||
- {id: 6, cat: Books, desc: "Books"}
|
||||
# Games
|
||||
- {id: 3, cat: Other, desc: "Games"}
|
||||
# Music
|
||||
- {id: 2, cat: Audio, desc: "Music"}
|
||||
# Movies
|
||||
- {id: 17, cat: Movies/SD, desc: "Movie BDRip"}
|
||||
- {id: 21, cat: Movies/Other, desc: "Movies - Films"}
|
||||
- {id: 11, cat: Movies/DVD, desc: "DVD-R"}
|
||||
- {id: 14, cat: Movies/HD, desc: "Movie 720p"}
|
||||
- {id: 13, cat: Movies/HD, desc: "Movie 1080p"}
|
||||
- {id: 15, cat: Movies/3D, desc: "Movie 3D"}
|
||||
- {id: 24, cat: TV/OTHER, desc: "TV Show Standard"}
|
||||
- {id: 19, cat: TV/HD, desc: "Tv Show 1080p"}
|
||||
- {id: 20, cat: TV/HD, desc: "Tv Show 720"}
|
||||
# Various
|
||||
- {id: 4, cat: Other, desc: "Other"}
|
||||
- {id: 7, cat: PC, desc: "Windows"}
|
||||
- {id: 8, cat: Other, desc: "Linux"}
|
||||
- {id: 9, cat: PC/Mac, desc: "Mac"}
|
||||
- {id: 23, cat: Other, desc: "Archive"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings: []
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /index.php
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["S[0-9]{2}([^E]|$)", ""] # remove season tag without episode (search doesn't support it)
|
||||
- name: diacritics
|
||||
args: replace
|
||||
# most ITA TV torrents are in XXxYY format, so we search without S/E prefixes and filter later
|
||||
- name: re_replace
|
||||
args: ["S0?(\\d{1,2})", " $1 "]
|
||||
- name: re_replace
|
||||
args: ["E(\\d{2,3})", " $1 "]
|
||||
inputs:
|
||||
search: "{{ .Keywords }}"
|
||||
category: "{{range .Categories}}{{.}};{{end}}"
|
||||
page: torrents
|
||||
active: 0
|
||||
rows:
|
||||
selector: div.b-content > table > tbody > tr > td > table.lista > tbody > tr:has(a[href^="index.php?page=torrents&category="])
|
||||
fields:
|
||||
title:
|
||||
selector: td:nth-child(2) > a
|
||||
# normalize to SXXEYY format
|
||||
filters:
|
||||
- name: re_replace # replace special characters with " " (space)
|
||||
args: ["[^a-zA-Z0-9]|\\.", " "]
|
||||
# normalize to SXXEYY format
|
||||
- name: re_replace
|
||||
args: ["(\\d{2})x(\\d{2})", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{1})x(\\d{2})", "S0$1E$2"]
|
||||
- name: re_replace #Stagione X --> S0X
|
||||
args: ["Stagione (\\d{0,1}\\s)", "S0$1"]
|
||||
- name: re_replace #Stagione XX --> SXX
|
||||
args: ["Stagione (\\d{2}\\s)", "S$1"]
|
||||
- name: re_replace #/ Episodio [YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sEpisodio|\\s\\/\\sEpisodi|\\sEpisodio|\\s\\|\\sEpisodio|\\sEpisodi)\\s\\[", "E"]
|
||||
- name: re_replace #/ Completa [episodi YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sCompleta\\s\\[episodi\\s)", "E"]
|
||||
- name: re_replace #remove di YY] | remove /YY]
|
||||
args: ["(\\sdi\\s\\d{1,2}|\\/\\d{1,2})\\]", " "]
|
||||
- name: re_replace #remove various
|
||||
args: ["(Serie completa|Completa|\\[in pausa\\])", ""]
|
||||
# fine prova
|
||||
download: # handle torrents with normal torrent file download
|
||||
optional: true
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: id
|
||||
- name: toupper
|
||||
- name: prepend
|
||||
args: http://itorrents.org/torrent/
|
||||
- name: append
|
||||
args: ".torrent"
|
||||
_magnetfilename: # convert title to valid magnet filename
|
||||
text: "{{ .Result.title }}"
|
||||
filters:
|
||||
- name: validfilename
|
||||
- name: urlencode
|
||||
magnet: # generate magnet link from download link
|
||||
optional: true
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: id
|
||||
- name: prepend
|
||||
args: "magnet:?xt=urn:btih:"
|
||||
- name: append
|
||||
args: "&dn={{ .Result._magnetfilename }}.torrent"
|
||||
- name: append # add some well known public trackers
|
||||
args: "&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://tracker.opentrackr.org:1337/announce"
|
||||
magnet: # in case a direct magnet link is provided use it
|
||||
optional: true
|
||||
selector: a[href^="magnet:?xt="]
|
||||
attribute: href
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
details:
|
||||
selector: td:nth-child(2) a
|
||||
attribute: href
|
||||
banner:
|
||||
optional: true
|
||||
selector: td:nth-child(2) > a
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
size:
|
||||
selector: td:nth-child(9)
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02/01/2006"
|
||||
grabs:
|
||||
selector: td:nth-child(8)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: td:nth-child(6)
|
||||
leechers:
|
||||
selector: td:nth-child(7)
|
@@ -9,6 +9,7 @@
|
||||
- https://ilcorsaronero.info/
|
||||
certificates:
|
||||
- aa7c40aa360a1cec8a9687312fd50402b912e618 # incomplete CA chain
|
||||
- 83174ec1f92fa13cdef9d51888ea1dfba2166e17 # incomplete CA chain
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -77,6 +77,9 @@
|
||||
attribute: href
|
||||
size:
|
||||
selector: td.size-row
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(\\d+).(?=\\d{3}(\\D|$))", "$1"]
|
||||
seeders:
|
||||
selector: td.sn
|
||||
date:
|
||||
@@ -96,4 +99,4 @@
|
||||
downloadvolumefactor:
|
||||
text: "0"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
text: "1"
|
||||
|
@@ -1,3 +1,5 @@
|
||||
# looks like gazelle but ajax.php seems to be disabled:
|
||||
# https://jpopsuki.eu/ajax.php?action=browse&order_by=time&order_way=desc => Invalid
|
||||
---
|
||||
site: jpopsuki
|
||||
name: JPopsuki
|
||||
@@ -34,7 +36,7 @@
|
||||
keeplogged: 1
|
||||
login: "Log in"
|
||||
error:
|
||||
- selector: form#loginform > span.warning
|
||||
- selector: form#loginform > span.warning, font[color="red"]
|
||||
test:
|
||||
path: torrents.php
|
||||
|
||||
|
@@ -38,6 +38,7 @@
|
||||
selector: table[class="data"] tr[id]
|
||||
fields:
|
||||
category:
|
||||
optional: true
|
||||
selector: span[id^="cat_"] > strong > a
|
||||
attribute: href
|
||||
filters:
|
||||
@@ -60,6 +61,8 @@
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["N/A", "0 Bytes"]
|
||||
- name: re_replace # replace all but last dot (They use dots as decimal and thousands separators)
|
||||
args: ["[.](?=.*[.])", ""]
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
seeders:
|
||||
|
@@ -258,6 +258,8 @@
|
||||
args: ["^Extinct", "Extinct 2017"]
|
||||
- name: re_replace
|
||||
args: ["^The Flash", "The Flash 2014"]
|
||||
- name: replace
|
||||
args: ["Nanatsu no Taizai - Imashime no Fukkatsu - E", "Nanatsu no Taizai - Imashime no Fukkatsu - S02E"]
|
||||
- name: re_replace
|
||||
args: ["^The Magicians", "The Magicians 2015"]
|
||||
# Workaround to remove the translated name from the series, didn't found an better and reliable way to do this, feel free to add more as needed.
|
||||
|
@@ -10,53 +10,20 @@
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
# TV
|
||||
- {id: 23, cat: TV, desc: "TV/HD"}
|
||||
- {id: 47, cat: TV, desc: "TV/PACKS"}
|
||||
- {id: 28, cat: TV, desc: "TV/eps"}
|
||||
- {id: 25, cat: TV, desc: "TV/HDRIP"}
|
||||
- {id: 24, cat: TV, desc: "TV/TV-packs"}
|
||||
- {id: 26, cat: TV/HD, desc: "TV/X264-HD"}
|
||||
- {id: 27, cat: TV/SD, desc: "TV/X264-SD"}
|
||||
# Movies
|
||||
- {id: 10, cat: Movies, desc: "Movies/0DAY"}
|
||||
- {id: 56, cat: Movies/3D, desc: "Movies/3D"}
|
||||
- {id: 16, cat: TV/Anime, desc: "Movies/ANIME"}
|
||||
- {id: 44, cat: Movies, desc: "Movies/CAM"}
|
||||
- {id: 18, cat: Movies/DVD, desc: "Movies/DVDR"}
|
||||
- {id: 49, cat: Movies/HD, desc: "Movies/hd 1080p"}
|
||||
- {id: 48, cat: Movies/HD, desc: "Movies/hd 720p"}
|
||||
- {id: 55, cat: Movies, desc: "Movies/Box Sets"}
|
||||
- {id: 53, cat: Movies, desc: "Movies/Sports"}
|
||||
- {id: 17, cat: Movies, desc: "Movies/X264"}
|
||||
- {id: 57, cat: Movies, desc: "Movies/xmas"}
|
||||
- {id: 15, cat: Movies/SD, desc: "Movies/XVID"}
|
||||
# Music
|
||||
- {id: 54, cat: Audio/Audiobook, desc: "Music/Audio Book"}
|
||||
- {id: 19, cat: Audio/Lossless, desc: "Music/FLAC"}
|
||||
- {id: 20, cat: Audio, desc: "Music/DVDR"}
|
||||
- {id: 21, cat: Audio/MP3, desc: "Music/MP3"}
|
||||
- {id: 42, cat: Audio, desc: "Music/0DAY"}
|
||||
- {id: 22, cat: Audio/Video, desc: "Music/VID"}
|
||||
# Apps
|
||||
- {id: 9, cat: PC/0day, desc: "Apps/APPS"}
|
||||
- {id: 11, cat: Books/Ebook, desc: "Apps/EBOOKS"}
|
||||
- {id: 46, cat: PC/Phone-Other, desc: "Apps/IPHONE/ANDROID"}
|
||||
- {id: 51, cat: PC, desc: "Apps/Linux"}
|
||||
- {id: 52, cat: PC/Mac, desc: "Apps/MAC"}
|
||||
# Games
|
||||
- {id: 58, cat: Console/Other, desc: "Games/Android"}
|
||||
- {id: 45, cat: PC/Games, desc: "Games/GAMES"}
|
||||
- {id: 59, cat: Console/NDS, desc: "Games/NDS"}
|
||||
- {id: 50, cat: Console, desc: "Games/PS2"}
|
||||
- {id: 14, cat: Console/PS3, desc: "Games/PS3"}
|
||||
- {id: 12, cat: Console/Wii, desc: "Games/WII"}
|
||||
- {id: 13, cat: Console/Xbox 360, desc: "Games/XBOX360"}
|
||||
# XXX
|
||||
- {id: 38, cat: XXX, desc: "XXX"}
|
||||
- {id: 39, cat: XXX, desc: "XXX/0DAY"}
|
||||
- {id: 40, cat: XXX/Imageset, desc: "XXX/IMAGESET"}
|
||||
- {id: 41, cat: XXX/Other, desc: "XXX/SITERIPS"}
|
||||
- {id: 9, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 1, cat: PC/0day, desc: "Apps"}
|
||||
- {id: 13, cat: PC/0day, desc: "Apps"}
|
||||
- {id: 5, cat: TV, desc: "Episodes"}
|
||||
- {id: 2, cat: PC/Games, desc: "Games"}
|
||||
- {id: 12, cat: PC/Games, desc: "Games/PC Rips"}
|
||||
- {id: 8, cat: Console/Other, desc: "Games/PS2"}
|
||||
- {id: 7, cat: Console/PSP, desc: "Games/PSP"}
|
||||
- {id: 3, cat: Movies, desc: "Movies"}
|
||||
- {id: 11, cat: Movies/SD, desc: "Movies/HDTV"}
|
||||
- {id: 10, cat: Movies/SD, desc: "Movies/XviD"}
|
||||
- {id: 4, cat: Audio, desc: "Music"}
|
||||
- {id: 14, cat: Audio, desc: "Music"}
|
||||
- {id: 6, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
@@ -101,13 +68,13 @@
|
||||
selector: table tr td.text
|
||||
test:
|
||||
path: browse.php
|
||||
selector: li a[href^="logout.php?hash_please="]
|
||||
selector: a[href*="logout.php?hash_please="]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
#$raw: "{{range .Categories}}c{{.}}=1&{{end}}" # this should work, untested
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
searchin: "title"
|
||||
incldead: "{{ .Config.incldead }}"
|
||||
@@ -115,6 +82,8 @@
|
||||
|
||||
rows:
|
||||
selector: tr.browse_color
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-of-type(1) a
|
||||
@@ -137,11 +106,7 @@
|
||||
selector: td:nth-of-type(3) a
|
||||
attribute: href
|
||||
files:
|
||||
optional: true
|
||||
selector: td:nth-of-type(5) a
|
||||
files:
|
||||
optional: true
|
||||
selector: td:nth-of-type(5):not(:has(a))
|
||||
selector: td:nth-of-type(5)
|
||||
date:
|
||||
selector: td:nth-of-type(7)
|
||||
filters:
|
||||
@@ -155,12 +120,13 @@
|
||||
- name: regexp
|
||||
args: "([\\d]+)"
|
||||
seeders:
|
||||
selector: td:nth-of-type(10) a font
|
||||
selector: td:nth-of-type(10)
|
||||
leechers:
|
||||
selector: td:nth-of-type(11)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"span:contains(\"Unlimited\")": "0"
|
||||
"img[title=\"Free Torrent\"]": "0" # torrent specific free leech (icon)?
|
||||
"a.info:contains(\"Free\")": "0" # global freeleech note?
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
|
@@ -38,7 +38,7 @@
|
||||
- path: /torrents/search.html
|
||||
method: post
|
||||
inputs:
|
||||
"SearchTorrentsForm[nameTorrent]": "{{ .Keywords }}"
|
||||
"SearchTorrentsForm[nameTorrent]": "{{if .Query.Artist}}{{ .Query.Artist }}{{else}}{{ .Keywords }}{{end}}"
|
||||
go-search: "Search"
|
||||
rows:
|
||||
selector: .smallalbum
|
||||
|
@@ -45,6 +45,17 @@
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: pin
|
||||
type: text
|
||||
label: Pin
|
||||
|
||||
login:
|
||||
path: /login.php
|
||||
method: form
|
||||
@@ -52,6 +63,7 @@
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
secure_pin: "{{ .Config.pin }}"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: table.tableinborder:contains("Anmeldung Gescheitert!") > tbody > tr > td.tablea
|
||||
|
@@ -6,8 +6,10 @@
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.nextorrent.site/
|
||||
- http://www.nextorrent.tv/
|
||||
legacylinks:
|
||||
- https://www.nextorrent.site/
|
||||
- http://www.nextorrent.site/
|
||||
- http://www.nextorrent.bz/
|
||||
- http://www.nextorrent.pro/
|
||||
- https://www.nextorrent.cc/
|
||||
@@ -16,11 +18,12 @@
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: Film, cat: Movies, desc: "Movies"}
|
||||
- {id: Films, cat: Movies, desc: "Movies"}
|
||||
- {id: Séries, cat: TV, desc: "TV"}
|
||||
- {id: Animes, cat: TV/Anime, desc: "TV/Anime"}
|
||||
- {id: Jeux, cat: Console, desc: "Games"}
|
||||
- {id: Ebooks, cat: Books, desc: "EBooks"}
|
||||
- {id: Jeux-PC, cat: PC/Games, desc: "Games PC"}
|
||||
- {id: Jeux-Consoles, cat: Console, desc: "Games Console"}
|
||||
- {id: Musique, cat: Audio, desc: "Music"}
|
||||
- {id: Ebook, cat: Books, desc: "EBooks"}
|
||||
- {id: Logiciels, cat: PC, desc: "Software"}
|
||||
|
||||
modes:
|
||||
@@ -31,24 +34,22 @@
|
||||
settings: []
|
||||
|
||||
download:
|
||||
selector: a[href^="/_get/NexTorrent.pro-"]
|
||||
selector: a[href^="/get_torrent/"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /
|
||||
inputs:
|
||||
s: "{{ .Keywords }}"
|
||||
- path: "recherche/{{ .Query.Keywords }}"
|
||||
rows:
|
||||
selector: article[id^="post-"]
|
||||
selector: div.listing-torrent > table tbody tr:has(a)
|
||||
fields:
|
||||
site_date:
|
||||
selector: h2 a
|
||||
selector: td:nth-child(1) a
|
||||
filters:
|
||||
# date is at the end of the title, so we get it and name it site_date
|
||||
- name: regexp
|
||||
args: "(\\w+)$"
|
||||
title:
|
||||
selector: h2 a
|
||||
selector: td:nth-child(1) a
|
||||
filters:
|
||||
# now we put the date at the right place according scene naming rules using .Result.site_date
|
||||
- name: replace
|
||||
@@ -61,32 +62,28 @@
|
||||
- name: re_replace
|
||||
args: ["(\\w+)$", ""]
|
||||
details:
|
||||
selector: h2 a
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
download:
|
||||
selector: h2 a
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
banner:
|
||||
selector: div.post_image
|
||||
attribute: src
|
||||
category:
|
||||
selector: p.meta span.post-category a
|
||||
|
||||
selector: td:nth-child(1) i
|
||||
attribute: class
|
||||
size:
|
||||
# there is no size on the primary search results page. defaulting to 500 MB
|
||||
text: "500 MB"
|
||||
selector: td:nth-child(2)
|
||||
date:
|
||||
selector: p.meta span.post-date a time.entry-date
|
||||
attribute: datetime
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "2006-01-02T15:04:05-07:00"
|
||||
text: now
|
||||
seeders:
|
||||
# there is no seeders on the primary search results page. defaulting to 0
|
||||
text: 0
|
||||
seeders:
|
||||
optional: true
|
||||
selector: td:nth-child(3)
|
||||
leechers:
|
||||
text: 0
|
||||
leechers:
|
||||
# there is no leechers on the primary search results page. defaulting to 0
|
||||
text: 0
|
||||
optional: true
|
||||
selector: td:nth-child(4)
|
||||
downloadvolumefactor:
|
||||
text: "0"
|
||||
uploadvolumefactor:
|
||||
|
@@ -44,6 +44,7 @@
|
||||
- selector: td.embedded:has(h2:contains("failed"))
|
||||
- selector: td.embedded:has(h2:contains("Error"))
|
||||
test:
|
||||
selector: a[href^="logout.php"]
|
||||
path: /browse.php
|
||||
|
||||
search:
|
||||
|
95
src/Jackett.Common/Definitions/racing4everyone.yml
Normal file
95
src/Jackett.Common/Definitions/racing4everyone.yml
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
site: racing4everyone
|
||||
name: Racing4Everyone (R4E)
|
||||
description: "Private Torrent Tracker for RACING"
|
||||
language: en-us
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://racing4everyone.eu/
|
||||
legacylinks:
|
||||
- https://racing4everyone.jp/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: TV/Sport, desc: "BTCC"}
|
||||
- {id: 3, cat: TV/Sport, desc: "DTM"}
|
||||
- {id: 4, cat: TV/Sport, desc: "Formula 1 2018"}
|
||||
- {id: 6, cat: TV/Sport, desc: "Formula 1 2017-1950 (HD)"}
|
||||
- {id: 21, cat: TV/Sport, desc: "Formula 1 2017-1950 (SD)"}
|
||||
- {id: 22, cat: TV/Sport, desc: "Formula 2"}
|
||||
- {id: 23, cat: TV/Sport, desc: "Formula E"}
|
||||
- {id: 24, cat: TV/Sport, desc: "Misc"}
|
||||
- {id: 25, cat: TV/Sport, desc: "Motorbikes"}
|
||||
- {id: 26, cat: TV/Sport, desc: "MotoGP/2/3"}
|
||||
- {id: 31, cat: TV/Sport, desc: "Nascar"}
|
||||
- {id: 32, cat: TV/Sport, desc: "Stockcars"}
|
||||
- {id: 33, cat: TV/Sport, desc: "Touring Cars"}
|
||||
- {id: 50, cat: TV/Sport, desc: "WRC"}
|
||||
- {id: 53, cat: TV/Sport, desc: "Open-Wheelers"}
|
||||
- {id: 54, cat: TV/Sport, desc: "Documentaries/Movies"}
|
||||
- {id: 55, cat: TV/Sport, desc: "Season Reviews"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
||||
login:
|
||||
path: /login
|
||||
method: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: table.main:contains("Login Failed!")
|
||||
test:
|
||||
path: /torrents
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /filterTorrents
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}categories[]={{.}}&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
uploader: ""
|
||||
sorting: created_at
|
||||
direction: desc
|
||||
qty: 100
|
||||
rows:
|
||||
selector: table > tbody > tr
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="/categories/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/categories/.*?\\.(\\d+)"
|
||||
title:
|
||||
selector: a.view-torrent
|
||||
download:
|
||||
selector: a[href*="/download/"]
|
||||
attribute: href
|
||||
details:
|
||||
selector: a.view-torrent
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(4)
|
||||
seeders:
|
||||
selector: td:nth-child(6)
|
||||
leechers:
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: ([\d\.]+)
|
||||
date:
|
||||
selector: time
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"i[data-original-title=\"100% Free\"]": "0"
|
||||
"i[data-original-title=\"Global FreeLeech\"]": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"i[data-original-title=\"Double upload\"]": "2"
|
||||
"*": "1"
|
152
src/Jackett.Common/Definitions/rgu.yml
Normal file
152
src/Jackett.Common/Definitions/rgu.yml
Normal file
@@ -0,0 +1,152 @@
|
||||
---
|
||||
site: RGU
|
||||
name: RGU
|
||||
description: "RGU is a Private site for MOVIES / TV / GENERAL"
|
||||
language: en-us
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://rgu.rgt.life/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 19, cat: Movies/BluRay, desc: "007 Movies"}
|
||||
- {id: 9, cat: TV/Anime, desc: "ANIME/HD"}
|
||||
- {id: 1, cat: PC/0day, desc: "Apps"}
|
||||
- {id: 18, cat: Movies/BluRay, desc: "Dr Who"}
|
||||
- {id: 5, cat: TV, desc: "TV Episodes"}
|
||||
- {id: 2, cat: PC/Games, desc: "Games/PC"}
|
||||
- {id: 11, cat: TV/HD, desc: "TV/HD"}
|
||||
- {id: 30, cat: Console/PS4, desc: "GAMES/PS4"}
|
||||
- {id: 7, cat: Console/PSP, desc: "Games/PSP"}
|
||||
- {id: 17, cat: Movies/BluRay, desc: "Movie Packs"}
|
||||
- {id: 10, cat: Movies, desc: "Movies X264"}
|
||||
- {id: 3, cat: Movies/DVD, desc: "Movies/DVDR"}
|
||||
- {id: 50, cat: Movies/BluRay, desc: "Movies/Bluray-UHD"}
|
||||
- {id: 31, cat: Movies/BluRay, desc: "MOVIES/COMPLETE-BLURAY"}
|
||||
- {id: 3, cat: Movies/DVD, desc: "MOVIES/DVDR"}
|
||||
- {id: 16, cat: TV, desc: "TV Packs"}
|
||||
- {id: 6, cat: XXX, desc: "MOVIES/XXX"}
|
||||
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: incldead
|
||||
type: select
|
||||
label: "Search Torrents that are:"
|
||||
default: "0"
|
||||
options:
|
||||
"0" : "Active"
|
||||
"1": "Including Dead"
|
||||
"2": "Only Dead"
|
||||
- name: onlyfree
|
||||
type: checkbox
|
||||
label: Show only Free torrents
|
||||
default: false
|
||||
- name: info
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the 'Torrents per page' setting to 100 on your profile.
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: form
|
||||
form: form[action="takelogin.php"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
submitme: "X"
|
||||
submitme: "X" # two submitme needed?!? shouldn't make a difference
|
||||
error:
|
||||
- selector: h2:contains("Login failed!")
|
||||
message:
|
||||
selector: td.colhead2
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
searchin: "title"
|
||||
incldead: "{{ .Config.incldead }}"
|
||||
"only_free": "{{ if .Config.onlyfree }}1{{else}}0{{end}}"
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["(\\w+)", " +$1"] # prepend + to each word
|
||||
rows:
|
||||
selector: tr.browse_color, tr[id^="kdescr"]
|
||||
after: 1
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-of-type(1) a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title:
|
||||
selector: td:nth-of-type(2) a
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: split
|
||||
args: [">", "1"]
|
||||
- name: replace
|
||||
args: ["</b", ""]
|
||||
details:
|
||||
selector: td:nth-of-type(2) > a[onmouseover]
|
||||
attribute: href
|
||||
banner:
|
||||
selector: td:nth-of-type(2) > a[onmouseover]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=\\\\'(.+?)\\\\'"
|
||||
- name: replace
|
||||
args: ["./pic/noposter.png", ""]
|
||||
download:
|
||||
selector: td:nth-of-type(3) a
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-of-type(5)
|
||||
date:
|
||||
selector: td:nth-of-type(7)
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "Jan 02 2006 03:04 PM"
|
||||
size:
|
||||
selector: td:nth-of-type(8)
|
||||
grabs:
|
||||
selector: td:nth-of-type(9)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "([\\d]+)"
|
||||
seeders:
|
||||
selector: td:nth-of-type(10)
|
||||
leechers:
|
||||
selector: td:nth-of-type(11)
|
||||
description:
|
||||
selector: td[colspan="14"]
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\n", "<br>\n"]
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"b:contains(\"[Free and Double]\")": "0"
|
||||
"img[alt=\"Free Torrent\"]": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"b:contains(\"[Free and Double]\")": "2"
|
||||
"*": "1"
|
@@ -6,10 +6,10 @@
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.shareisland.org/
|
||||
legacylinks:
|
||||
- https://shareisland.org/
|
||||
- http://shareisland.org/
|
||||
legacylinks:
|
||||
- http://shareisland.org/
|
||||
- http://www.shareisland.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
@@ -28,35 +28,35 @@
|
||||
- {id: 41, cat: Books, desc: "Quotidiani"}
|
||||
- {id: 59, cat: Books, desc: "Fumetti"}
|
||||
- {id: 60, cat: Books, desc: "Riviste"}
|
||||
- {id: 61, cat: Books, desc: "Audiolibri"}
|
||||
# Games
|
||||
- {id: 47, cat: PC/Games, desc: "Games PC"}
|
||||
- {id: 22, cat: Console/Other, desc: "Nintendo"}
|
||||
- {id: 40, cat: Console/Other, desc: "Nintendo"}
|
||||
- {id: 13, cat: Console/PS4, desc: "Sony PS"}
|
||||
- {id: 20, cat: Console/Xbox, desc: "XboX"}
|
||||
- {id: 33, cat: Console/Xbox, desc: "XboX"}
|
||||
- {id: 14, cat: Console/Wii, desc: "Wii"}
|
||||
# Music
|
||||
- {id: 54, cat: Audio/MP3, desc: "MP3"}
|
||||
- {id: 55, cat: Audio/Lossless, desc: "Flac"}
|
||||
# Movies
|
||||
- {id: 17, cat: Movies/SD, desc: "Cine News"}
|
||||
- {id: 23, cat: Movies/SD, desc: "BDRip"}
|
||||
- {id: 43, cat: Movies/SD, desc: "BDRip"}
|
||||
- {id: 16, cat: Movies/SD, desc: "DivX"}
|
||||
- {id: 32, cat: Movies/SD, desc: "DVDRip"}
|
||||
- {id: 11, cat: Movies/DVD, desc: "DVD"}
|
||||
- {id: 29, cat: Movies/HD, desc: "720p"}
|
||||
- {id: 30, cat: Movies/HD, desc: "1080p"}
|
||||
- {id: 35, cat: Movies/BluRay, desc: "Blu Ray Disk"}
|
||||
- {id: 40, cat: Movies/HD, desc: "H-265"}
|
||||
- {id: 56, cat: Movies/3D, desc: "FullHD-3D"}
|
||||
- {id: 27, cat: TV/SD, desc: "SerieTV"}
|
||||
- {id: 20, cat: Movies/SD, desc: "DVDRip"}
|
||||
- {id: 21, cat: Movies/DVD, desc: "DVD"}
|
||||
- {id: 25, cat: Movies/HD, desc: "720p"}
|
||||
- {id: 24, cat: Movies/HD, desc: "1080p"}
|
||||
- {id: 27, cat: Movies/BluRay, desc: "Blu Ray Disk"}
|
||||
- {id: 23, cat: Movies/HD, desc: "H-265"}
|
||||
- {id: 26, cat: Movies/3D, desc: "3D-FullHD"}
|
||||
- {id: 31, cat: TV/SD, desc: "SerieTV"}
|
||||
- {id: 45, cat: TV/HD, desc: "Serie Tv HD"}
|
||||
- {id: 44, cat: Movies/UHD, desc: "4K Ultra HD"}
|
||||
- {id: 22, cat: Movies/UHD, desc: "4K-Ultra-HD"}
|
||||
- {id: 49, cat: TV/Documentary, desc: "Documentari"}
|
||||
- {id: 50, cat: TV/Other, desc: "Programmi TV"}
|
||||
- {id: 51, cat: Movies/Other, desc: "Mp4"}
|
||||
|
||||
- {id: 5, cat: TV/Anime, desc: "Anime"}
|
||||
- {id: 31, cat: TV/Anime, desc: "Cartoni Animati"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -226,7 +226,7 @@
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(Ep[\\.]?[ ]?)|([S]\\d\\d[Ee])", "E"]
|
||||
args: ["(Ep[\\.]?[ ]?(\\d{1,3}))", "E$2"]
|
||||
title_normal:
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
filters:
|
||||
@@ -273,11 +273,15 @@
|
||||
- name: replace
|
||||
args: ["Resolução:", ""]
|
||||
- name: re_replace
|
||||
args: ["(\\d{3,4})[ ]?x[ ]?(1[0-9][0-9][0-9])", "1080p"]
|
||||
args: ["(\\d{3,4})[ ]?[xX][ ]?(1[0-9][0-9][0-9])", "1080p"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{3,4})[ ]?x[ ]?([2-9][0-9][0-9][0-9])", "2160p"]
|
||||
args: ["(\\d{3,4})[ ]?[xX][ ]?([2-9][0-9][0-9][0-9])", "2160p"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{3,4})[ ]?x[ ]?([7-9][0-9][0-9])", "720p"]
|
||||
args: ["(\\d{3,4})[ ]?[xX][ ]?([6-9][0-9][0-9])", "720p"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{3,4})[ ]?[xX][ ]?([3-5][0-9][0-9])", "480p"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{3,4})[ ]?[xX][ ]?([1-2][0-9][0-9])", "240p"]
|
||||
download:
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
attribute: href
|
||||
|
@@ -34,15 +34,8 @@
|
||||
page: 1
|
||||
srcrel: "{{ .Keywords }}"
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["S[0-9]{2}([^E]|$)", ""] # remove season tag without episode (search doesn't support it)
|
||||
- name: diacritics
|
||||
args: replace
|
||||
# most ITA TV torrents are in XXxYY format, so we search without S/E prefixes and filter later
|
||||
- name: re_replace
|
||||
args: ["S0?(\\d{1,2})", " $1 "]
|
||||
- name: re_replace
|
||||
args: ["E(\\d{2,3})", " $1 "]
|
||||
rows:
|
||||
selector: div.showrelease_tb table tbody tr:not(tr:nth-child(1))
|
||||
fields:
|
||||
@@ -54,27 +47,9 @@
|
||||
filters:
|
||||
- name: split
|
||||
args: ["=", "-1"]
|
||||
# inizio prova
|
||||
- name: re_replace # replace special characters with " " (space)
|
||||
args: ["[^a-zA-Z0-9]|\\.", " "]
|
||||
# normalize to SXXEYY format
|
||||
- name: re_replace
|
||||
args: ["(\\d{2})x(\\d{2})", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["(\\d{1})x(\\d{2})", "S0$1E$2"]
|
||||
- name: re_replace #Stagione X --> S0X
|
||||
args: ["Stagione (\\d{0,1}\\s)", "S0$1"]
|
||||
- name: re_replace #Stagione XX --> SXX
|
||||
args: ["Stagione (\\d{2}\\s)", "S$1"]
|
||||
- name: re_replace #/ Episodio [YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sEpisodio|\\s\\/\\sEpisodi|\\sEpisodio|\\s\\|\\sEpisodio|\\sEpisodi)\\s\\[", "E"]
|
||||
- name: re_replace #/ Completa [episodi YY-YY --> EYY-YY
|
||||
args: ["(\\s\\/\\sCompleta\\s\\[episodi\\s)", "E"]
|
||||
- name: re_replace #remove di YY] | remove /YY]
|
||||
args: ["(\\sdi\\s\\d{1,2}|\\/\\d{1,2})\\]", " "]
|
||||
- name: re_replace #remove various
|
||||
args: ["(Serie completa|Completa|\\[in pausa\\])", ""]
|
||||
# fine prova
|
||||
args: ["\\b([s])?(\\d{1,3})[x\\s](\\d{1,3})", "S$2E$3"]
|
||||
details:
|
||||
selector: td:nth-child(7) a
|
||||
attribute: href
|
||||
|
@@ -60,6 +60,8 @@
|
||||
args: ["[^a-zA-Z0-9]+", "-"]
|
||||
rows:
|
||||
selector: table > tbody > tr:has(td:has(div:has(a[href^="/torrent/"])))
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
title:
|
||||
selector: td:nth-child(1) > div > a
|
||||
|
111
src/Jackett.Common/Definitions/torrent-turk.yml
Normal file
111
src/Jackett.Common/Definitions/torrent-turk.yml
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
site: torrent-turk
|
||||
name: TOrrent-tuRK
|
||||
description: "TOrrent-tuRK (TORK) is a Turkish Private Torrent Tracker for HD MOVIES / TV / GENERAL"
|
||||
language: tr-TR
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrent-turk.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 149, cat: Movies, desc: "Movies/Turkish"}
|
||||
- {id: 151, cat: Movies/HD, desc: "Movies/Turkish/1080p"}
|
||||
- {id: 152, cat: Movies/HD, desc: "Movies/Turkish/720p"}
|
||||
|
||||
- {id: 156, cat: Movies, desc: "Movies/Foreign"}
|
||||
- {id: 157, cat: Movies/UHD, desc: "Movies/Foreign/4K"}
|
||||
- {id: 159, cat: Movies/HD, desc: "Movies/Foreign/1080p"}
|
||||
- {id: 160, cat: Movies/HD, desc: "Movies/Foreign/720p"}
|
||||
|
||||
- {id: 164, cat: TV, desc: "TV"}
|
||||
- {id: 165, cat: TV, desc: "TV/Turkish"}
|
||||
- {id: 166, cat: TV, desc: "TV/Foreign"}
|
||||
|
||||
- {id: 171, cat: Audio, desc: "Music"}
|
||||
- {id: 172, cat: Audio, desc: "Music/Turkish"}
|
||||
- {id: 173, cat: Audio, desc: "Music/Foreign"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q]
|
||||
movie-search: [q]
|
||||
|
||||
login:
|
||||
path: /?p=home&pid=1
|
||||
method: form
|
||||
form: form#loginbox_form
|
||||
submitpath: /ajax/login.php
|
||||
inputs:
|
||||
action: "login"
|
||||
loginbox_membername: "{{ .Config.username }}"
|
||||
loginbox_password: "{{ .Config.password }}"
|
||||
loginbox_remember: "true"
|
||||
selectorinputs:
|
||||
securitytoken:
|
||||
selector: "script:contains(\"stKey: \")"
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "stKey: \"(.+?)\","
|
||||
error:
|
||||
- selector: ":contains(\"-ERROR-\")"
|
||||
test:
|
||||
path: /?p=torrents&type=bookmarks&pid=508
|
||||
selector: a#logout
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["[^a-zA-Z0-9]+", "%25"]
|
||||
inputs:
|
||||
p: "torrents"
|
||||
pid: "32"
|
||||
$raw: "{{range .Categories}}cid[]={{.}}&{{end}}"
|
||||
keywords: "{{ .Keywords }}"
|
||||
search_type: "name"
|
||||
searchin: "title"
|
||||
error:
|
||||
- selector: div.error:not(:contains("Hiçbir sonuç bulunamadı."))
|
||||
rows:
|
||||
selector: table#torrents_table_classic > tbody > tr:has(td.torrent_name)
|
||||
fields:
|
||||
title:
|
||||
selector: a[href*="?p=torrents&pid=10&action=details"]
|
||||
category:
|
||||
selector: div.category_image > a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cid
|
||||
details:
|
||||
selector: a[href*="?p=torrents&pid=10&action=details"]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href*="?p=torrents&pid=10&action=download"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: a[rel="torrent_size"]
|
||||
seeders:
|
||||
selector: a[rel="torrent_seeders"]
|
||||
leechers:
|
||||
selector: a[rel="torrent_leechers"]
|
||||
grabs:
|
||||
selector: a[rel="times_completed"]
|
||||
banner:
|
||||
selector: a[rel="fancybox"]
|
||||
optional: true
|
||||
attribute: href
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"img[title=\"FREE!\"]": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
date:
|
||||
selector: td.torrent_name > abbr.timeago
|
||||
optional: true
|
||||
attribute: data-time
|
@@ -6,8 +6,10 @@
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.torrent9.red/
|
||||
- http://www.torrent9.blue/
|
||||
legacylinks:
|
||||
- http://www.torrent9.ec/
|
||||
- http://www.torrent9.red/
|
||||
- http://www.torrent9.bz/
|
||||
- http://www.torrents9.pe/
|
||||
- http://www.torrent9.cc/
|
||||
@@ -27,33 +29,12 @@
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: category
|
||||
type: select
|
||||
label: Category Filter
|
||||
default: "_"
|
||||
options:
|
||||
"_" : "All"
|
||||
"films": "Movies"
|
||||
"films-french": "Movies/French"
|
||||
"films-vostfr": "Movies/VOSTFR"
|
||||
"films-dvdrip-x264": "Movies/DVDRIP .x264"
|
||||
"720p": "Movies/BluRay 720p"
|
||||
"1080p": "Movies/BluRay 1080p"
|
||||
"series": "TV/Series"
|
||||
"series-vostfr": "TV/VOSTFR"
|
||||
"series-francaise": "TV/French"
|
||||
"series-dvdrip": "TV/DVDRIP"
|
||||
"spectacles": "Shows"
|
||||
"musique": "Music"
|
||||
"ebook": "Ebooks"
|
||||
"logiciels": "Software"
|
||||
"jeux-pc": "PC Games"
|
||||
"jeux-consoles": "Console Games"
|
||||
|
||||
download:
|
||||
selector: a[href^="/downloading/"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Keywords }}/search_torrent/{{ re_replace .Config.category \"_\" \"\" }}/{{ .Keywords }}/page-0{{else}}/top_torrent.php{{end}}"
|
||||
- path: "{{ if .Keywords }}/search_torrent/{{ .Keywords }}/page-0{{else}}/top_torrent.php{{end}}"
|
||||
rows:
|
||||
selector: div.table-responsive > table tbody tr
|
||||
fields:
|
||||
@@ -91,11 +72,6 @@
|
||||
download:
|
||||
selector: td:nth-child(1) a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: [ "/torrent/\\d{5,}/", "/get_torrent/"]
|
||||
- name: append
|
||||
args: ".torrent"
|
||||
date:
|
||||
text: "now"
|
||||
size:
|
||||
|
@@ -22,10 +22,18 @@
|
||||
modes:
|
||||
search: [q]
|
||||
|
||||
settings: []
|
||||
settings:
|
||||
- name: downloadlink
|
||||
type: select
|
||||
label: Download link
|
||||
default: "magnet:"
|
||||
options:
|
||||
"http://itorrents.org/" : "iTorrents.org"
|
||||
"magnet:": "magnet"
|
||||
|
||||
|
||||
download:
|
||||
selector: a[href^="magnet:"]
|
||||
selector: a[href^="{{ .Config.downloadlink }}"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
@@ -44,6 +52,8 @@
|
||||
download:
|
||||
selector: p:nth-child(1) > a[href^="/torrent/"]
|
||||
attribute: href
|
||||
date:
|
||||
text: now
|
||||
size:
|
||||
selector: span:nth-child(5)
|
||||
seeders:
|
||||
|
@@ -30,7 +30,8 @@
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
@@ -42,34 +43,37 @@
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("failed"))
|
||||
test:
|
||||
path: browse.php
|
||||
path: t
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
- path: t
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
$raw: "{{range .Categories}}{{.}}=&{{end}}"
|
||||
q: "{{if .Query.IMDBID}}{{ .Query.IMDBID }}{{else}}{{ .Keywords }}{{end}}"
|
||||
incldead: 1
|
||||
rows:
|
||||
selector: table#torrentsTable > tbody > tr.torrentsTableTR
|
||||
selector: table#torrentTable > tbody > tr:has(td.torrentNameInfo)
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
selector: a[href^="/download.php/"]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a.nameLink
|
||||
selector: a[href^="/details.php?id="]
|
||||
details:
|
||||
selector: a.nameLink
|
||||
selector: a[href^="/details.php?id="]
|
||||
attribute: href
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
selector: a[href^="?"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
- name: replace
|
||||
args: ["?", ""]
|
||||
date:
|
||||
selector: div.uploaded
|
||||
selector: td.torrentNameInfo > div
|
||||
filters:
|
||||
- name: split
|
||||
args: ["|", -1]
|
||||
seeders:
|
||||
selector: td:nth-last-child(2)
|
||||
leechers:
|
||||
|
@@ -55,6 +55,7 @@
|
||||
- {id: 71, cat: XXX/Packs, desc: "pr0n / pack"}
|
||||
- {id: 30, cat: Other, desc: "Kita"}
|
||||
- {id: 41, cat: Books, desc: "E-Books"}
|
||||
- {id: 76, cat: TV, desc: "Animacija / LT"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
@@ -77,6 +78,9 @@
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}cats[]={{.}}&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
keywordsfilters:
|
||||
- name: replace
|
||||
args: [".", " "] # issue #3296
|
||||
rows:
|
||||
selector: table> tbody > tr[class^="torrents_table_row_"]
|
||||
filters:
|
||||
|
@@ -115,6 +115,8 @@
|
||||
rows:
|
||||
selector: tr.browse_color, tr[id^="kdescr"]
|
||||
after: 1
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-of-type(1) a
|
||||
|
@@ -1,111 +0,0 @@
|
||||
---
|
||||
site: torrentwtf
|
||||
name: Torrentwtf
|
||||
description: "Torrentwtf is a Czech Private site for TV / MOVIES / GENERAL"
|
||||
language: cs-cz
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrent.wtf/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: Movies, desc: "Filmy"}
|
||||
- {id: 2, cat: TV, desc: "Seriály"}
|
||||
- {id: 3, cat: Audio, desc: "Hudba"}
|
||||
- {id: 5, cat: PC/Games, desc: "Hry"}
|
||||
- {id: 6, cat: Books, desc: "Knihy"}
|
||||
- {id: 8, cat: PC, desc: "Software"}
|
||||
- {id: 9, cat: XXX, desc: "xXx"}
|
||||
- {id: 10, cat: Other, desc: "Ostatní"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
|
||||
login:
|
||||
path: /login
|
||||
method: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: table.main:contains("Tieto poverenia sa nezhodujú s našimi záznamami.")
|
||||
test:
|
||||
path: /torrents
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: /filter
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}categories[]={{.}}&{{end}}"
|
||||
search: "{{if .Query.IMDBID}}{{else}}{{ .Keywords }}{{end}}"
|
||||
imdb: "{{ .Query.IMDBIDShort }}"
|
||||
tvdb: ""
|
||||
tmdb: ""
|
||||
sorting: created_at
|
||||
direction: desc
|
||||
qty: 100
|
||||
preprocessingfilters:
|
||||
- name: jsonjoinarray
|
||||
args: ["$.result", ""]
|
||||
- name: prepend
|
||||
args: "<table>"
|
||||
- name: append
|
||||
args: "</table>"
|
||||
rows:
|
||||
selector: tr
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="/categories/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/categories/.*?\\.(\\d+)"
|
||||
title:
|
||||
selector: a.view-torrent
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: [".*? / ", ""]
|
||||
download:
|
||||
selector: a[href*="/download_check/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["/download_check/", "/download/"]
|
||||
details:
|
||||
selector: a.view-torrent
|
||||
attribute: href
|
||||
imdb:
|
||||
optional: true
|
||||
selector: a[href*="://www.imdb.com/title/"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: ([\d\.]+)
|
||||
date:
|
||||
selector: time
|
||||
attribute: datetime
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00"
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04:05 -07:00"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"i[data-original-title=\"100% Free\"]": "0"
|
||||
"i[data-original-title=\"Global FreeLeech\"]": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"i[data-original-title=\"Double upload\"]": "2"
|
||||
"*": "1"
|
@@ -76,20 +76,20 @@ search:
|
||||
- name: replace
|
||||
args: ["&hit=", "&aviso="]
|
||||
files:
|
||||
selector: td:nth-child(6)
|
||||
selector: td:nth-last-child(8)
|
||||
size:
|
||||
selector: td:nth-child(9)
|
||||
selector: td:nth-last-child(5)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
grabs:
|
||||
selector: td:nth-child(10)
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
selector: td:nth-child(11)
|
||||
selector: td:nth-last-child(3)
|
||||
leechers:
|
||||
selector: td:nth-child(12)
|
||||
selector: td:nth-last-child(2)
|
||||
date:
|
||||
selector: td:nth-child(8)
|
||||
selector: td:nth-last-child(6)
|
||||
filters:
|
||||
- name: timeago
|
||||
downloadvolumefactor:
|
||||
|
@@ -120,7 +120,7 @@
|
||||
selector: table#browsetable > tbody > tr:has(a[href^="/details.php?id="])
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="/browse.php?q="]
|
||||
selector: a[href^="/browse.php"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
|
@@ -72,8 +72,8 @@
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
path: /login.php
|
||||
method: form
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
|
@@ -162,6 +162,8 @@
|
||||
- path: "torrents-search.php?{{range .Categories}}c{{.}}=1&{{end}}search={{.Keywords}}{{if .Categories}}{{else}}&cat=0{{end}}&incldead=0&lang=0"
|
||||
rows:
|
||||
selector: tr:not(:has(script)):has(a[title][href^="torrents-details.php?id="])
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
category:
|
||||
optional: true
|
||||
|
@@ -4,7 +4,7 @@
|
||||
description: "xbytesV2 is a SPANISH site for HD content"
|
||||
language: es-es
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
encoding: ISO-8859-1
|
||||
links:
|
||||
- http://xbytesv2.li
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
- {id: 30 , cat: Movies/HD, desc: "Peliculas - MicroHD x265"}
|
||||
- {id: 35 , cat: TV/HD, desc: "TV/Series - 4K"}
|
||||
- {id: 31 , cat: TV/HD, desc: "TV/Series - x265"}
|
||||
- {id: 25 , cat: TV/HD, desc: "TV/Series"}
|
||||
- {id: 25 , cat: TV/HD, desc: "TV/Series - HDTV & WEB-DL 1080p"}
|
||||
- {id: 37 , cat: TV/Documentary, desc: "Documentales"}
|
||||
- {id: 41 , cat: TV/Sport, desc: "Deportes"}
|
||||
- {id: 5 , cat: Movies/HD, desc: "Anime"}
|
||||
@@ -57,6 +57,35 @@
|
||||
args: category
|
||||
title:
|
||||
selector: td[valign="middle"] a
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["\\/", " "]
|
||||
- name: re_replace
|
||||
args: ["S(\\d{1,2}) E(\\d{1,2})", "S$1E$2"]
|
||||
- name: re_replace
|
||||
args: ["\\(", ""]
|
||||
- name: re_replace
|
||||
args: ["\\)", ""]
|
||||
- name: re_replace
|
||||
args: ["20[0-2][0-9] [0-9][0-9]", ""]
|
||||
- name: re_replace
|
||||
args: ["20[0-2][0-9]", ""]
|
||||
- name: replace
|
||||
args: ["ESP", "Spanish"]
|
||||
- name: re_replace
|
||||
args: ["[EI]NG", "English"]
|
||||
- name: replace
|
||||
args: ["CAT", "Catalan"]
|
||||
- name: replace
|
||||
args: ["FRA", "French"]
|
||||
- name: replace
|
||||
args: ["JAP", "Japanese"]
|
||||
- name: replace
|
||||
args: ["ITA", "Italian"]
|
||||
- name: replace
|
||||
args: ["RUS", "Russian"]
|
||||
- name: replace
|
||||
args: ["DUAL", "Spanish English"]
|
||||
details:
|
||||
selector: td[valign="middle"] a
|
||||
attribute: href
|
||||
@@ -85,8 +114,8 @@
|
||||
attribute: href
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src$="gold.gif"]: "0"
|
||||
img[src$="silver.gif"]: "0.5"
|
||||
img[src$="gold.png"]: "0"
|
||||
img[src$="silver.png"]: "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
|
@@ -6,8 +6,9 @@
|
||||
type: semi-private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://yggtorrent.is/
|
||||
- https://ww1.yggtorrent.is/
|
||||
legacylinks:
|
||||
- https://yggtorrent.is/
|
||||
- https://yggtorrent.com/
|
||||
- https://ww1.yggtorrent.com/
|
||||
|
||||
@@ -96,7 +97,18 @@
|
||||
type: checkbox
|
||||
label: Try to normalize releases names by moving year after the title
|
||||
default: false
|
||||
|
||||
- name: multilang
|
||||
type: checkbox
|
||||
label: Replace MULTI by another language in release name
|
||||
default: false
|
||||
- name: multilanguage
|
||||
type: select
|
||||
label: Replace MULTI by this language
|
||||
default: FRENCH
|
||||
options:
|
||||
FRENCH : "FRENCH"
|
||||
MULTI.FRENCH: "MULTI.FRENCH"
|
||||
ENGLISH: "ENGLISH"
|
||||
login:
|
||||
method: form
|
||||
path: /
|
||||
@@ -140,8 +152,15 @@
|
||||
- name: re_replace
|
||||
args: ["(\\s{2,5})", " "]
|
||||
- name: trim
|
||||
title:
|
||||
title_phase1:
|
||||
text: "{{if .Config.filter_title }}{{ .Result.title_filtered }}{{else}}{{ .Result.title_normal }}{{end}}"
|
||||
title_multilang:
|
||||
text: "{{ .Result.title_phase1 }}"
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["[\\.\\s\\[\\-][Mm][Uu][Ll][Tt][Ii][\\.\\s\\]\\-]", ".{{ .Config.multilanguage }}."]
|
||||
title:
|
||||
text: "{{if .Config.multilang }}{{ .Result.title_multilang }}{{else}}{{ .Result.title_phase1 }}{{end}}"
|
||||
details:
|
||||
selector: ":nth-child(2) > a"
|
||||
attribute: href
|
||||
@@ -225,6 +244,11 @@
|
||||
args: " ago"
|
||||
size:
|
||||
selector: "td:nth-child(6)"
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["o", "B"]
|
||||
grabs:
|
||||
selector: "td:nth-child(7)"
|
||||
seeders:
|
||||
text: 0
|
||||
seeders:
|
||||
@@ -238,4 +262,4 @@
|
||||
downloadvolumefactor:
|
||||
text: "1"
|
||||
uploadvolumefactor:
|
||||
text: "1"
|
||||
text: "1"
|
||||
|
@@ -48,7 +48,11 @@
|
||||
selector: .responsetop > tbody > tr:has(td.td_newborder)
|
||||
fields:
|
||||
title:
|
||||
selector: td:nth-child(2) > a:nth-child(1)
|
||||
selector: td:nth-child(2) > a[href^="/download.php"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["^(.*?)download\\.php\\/[0-9]{1,10}\\/|\\.torrent(?=[^.]*$)", ""]
|
||||
details:
|
||||
selector: td:nth-child(2) > a:nth-child(1)
|
||||
attribute: href
|
||||
|
@@ -33,6 +33,8 @@
|
||||
- path: "/search?{{if .Keywords}}s=ns&v=t&sd=d&q={{ .Keywords}}{{else}}s=dt&v=t&sd=d&q= *{{end}}{{if .Categories}} category:{{range .Categories}}{{.}},{{end}}{{else}}{{end}}"
|
||||
rows:
|
||||
selector: tr:has(td[class^="text-muted3"])
|
||||
filters:
|
||||
- name: andmatch
|
||||
fields:
|
||||
title:
|
||||
selector: td:nth-child(2) a
|
||||
|
@@ -13,7 +13,6 @@ using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.LayoutRenderers;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace Jackett.Common
|
||||
@@ -179,7 +178,7 @@ namespace Jackett.Common
|
||||
var logFileName = settings.CustomLogFileName ?? "log.txt";
|
||||
var logLevel = settings.TracingEnabled ? LogLevel.Debug : LogLevel.Info;
|
||||
// Add custom date time format renderer as the default is too long
|
||||
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("simpledatetime", typeof(SimpleDateTimeRenderer));
|
||||
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("simpledatetime", typeof(Utils.LoggingSetup.SimpleDateTimeRenderer));
|
||||
|
||||
var logConfig = new LoggingConfiguration();
|
||||
var logFile = new FileTarget();
|
||||
@@ -265,13 +264,4 @@ namespace Jackett.Common
|
||||
ConfigService.SaveConfig(ServerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
[LayoutRenderer("simpledatetime")]
|
||||
public class SimpleDateTimeRenderer : LayoutRenderer
|
||||
{
|
||||
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
|
||||
{
|
||||
builder.Append(DateTime.Now.ToString("MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -53,15 +53,20 @@ namespace Jackett.Common.Indexers.Abstract
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
public override void LoadValuesFromJson(JToken jsonConfig, bool useProtectionService = false)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
base.LoadValuesFromJson(jsonConfig, useProtectionService);
|
||||
|
||||
var useTokenItem = (ConfigurationData.BoolItem)configData.GetDynamic("usetoken");
|
||||
if (useTokenItem != null)
|
||||
{
|
||||
useTokens = useTokenItem.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
@@ -84,10 +89,16 @@ namespace Jackett.Common.Indexers.Abstract
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
// hook to adjust the search term
|
||||
protected virtual string GetSearchTerm(TorznabQuery query)
|
||||
{
|
||||
return query.GetQueryString();
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchString = GetSearchTerm(query);
|
||||
|
||||
var searchUrl = APIUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
@@ -228,10 +239,40 @@ namespace Jackett.Common.Indexers.Abstract
|
||||
if (torrent["hasCue"] != null && (bool)torrent["hasCue"])
|
||||
flags.Add("Cue");
|
||||
|
||||
// tehconnection.me specific?
|
||||
var lang = (string)torrent["lang"];
|
||||
if (!string.IsNullOrEmpty(lang) && lang != "---")
|
||||
flags.Add(lang);
|
||||
|
||||
var media = (string)torrent["media"];
|
||||
if (!string.IsNullOrEmpty(media))
|
||||
flags.Add(media);
|
||||
|
||||
// tehconnection.me specific?
|
||||
var resolution = (string)torrent["resolution"];
|
||||
if (!string.IsNullOrEmpty(resolution))
|
||||
flags.Add(resolution);
|
||||
|
||||
// tehconnection.me specific?
|
||||
var container = (string)torrent["container"];
|
||||
if (!string.IsNullOrEmpty(container))
|
||||
flags.Add(container);
|
||||
|
||||
// tehconnection.me specific?
|
||||
var codec = (string)torrent["codec"];
|
||||
if (!string.IsNullOrEmpty(codec))
|
||||
flags.Add(codec);
|
||||
|
||||
// tehconnection.me specific?
|
||||
var audio = (string)torrent["audio"];
|
||||
if (!string.IsNullOrEmpty(audio))
|
||||
flags.Add(audio);
|
||||
|
||||
// tehconnection.me specific?
|
||||
var subbing = (string)torrent["subbing"];
|
||||
if (!string.IsNullOrEmpty(subbing) && subbing != "---")
|
||||
flags.Add(subbing);
|
||||
|
||||
if (torrent["remastered"] != null && (bool)torrent["remastered"])
|
||||
{
|
||||
var remasterYear = (string)torrent["remasterYear"];
|
||||
@@ -272,5 +313,28 @@ namespace Jackett.Common.Indexers.Abstract
|
||||
release.UploadVolumeFactor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var content = await base.Download(link);
|
||||
|
||||
// Check if we're out of FL tokens
|
||||
// most gazelle trackers will simply return the torrent anyway but e.g. redacted will return an error
|
||||
var requestLink = link.ToString();
|
||||
if (content.Length >= 1
|
||||
&& content[0] != 'd' // simple test for torrent vs HTML content
|
||||
&& requestLink.Contains("usetoken=1"))
|
||||
{
|
||||
var html = Encoding.GetString(content);
|
||||
if (html.Contains("You do not have any freeleech tokens left."))
|
||||
{
|
||||
// download again with usetoken=0
|
||||
var requestLinkNew = requestLink.Replace("usetoken=1", "usetoken=0");
|
||||
content = await base.Download(new Uri(requestLinkNew));
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -47,5 +47,10 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(23, TorznabCatType.AudioOther, "Music");
|
||||
AddCategoryMapping(24, TorznabCatType.Other, "Misc");
|
||||
}
|
||||
|
||||
protected override string GetSearchTerm(TorznabQuery query)
|
||||
{
|
||||
return query.GetQueryString().Replace(".", " "); // Alpharatio can't handle dots in the searchstr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,211 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CsQuery;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class Andraste : BaseWebIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
private new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Andraste(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Andraste",
|
||||
description: "A German general tracker.",
|
||||
link: "https://andraste.io/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
Encoding = Encoding.GetEncoding("iso-8859-1");
|
||||
Language = "de-de";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping(9, TorznabCatType.Other); // Anderes
|
||||
AddCategoryMapping(23, TorznabCatType.TVAnime); // Animation - Film &; Serie
|
||||
AddCategoryMapping(1, TorznabCatType.PC); // Appz
|
||||
AddCategoryMapping(52, TorznabCatType.Other); // Botuploads
|
||||
AddCategoryMapping(25, TorznabCatType.TVDocumentary); // Doku - Alle Formate
|
||||
AddCategoryMapping(27, TorznabCatType.Books); // E-Books
|
||||
AddCategoryMapping(51, TorznabCatType.Movies3D); // Film/3D
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesDVD); // Film/DVDr
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesHD); // Film/HD 1080p++
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesSD); // Film/HD 720p
|
||||
AddCategoryMapping(36, TorznabCatType.Movies); // Film/im Kino
|
||||
AddCategoryMapping(19, TorznabCatType.Movies); // Film/XviD,DivX,x264
|
||||
AddCategoryMapping(4, TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(12, TorznabCatType.ConsolePS4); // Games/Playstation
|
||||
AddCategoryMapping(22, TorznabCatType.ConsoleWii); // Games/Wii & DS
|
||||
AddCategoryMapping(21, TorznabCatType.ConsoleXbox); // Games/Xbox & 360
|
||||
AddCategoryMapping(48, TorznabCatType.PCPhoneAndroid); // Handy & PDA/Android
|
||||
AddCategoryMapping(47, TorznabCatType.PCPhoneIOS); // Handy & PDA/iOS
|
||||
AddCategoryMapping(44, TorznabCatType.PCMac); // Macintosh
|
||||
AddCategoryMapping(41, TorznabCatType.Other); // MegaPack
|
||||
AddCategoryMapping(24, TorznabCatType.AudioAudiobook); // Musik/Hörbuch & Hörspiel
|
||||
AddCategoryMapping(46, TorznabCatType.Audio); // Musik/HQ 320++
|
||||
AddCategoryMapping(6, TorznabCatType.Audio); // Musik/Musik
|
||||
AddCategoryMapping(26, TorznabCatType.AudioVideo); // Musik/Musikvideos
|
||||
AddCategoryMapping(29, TorznabCatType.TVSD); // Serien/DVDr
|
||||
AddCategoryMapping(35, TorznabCatType.TVHD); // Serien/HD 720p++
|
||||
AddCategoryMapping(7, TorznabCatType.TV); // Serien/XviD,DivX,x264
|
||||
AddCategoryMapping(45, TorznabCatType.TV); // Shows
|
||||
AddCategoryMapping(40, TorznabCatType.TVSport); // Sport
|
||||
AddCategoryMapping(32, TorznabCatType.XXX); // XXX
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["table.tableinborder"].Html();
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3, 0, 0), 3, 5, DayOfWeek.Sunday);
|
||||
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4, 0, 0), 10, 5, DayOfWeek.Sunday);
|
||||
TimeSpan delta = new TimeSpan(1, 0, 0);
|
||||
TimeZoneInfo.AdjustmentRule adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1), DateTime.MaxValue.Date, delta, startTransition, endTransition);
|
||||
TimeZoneInfo.AdjustmentRule[] adjustments = { adjustment };
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "(GMT+01:00) W. Europe Standard Time", "W. Europe Standard Time", "W. Europe DST Time", adjustments);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "1");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("orderby", "added");
|
||||
queryCollection.Add("sort", "desc");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("c" + cat, "1");
|
||||
}
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var response = await RequestStringWithCookies(searchUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var globalFreeleech = dom.Find("div > img[alt=\"Only Upload\"][title^=\"ONLY UPLOAD \"]").Any();
|
||||
var rows = dom["table.tableinborder > tbody > tr:has(td.tableb)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
|
||||
var qDetailsLink = qRow.Find("a[href^=details.php?id=]").First();
|
||||
release.Title = qDetailsLink.Attr("title");
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qDLLink = qRow.Find("a[href^=download.php?torrent=]").First();
|
||||
var qSeeders = qRow.Find("span:contains(Seeder) > b:eq(0)");
|
||||
var qLeechers = qRow.Find("span:contains(Seeder) > b:eq(1)");
|
||||
var qDateStr = qRow.Find("td > table > tbody > tr > td:eq(7)").First();
|
||||
var qSize = qRow.Find("span:contains(Volumen) > b:eq(0)").First();
|
||||
var qOnlyUpload = qRow.Find("img[title=OnlyUpload]");
|
||||
|
||||
if (qOnlyUpload.Any())
|
||||
{
|
||||
release.MinimumRatio = 2;
|
||||
release.MinimumSeedTime = 144 * 60 * 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 72 * 60 * 60;
|
||||
}
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.Attr("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text().Trim().Replace('\xA0', ' ');
|
||||
DateTime dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var files = qRow.Find("a[href*=\"&filelist=1\"] ~ font ~ b").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("a[href*=\"&tosnatchers=1\"] ~ font ~ b").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (globalFreeleech)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[alt=\"OU\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using CsQuery;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
@@ -18,23 +19,13 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class AnimeBytes : BaseCachingWebIndexer
|
||||
{
|
||||
private enum SearchType
|
||||
{
|
||||
Video,
|
||||
Audio
|
||||
}
|
||||
|
||||
private string LoginUrl { get { return SiteLink + "user/login"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?"; } }
|
||||
private string MusicSearchUrl { get { return SiteLink + "torrents2.php?"; } }
|
||||
private string ScrapeUrl { get { return SiteLink + "scrape.php"; } }
|
||||
private string TorrentsUrl { get { return SiteLink + "torrents.php"; } }
|
||||
public bool AllowRaws { get { return configData.IncludeRaw.Value; } }
|
||||
public bool InsertSeason { get { return configData.InsertSeason != null && configData.InsertSeason.Value; } }
|
||||
public bool AddSynonyms { get { return configData.AddSynonyms.Value; } }
|
||||
public bool FilterSeasonEpisode { get { return configData.FilterSeasonEpisode.Value; } }
|
||||
|
||||
string csrfIndex = null;
|
||||
string csrfToken = null;
|
||||
|
||||
private new ConfigurationDataAnimeBytes configData
|
||||
{
|
||||
get { return (ConfigurationDataAnimeBytes)base.configData; }
|
||||
@@ -58,7 +49,7 @@ namespace Jackett.Common.Indexers
|
||||
TorznabCatType.AudioOther),
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataAnimeBytes())
|
||||
configData: new ConfigurationDataAnimeBytes("Note: Go to AnimeBytes site and open your account settings. Go to 'Account' tab, move cursor over black part near 'Passkey' and copy its value. Your username is case sensitive."))
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
@@ -73,82 +64,22 @@ namespace Jackett.Common.Indexers
|
||||
return input;
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
// Get the login form as we need the CSRF Token
|
||||
var loginPage = await webclient.GetString(new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl,
|
||||
Encoding = Encoding,
|
||||
});
|
||||
UpdateCookieHeader(loginPage.Cookies);
|
||||
|
||||
CQ loginPageDom = loginPage.Content;
|
||||
csrfIndex = loginPageDom["input[name=\"_CSRF_INDEX\"]"].Last().Attr("value");
|
||||
csrfToken = loginPageDom["input[name=\"_CSRF_TOKEN\"]"].Last().Attr("value");
|
||||
|
||||
CQ qCaptchaImg = loginPageDom.Find("#captcha_img").First();
|
||||
if (qCaptchaImg.Length == 1)
|
||||
{
|
||||
var CaptchaUrl = SiteLink + qCaptchaImg.Attr("src");
|
||||
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;
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
lock (cache)
|
||||
if (configData.Passkey.Value.Length != 32 && configData.Passkey.Value.Length != 48)
|
||||
throw new Exception("invalid passkey configured: expected length: 32 or 48, got " + configData.Passkey.Value.Length.ToString());
|
||||
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
cache.Clear();
|
||||
throw new Exception("no results found, please report this bug");
|
||||
}
|
||||
|
||||
// Build login form
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "_CSRF_INDEX", csrfIndex },
|
||||
{ "_CSRF_TOKEN", csrfToken },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "keeplogged_sent", "true" },
|
||||
{ "keeplogged", "on" },
|
||||
{ "login", "Log In!" }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.CaptchaText.Value))
|
||||
{
|
||||
pairs.Add("captcha", configData.CaptchaText.Value);
|
||||
}
|
||||
|
||||
// Do the login
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CaptchaCookie.Value, true, null);
|
||||
|
||||
// Follow the redirect
|
||||
await FollowIfRedirect(response, LoginUrl, SearchUrl);
|
||||
|
||||
if (response.Status == HttpStatusCode.Forbidden)
|
||||
throw new ExceptionWithConfigData("Failed to login, your IP seems to be blacklisted (shared VPN/seedbox?). Contact the staff to resolve this.", configData);
|
||||
|
||||
await ConfigureIfOK(response.Cookies, response.Content != null && response.Content.Contains("/user/logout"), () =>
|
||||
{
|
||||
logger.Info(response.Content);
|
||||
CQ responseDom = response.Content;
|
||||
var alert = responseDom.Find("div.alert-danger");
|
||||
if (alert.Any())
|
||||
throw new ExceptionWithConfigData(alert.Text(), configData);
|
||||
|
||||
// Their login page appears to be broken and just gives a 500 error.
|
||||
throw new ExceptionWithConfigData("Failed to login (unknown reason), 6 failed attempts will get you banned for 6 hours.", configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
private string StripEpisodeNumber(string term)
|
||||
@@ -166,13 +97,13 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
if (ContainsMusicCategories(query.Categories))
|
||||
{
|
||||
foreach (var result in await GetResults(query, SearchType.Audio, query.SanitizedSearchTerm))
|
||||
foreach (var result in await GetResults(query, "music", query.SanitizedSearchTerm))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var result in await GetResults(query, SearchType.Video, StripEpisodeNumber(query.SanitizedSearchTerm)))
|
||||
foreach (var result in await GetResults(query, "anime", StripEpisodeNumber(query.SanitizedSearchTerm)))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
@@ -194,20 +125,26 @@ namespace Jackett.Common.Indexers
|
||||
return categories.Length == 0 || music.Any(categories.Contains);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> GetResults(TorznabQuery query, SearchType searchType, string searchTerm)
|
||||
private async Task<IEnumerable<ReleaseInfo>> GetResults(TorznabQuery query, string searchType, string searchTerm)
|
||||
{
|
||||
var cleanSearchTerm = WebUtility.UrlEncode(searchTerm);
|
||||
|
||||
// The result list
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var queryUrl = searchType == SearchType.Video ? SearchUrl : MusicSearchUrl;
|
||||
// Only include the query bit if its required as hopefully the site caches the non query page
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
var cat = "0";
|
||||
var queryCats = MapTorznabCapsToTrackers(query);
|
||||
if (queryCats.Count == 1)
|
||||
{
|
||||
queryUrl += string.Format("searchstr={0}&action=advanced&search_type=title&year=&year2=&tags=&tags_type=0&sort=time_added&way=desc&hentai=2&releasegroup=&epcount=&epcount2=&artbooktitle=", cleanSearchTerm);
|
||||
cat = queryCats.First().ToString();
|
||||
}
|
||||
|
||||
queryCollection.Add("username", configData.Username.Value);
|
||||
queryCollection.Add("torrent_pass", configData.Passkey.Value);
|
||||
queryCollection.Add("type", searchType);
|
||||
queryCollection.Add("searchstr", searchTerm);
|
||||
var queryUrl = ScrapeUrl + "?" + queryCollection.GetQueryString();
|
||||
|
||||
// Check cache first so we don't query the server for each episode when searching for each episode in a series.
|
||||
lock (cache)
|
||||
{
|
||||
@@ -221,263 +158,234 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
// Get the content from the tracker
|
||||
var response = await RequestStringWithCookiesAndRetry(queryUrl);
|
||||
if (response.IsRedirect)
|
||||
{
|
||||
// re-login
|
||||
await GetConfigurationForSetup();
|
||||
await ApplyConfiguration(null);
|
||||
response = await RequestStringWithCookiesAndRetry(queryUrl);
|
||||
}
|
||||
|
||||
CQ dom = response.Content;
|
||||
if (!response.Content.StartsWith("{")) // not JSON => error
|
||||
throw new ExceptionWithConfigData("unexcepted response (not JSON)", configData);
|
||||
dynamic json = JsonConvert.DeserializeObject<dynamic>(response.Content);
|
||||
|
||||
// Parse
|
||||
try
|
||||
{
|
||||
var releaseInfo = "S01";
|
||||
var root = dom.Find(".group_cont");
|
||||
// We may have got redirected to the series page if we have none of these
|
||||
if (root.Count() == 0)
|
||||
root = dom.Find(".torrent_table");
|
||||
if (json["error"] != null)
|
||||
throw new Exception(json["error"].ToString());
|
||||
|
||||
foreach (var series in root)
|
||||
var Matches = (long)json["Matches"];
|
||||
|
||||
if(Matches > 0)
|
||||
{
|
||||
var seriesCq = series.Cq();
|
||||
var groups = (JArray)json.Groups;
|
||||
|
||||
var synonyms = new List<string>();
|
||||
string mainTitle;
|
||||
if (searchType == SearchType.Video)
|
||||
mainTitle = seriesCq.Find(".group_title strong a").First().Text().Trim();
|
||||
else
|
||||
mainTitle = seriesCq.Find(".group_title strong").Text().Trim();
|
||||
|
||||
var yearStr = seriesCq.Find(".group_title strong").First().Text().Trim().Replace("]", "").Trim();
|
||||
int yearIndex = yearStr.LastIndexOf("[");
|
||||
if (yearIndex > -1)
|
||||
yearStr = yearStr.Substring(yearIndex + 1);
|
||||
|
||||
int year = 0;
|
||||
if (!int.TryParse(yearStr, out year))
|
||||
year = DateTime.Now.Year;
|
||||
|
||||
synonyms.Add(mainTitle);
|
||||
|
||||
// If the title contains a comma then we can't use the synonyms as they are comma seperated
|
||||
if (!mainTitle.Contains(",") && AddSynonyms)
|
||||
foreach (JObject group in groups)
|
||||
{
|
||||
var symnomnNames = string.Empty;
|
||||
foreach (var e in seriesCq.Find(".group_statbox li"))
|
||||
var synonyms = new List<string>();
|
||||
var groupID = (long)group["ID"];
|
||||
var Image = (string)group["Image"];
|
||||
var ImageUrl = (string.IsNullOrWhiteSpace(Image) ? null : new Uri(Image));
|
||||
var Year = (int)group["Year"];
|
||||
var GroupName = (string)group["GroupName"];
|
||||
var SeriesName = (string)group["SeriesName"];
|
||||
var Artists = (string)group["Artists"];
|
||||
|
||||
var mainTitle = WebUtility.HtmlDecode((string)group["FullName"]);
|
||||
if (SeriesName != null)
|
||||
mainTitle = SeriesName;
|
||||
|
||||
synonyms.Add(mainTitle);
|
||||
|
||||
// If the title contains a comma then we can't use the synonyms as they are comma seperated
|
||||
if (!mainTitle.Contains(",") && AddSynonyms)
|
||||
{
|
||||
if (e.FirstChild.InnerText == "Synonyms:")
|
||||
var symnomnNames = WebUtility.HtmlDecode((string)group["Synonymns"]);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(symnomnNames))
|
||||
{
|
||||
symnomnNames = e.InnerText;
|
||||
foreach (var name in symnomnNames.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var theName = name.Trim();
|
||||
if (!theName.Contains("&#") && !string.IsNullOrWhiteSpace(theName))
|
||||
{
|
||||
synonyms.Add(theName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(symnomnNames))
|
||||
List<int> Category = null;
|
||||
var category = (string)group["CategoryName"];
|
||||
|
||||
var Description = (string)group["Description"];
|
||||
|
||||
foreach (JObject torrent in group["Torrents"])
|
||||
{
|
||||
foreach (var name in symnomnNames.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
|
||||
var releaseInfo = "S01";
|
||||
string episode = null;
|
||||
int? season = null;
|
||||
var EditionTitle = (string)torrent["EditionData"]["EditionTitle"];
|
||||
if (!string.IsNullOrWhiteSpace(EditionTitle))
|
||||
releaseInfo = WebUtility.HtmlDecode(EditionTitle);
|
||||
|
||||
Regex SeasonRegEx = new Regex(@"Season (\d+)", RegexOptions.Compiled);
|
||||
var SeasonRegExMatch = SeasonRegEx.Match(releaseInfo);
|
||||
if (SeasonRegExMatch.Success)
|
||||
season = ParseUtil.CoerceInt(SeasonRegExMatch.Groups[1].Value);
|
||||
|
||||
Regex EpisodeRegEx = new Regex(@"Episode (\d+)", RegexOptions.Compiled);
|
||||
var EpisodeRegExMatch = EpisodeRegEx.Match(releaseInfo);
|
||||
if (EpisodeRegExMatch.Success)
|
||||
episode = EpisodeRegExMatch.Groups[1].Value;
|
||||
|
||||
releaseInfo = releaseInfo.Replace("Episode ", "");
|
||||
releaseInfo = releaseInfo.Replace("Season ", "S");
|
||||
releaseInfo = releaseInfo.Trim();
|
||||
|
||||
int test = 0;
|
||||
if (InsertSeason && int.TryParse(releaseInfo, out test) && releaseInfo.Length <= 3)
|
||||
{
|
||||
var theName = name.Trim();
|
||||
if (!theName.Contains("&#") && !string.IsNullOrWhiteSpace(theName))
|
||||
releaseInfo = "E0" + releaseInfo;
|
||||
}
|
||||
|
||||
if (FilterSeasonEpisode)
|
||||
{
|
||||
if (query.Season != 0 && season != null && season != query.Season) // skip if season doesn't match
|
||||
continue;
|
||||
if (query.Episode != null && episode != null && episode != query.Episode) // skip if episode doesn't match
|
||||
continue;
|
||||
}
|
||||
var torrentID = (long)torrent["ID"];
|
||||
var Property = (string)torrent["Property"];
|
||||
Property = Property.Replace(" | Freeleech", "");
|
||||
var Link = (string)torrent["Link"];
|
||||
var LinkUri = new Uri(Link);
|
||||
var UploadTimeString = (string)torrent["UploadTime"];
|
||||
var UploadTime = DateTime.ParseExact(UploadTimeString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
var PublushDate = DateTime.SpecifyKind(UploadTime, DateTimeKind.Utc).ToLocalTime();
|
||||
var CommentsLink = TorrentsUrl + "?id=" + groupID.ToString() + "&torrentid=" + torrentID.ToString();
|
||||
var CommentsLinkUri = new Uri(CommentsLink);
|
||||
var Size = (long)torrent["Size"];
|
||||
var Snatched = (long)torrent["Snatched"];
|
||||
var Seeders = (int)torrent["Seeders"];
|
||||
var Leechers = (int)torrent["Leechers"];
|
||||
var FileCount = (long)torrent["FileCount"];
|
||||
var Peers = Seeders + Leechers;
|
||||
|
||||
var RawDownMultiplier = (int?)torrent["RawDownMultiplier"];
|
||||
if (RawDownMultiplier == null)
|
||||
RawDownMultiplier = 0;
|
||||
var RawUpMultiplier = (int?)torrent["RawUpMultiplier"];
|
||||
if (RawUpMultiplier == null)
|
||||
RawDownMultiplier = 0;
|
||||
|
||||
if (searchType == "anime")
|
||||
{
|
||||
if (GroupName == "TV Series")
|
||||
Category = new List<int> { TorznabCatType.TVAnime.ID };
|
||||
|
||||
// Ignore these categories as they'll cause hell with the matcher
|
||||
// TV Special, OVA, ONA, DVD Special, BD Special
|
||||
|
||||
if (GroupName == "Movie")
|
||||
Category = new List<int> { TorznabCatType.Movies.ID };
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
Category = new List<int> { TorznabCatType.BooksComics.ID };
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
Category = new List<int> { TorznabCatType.BooksComics.ID };
|
||||
|
||||
if (category == "Game" || category == "Visual Novel")
|
||||
{
|
||||
synonyms.Add(theName);
|
||||
if (Property.Contains(" PSP "))
|
||||
Category = new List<int> { TorznabCatType.ConsolePSP.ID };
|
||||
if (Property.Contains("PSX"))
|
||||
Category = new List<int> { TorznabCatType.ConsoleOther.ID };
|
||||
if (Property.Contains(" NES "))
|
||||
Category = new List<int> { TorznabCatType.ConsoleOther.ID };
|
||||
if (Property.Contains(" PC "))
|
||||
Category = new List<int> { TorznabCatType.PCGames.ID };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var title in synonyms)
|
||||
{
|
||||
var releaseRows = seriesCq.Find(".torrent_group tr");
|
||||
string episode = null;
|
||||
int? season = null;
|
||||
|
||||
// Skip the first two info rows
|
||||
for (int r = 1; r < releaseRows.Count(); r++)
|
||||
{
|
||||
var row = releaseRows.Get(r);
|
||||
var rowCq = row.Cq();
|
||||
if (rowCq.HasClass("edition_info"))
|
||||
else if (searchType == "music")
|
||||
{
|
||||
episode = null;
|
||||
season = null;
|
||||
releaseInfo = rowCq.Find("td").Text();
|
||||
if (string.IsNullOrWhiteSpace(releaseInfo))
|
||||
if (category == "Single" || category == "EP" || category == "Album" || category == "Compilation" || category == "Soundtrack" || category == "Remix CD" || category == "PV" || category == "Live Album" || category == "Image CD" || category == "Drama CD" || category == "Vocal CD")
|
||||
{
|
||||
// Single episodes alpha - Reported that this info is missing.
|
||||
// It should self correct when availible
|
||||
break;
|
||||
}
|
||||
|
||||
Regex SeasonRegEx = new Regex(@"Season (\d+)", RegexOptions.Compiled);
|
||||
var SeasonRegExMatch = SeasonRegEx.Match(releaseInfo);
|
||||
if (SeasonRegExMatch.Success)
|
||||
season = ParseUtil.CoerceInt(SeasonRegExMatch.Groups[1].Value);
|
||||
|
||||
Regex EpisodeRegEx = new Regex(@"Episode (\d+)", RegexOptions.Compiled);
|
||||
var EpisodeRegExMatch = EpisodeRegEx.Match(releaseInfo);
|
||||
if (EpisodeRegExMatch.Success)
|
||||
episode = EpisodeRegExMatch.Groups[1].Value;
|
||||
|
||||
releaseInfo = releaseInfo.Replace("Episode ", "");
|
||||
releaseInfo = releaseInfo.Replace("Season ", "S");
|
||||
releaseInfo = releaseInfo.Trim();
|
||||
int test = 0;
|
||||
if (InsertSeason && int.TryParse(releaseInfo, out test) && releaseInfo.Length <= 3)
|
||||
{
|
||||
releaseInfo = "E0" + releaseInfo;
|
||||
if (Property.Contains(" Lossless "))
|
||||
Category = new List<int> { TorznabCatType.AudioLossless.ID };
|
||||
else if (Property.Contains("MP3"))
|
||||
Category = new List<int> { TorznabCatType.AudioMP3.ID };
|
||||
else
|
||||
Category = new List<int> { TorznabCatType.AudioOther.ID };
|
||||
}
|
||||
}
|
||||
else if (rowCq.HasClass("torrent"))
|
||||
|
||||
// We dont actually have a release name >.> so try to create one
|
||||
var releaseTags = Property.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
for (int i = releaseTags.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var links = rowCq.Find("a");
|
||||
// Protect against format changes
|
||||
if (links.Count() != 2)
|
||||
releaseTags[i] = releaseTags[i].Trim();
|
||||
if (string.IsNullOrWhiteSpace(releaseTags[i]))
|
||||
releaseTags.RemoveAt(i);
|
||||
}
|
||||
|
||||
var releasegroup = releaseTags.LastOrDefault();
|
||||
if (releasegroup != null && releasegroup.Contains("(") && releasegroup.Contains(")"))
|
||||
{
|
||||
// Skip raws if set
|
||||
if (releasegroup.ToLowerInvariant().StartsWith("raw") && !AllowRaws)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FilterSeasonEpisode)
|
||||
var start = releasegroup.IndexOf("(");
|
||||
releasegroup = "[" + releasegroup.Substring(start + 1, (releasegroup.IndexOf(")") - 1) - start) + "] ";
|
||||
}
|
||||
else
|
||||
{
|
||||
releasegroup = string.Empty;
|
||||
}
|
||||
|
||||
var infoString = "";
|
||||
|
||||
for (int i = 0; i + 1 < releaseTags.Count(); i++)
|
||||
{
|
||||
if (releaseTags[i] == "Raw" && !AllowRaws)
|
||||
continue;
|
||||
infoString += "[" + releaseTags[i] + "]";
|
||||
}
|
||||
|
||||
var MinimumSeedTime = 259200;
|
||||
// Additional 5 hours per GB
|
||||
MinimumSeedTime += (int)((Size / 1000000000) * 18000);
|
||||
|
||||
foreach (var title in synonyms)
|
||||
{
|
||||
string releaseTitle = null;
|
||||
if (GroupName == "Movie")
|
||||
{
|
||||
if (query.Season != 0 && season != null && season != query.Season) // skip if season doesn't match
|
||||
continue;
|
||||
if (query.Episode != null && episode != null && episode != query.Episode) // skip if episode doesn't match
|
||||
continue;
|
||||
releaseTitle = string.Format("{0} {1} {2}{3}", title, Year, releasegroup, infoString);
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseTitle = string.Format("{0}{1} {2} {3}", releasegroup, title, releaseInfo, infoString);
|
||||
}
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 259200;
|
||||
var downloadLink = links.Get(0);
|
||||
release.MinimumSeedTime = MinimumSeedTime;
|
||||
release.Title = releaseTitle;
|
||||
release.Comments = CommentsLinkUri;
|
||||
release.Guid = new Uri(CommentsLinkUri + "&nh=" + StringUtil.Hash(title)); // Sonarr should dedupe on this url - allow a url per name.
|
||||
release.Link = LinkUri;
|
||||
release.BannerUrl = ImageUrl;
|
||||
release.PublishDate = PublushDate;
|
||||
release.Category = Category;
|
||||
release.Description = Description;
|
||||
release.Size = Size;
|
||||
release.Seeders = Seeders;
|
||||
release.Peers = Peers;
|
||||
release.Grabs = Snatched;
|
||||
release.Files = FileCount;
|
||||
release.DownloadVolumeFactor = RawDownMultiplier;
|
||||
release.UploadVolumeFactor = RawUpMultiplier;
|
||||
|
||||
// We dont know this so try to fake based on the release year
|
||||
release.PublishDate = new DateTime(year, 1, 1);
|
||||
release.PublishDate = release.PublishDate.AddDays(Math.Min(DateTime.Now.DayOfYear, 365) - 1);
|
||||
|
||||
var infoLink = links.Get(1);
|
||||
release.Comments = new Uri(SiteLink + infoLink.Attributes.GetAttribute("href"));
|
||||
release.Guid = new Uri(SiteLink + infoLink.Attributes.GetAttribute("href") + "&nh=" + StringUtil.Hash(title)); // Sonarr should dedupe on this url - allow a url per name.
|
||||
release.Link = new Uri(downloadLink.Attributes.GetAttribute("href"));
|
||||
|
||||
string category = null;
|
||||
if (searchType == SearchType.Video)
|
||||
{
|
||||
category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim();
|
||||
if (category == "TV Series")
|
||||
release.Category = new List<int> { TorznabCatType.TVAnime.ID };
|
||||
|
||||
// Ignore these categories as they'll cause hell with the matcher
|
||||
// TV Special, OVA, ONA, DVD Special, BD Special
|
||||
|
||||
if (category == "Movie")
|
||||
release.Category = new List<int> { TorznabCatType.Movies.ID };
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
release.Category = new List<int> { TorznabCatType.BooksComics.ID };
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
release.Category = new List<int> { TorznabCatType.BooksComics.ID };
|
||||
|
||||
if (category == "Game" || category == "Visual Novel")
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" PSP "))
|
||||
release.Category = new List<int> { TorznabCatType.ConsolePSP.ID };
|
||||
if (description.Contains("PSX"))
|
||||
release.Category = new List<int> { TorznabCatType.ConsoleOther.ID };
|
||||
if (description.Contains(" NES "))
|
||||
release.Category = new List<int> { TorznabCatType.ConsoleOther.ID };
|
||||
if (description.Contains(" PC "))
|
||||
release.Category = new List<int> { TorznabCatType.PCGames.ID };
|
||||
}
|
||||
}
|
||||
|
||||
if (searchType == SearchType.Audio)
|
||||
{
|
||||
category = seriesCq.Find(".group_img .cat a").Text();
|
||||
if (category == "Single" || category == "EP" || category == "Album" || category == "Compilation" || category == "Soundtrack" || category == "Remix CD" || category == "PV" || category == "Live Album" || category == "Image CD" || category == "Drama CD" || category == "Vocal CD")
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" Lossless "))
|
||||
release.Category = new List<int> { TorznabCatType.AudioLossless.ID };
|
||||
else if (description.Contains("MP3"))
|
||||
release.Category = new List<int> { TorznabCatType.AudioMP3.ID };
|
||||
else
|
||||
release.Category = new List<int> { TorznabCatType.AudioOther.ID };
|
||||
}
|
||||
}
|
||||
|
||||
// We dont actually have a release name >.> so try to create one
|
||||
var releaseTags = infoLink.InnerText.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
for (int i = releaseTags.Count - 1; i >= 0; i--)
|
||||
{
|
||||
releaseTags[i] = releaseTags[i].Trim();
|
||||
if (string.IsNullOrWhiteSpace(releaseTags[i]))
|
||||
releaseTags.RemoveAt(i);
|
||||
}
|
||||
|
||||
var group = releaseTags.LastOrDefault();
|
||||
if (group != null && group.Contains("(") && group.Contains(")"))
|
||||
{
|
||||
// Skip raws if set
|
||||
if (group.ToLowerInvariant().StartsWith("raw") && !AllowRaws)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var start = group.IndexOf("(");
|
||||
group = "[" + group.Substring(start + 1, (group.IndexOf(")") - 1) - start) + "] ";
|
||||
}
|
||||
else
|
||||
{
|
||||
group = string.Empty;
|
||||
}
|
||||
|
||||
var infoString = "";
|
||||
|
||||
for (int i = 0; i + 1 < releaseTags.Count(); i++)
|
||||
{
|
||||
if (releaseTags[i] == "Raw" && !AllowRaws)
|
||||
continue;
|
||||
infoString += "[" + releaseTags[i] + "]";
|
||||
}
|
||||
|
||||
if (category == "Movie")
|
||||
{
|
||||
release.Title = string.Format("{0} {1} {2}{3}", title, year, group, infoString);
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Title = string.Format("{0}{1} {2} {3}", group, title, releaseInfo, infoString);
|
||||
}
|
||||
release.Description = title;
|
||||
|
||||
var size = rowCq.Find(".torrent_size");
|
||||
if (size.Count() > 0)
|
||||
{
|
||||
release.Size = ReleaseInfo.GetBytes(size.First().Text());
|
||||
}
|
||||
|
||||
// Additional 5 hours per GB
|
||||
release.MinimumSeedTime += (release.Size / 1000000000) * 18000;
|
||||
|
||||
// Peer info
|
||||
release.Seeders = ParseUtil.CoerceInt(rowCq.Find(".torrent_seeders").Text());
|
||||
release.Peers = release.Seeders + ParseUtil.CoerceInt(rowCq.Find(".torrent_leechers").Text());
|
||||
|
||||
// grabs
|
||||
var grabs = rowCq.Find("td.torrent_snatched").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
// freeleech
|
||||
if (rowCq.Find("img[alt=\"Freeleech!\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
//if (release.Category != null)
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -20,21 +20,29 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class BJShare : BaseWebIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string BrowseUrl { get { return SiteLink + "torrents.php"; } }
|
||||
private string TodayUrl { get { return SiteLink + "torrents.php?action=today"; } }
|
||||
private char[] digits = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
|
||||
private new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
private string LoginUrl => SiteLink + "login.php";
|
||||
private string BrowseUrl => SiteLink + "torrents.php";
|
||||
private string TodayUrl => SiteLink + "torrents.php?action=today";
|
||||
private readonly char[] _digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
private readonly Dictionary<string, string> _commonSearchTerms = new Dictionary<string, string>
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
{ "agents of shield", "Agents of S.H.I.E.L.D."}
|
||||
};
|
||||
|
||||
public override string[] LegacySiteLinks { get; protected set; } = new string[] {
|
||||
"https://bj-share.me/"
|
||||
};
|
||||
|
||||
private ConfigurationDataBasicLoginWithRSSAndDisplay ConfigData
|
||||
{
|
||||
get => (ConfigurationDataBasicLoginWithRSSAndDisplay)configData;
|
||||
set => configData = value;
|
||||
}
|
||||
|
||||
public BJShare(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BJ-Share",
|
||||
: base("BJ-Share",
|
||||
description: "A brazilian tracker.",
|
||||
link: "https://bj-share.me/",
|
||||
link: "https://bj-share.info/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
@@ -46,28 +54,28 @@ namespace Jackett.Common.Indexers
|
||||
Language = "pt-br";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping(14, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(3, TorznabCatType.PC0day); // Aplicativos
|
||||
AddCategoryMapping(8, TorznabCatType.Other); // Apostilas/Tutoriais
|
||||
AddCategoryMapping(19, TorznabCatType.AudioAudiobook); // Audiobook
|
||||
AddCategoryMapping(16, TorznabCatType.TVOTHER); // Desenho Animado
|
||||
AddCategoryMapping(18, TorznabCatType.TVDocumentary); // Documentários
|
||||
AddCategoryMapping(10, TorznabCatType.Books); // E-Books
|
||||
AddCategoryMapping(20, TorznabCatType.TVSport); // Esportes
|
||||
AddCategoryMapping(1, TorznabCatType.Movies); // Filmes
|
||||
AddCategoryMapping(12, TorznabCatType.MoviesOther); // Histórias em Quadrinhos
|
||||
AddCategoryMapping(5, TorznabCatType.Audio); // Músicas
|
||||
AddCategoryMapping(7, TorznabCatType.Other); // Outros
|
||||
AddCategoryMapping(9, TorznabCatType.BooksMagazines); // Revistas
|
||||
AddCategoryMapping(2, TorznabCatType.TV); // Seriados
|
||||
AddCategoryMapping(17, TorznabCatType.TV); // Shows
|
||||
AddCategoryMapping(13, TorznabCatType.TV); // Stand Up Comedy
|
||||
AddCategoryMapping(11, TorznabCatType.Other); // Video-Aula
|
||||
AddCategoryMapping(6, TorznabCatType.TV); // Vídeos de TV
|
||||
AddCategoryMapping(4, TorznabCatType.Other); // Jogos
|
||||
AddCategoryMapping(199, TorznabCatType.XXX); // Filmes Adultos
|
||||
AddCategoryMapping(200, TorznabCatType.XXX); // Jogos Adultos
|
||||
AddCategoryMapping(201, TorznabCatType.XXXImageset); // Fotos Adultas
|
||||
AddCategoryMapping(14, TorznabCatType.TVAnime, "Anime");
|
||||
AddCategoryMapping(3, TorznabCatType.PC0day, "Aplicativos");
|
||||
AddCategoryMapping(8, TorznabCatType.Other, "Apostilas/Tutoriais");
|
||||
AddCategoryMapping(19, TorznabCatType.AudioAudiobook, "Audiobook");
|
||||
AddCategoryMapping(16, TorznabCatType.TVOTHER, "Desenho Animado");
|
||||
AddCategoryMapping(18, TorznabCatType.TVDocumentary, "Documentários");
|
||||
AddCategoryMapping(10, TorznabCatType.Books, "E-Books");
|
||||
AddCategoryMapping(20, TorznabCatType.TVSport, "Esportes");
|
||||
AddCategoryMapping(1, TorznabCatType.Movies, "Filmes");
|
||||
AddCategoryMapping(12, TorznabCatType.MoviesOther, "Histórias em Quadrinhos");
|
||||
AddCategoryMapping(5, TorznabCatType.Audio, "Músicas");
|
||||
AddCategoryMapping(7, TorznabCatType.Other, "Outros");
|
||||
AddCategoryMapping(9, TorznabCatType.BooksMagazines, "Revistas");
|
||||
AddCategoryMapping(2, TorznabCatType.TV, "Seriados");
|
||||
AddCategoryMapping(17, TorznabCatType.TV, "Shows");
|
||||
AddCategoryMapping(13, TorznabCatType.TV, "Stand Up Comedy");
|
||||
AddCategoryMapping(11, TorznabCatType.Other, "Video-Aula");
|
||||
AddCategoryMapping(6, TorznabCatType.TV, "Vídeos de TV");
|
||||
AddCategoryMapping(4, TorznabCatType.Other, "Jogos");
|
||||
AddCategoryMapping(199, TorznabCatType.XXX, "Filmes Adultos");
|
||||
AddCategoryMapping(200, TorznabCatType.XXX, "Jogos Adultos");
|
||||
AddCategoryMapping(201, TorznabCatType.XXXImageset, "Fotos Adultas");
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
@@ -76,8 +84,8 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value },
|
||||
{ "keeplogged", "1" }
|
||||
};
|
||||
|
||||
@@ -85,7 +93,7 @@ namespace Jackett.Common.Indexers
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
throw new ExceptionWithConfigData(errorMessage, ConfigData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -94,13 +102,24 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
// 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
|
||||
if (isAnime)
|
||||
{
|
||||
return term.TrimEnd(digits);
|
||||
}
|
||||
|
||||
var ret = Regex.Replace(term, @"[S|E]\d\d", string.Empty).Trim();
|
||||
return ret.Replace("Agents of SHIELD", "Agents of S.H.I.E.L.D.");
|
||||
return isAnime ? term.TrimEnd(_digits) : Regex.Replace(term, @"[S|E]\d\d", string.Empty).Trim();
|
||||
}
|
||||
|
||||
private static string FixAbsoluteNumbering(string title)
|
||||
{
|
||||
// if result is One piece, convert title from SXXEXX to EXX
|
||||
// One piece is the only anime that i'm aware that is in "absolute" numbering, the problem is that they include
|
||||
// the season (wrong season) and episode as absolute, eg: One Piece - S08E836
|
||||
// 836 is the latest episode in absolute numbering, that is correct, but S08 is not the current season...
|
||||
// So for this show, i don't see a other way to make it work...
|
||||
//
|
||||
// All others animes that i tested is with correct season and episode set, so i can't remove the season from all
|
||||
// or will break everything else
|
||||
//
|
||||
// In this indexer, it looks that it is added "automatically", so all current and new releases will be broken
|
||||
// until they or the source from where they get that info fix it...
|
||||
|
||||
return title.Contains("One Piece") ? Regex.Replace(title, @"(Ep[\.]?[ ]?)|([S]\d\d[Ee])", "E") : title;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
@@ -113,51 +132,53 @@ namespace Jackett.Common.Indexers
|
||||
var results = await RequestStringWithCookies(TodayUrl);
|
||||
try
|
||||
{
|
||||
string RowsSelector = "table.torrent_table > tbody > tr:not(tr.colhead)";
|
||||
const string rowsSelector = "table.torrent_table > tbody > tr:not(tr.colhead)";
|
||||
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.Parse(results.Content);
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
|
||||
foreach (var Row in Rows)
|
||||
var searchResultParser = new HtmlParser();
|
||||
var searchResultDocument = searchResultParser.Parse(results.Content);
|
||||
var rows = searchResultDocument.QuerySelectorAll(rowsSelector);
|
||||
foreach (var row in rows)
|
||||
{
|
||||
try
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 0
|
||||
};
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 0;
|
||||
|
||||
var qDetailsLink = Row.QuerySelector("a.BJinfoBox");
|
||||
var qTitle = qDetailsLink.QuerySelector("font");
|
||||
release.Title = qTitle.TextContent;
|
||||
|
||||
var qDetailsLink = row.QuerySelector("a.BJinfoBox");
|
||||
var qBJinfoBox = qDetailsLink.QuerySelector("span");
|
||||
var qCatLink = Row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");
|
||||
var qDLLink = Row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
|
||||
var qSeeders = Row.QuerySelector("td:nth-child(4)");
|
||||
var qLeechers = Row.QuerySelector("td:nth-child(5)");
|
||||
var qQuality = Row.QuerySelector("font[color=\"red\"]");
|
||||
var qFreeLeech = Row.QuerySelector("font[color=\"green\"]:contains(Free)");
|
||||
var qCatLink = row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");
|
||||
var qDlLink = row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
|
||||
var qSeeders = row.QuerySelector("td:nth-child(4)");
|
||||
var qLeechers = row.QuerySelector("td:nth-child(5)");
|
||||
var qQuality = row.QuerySelector("font[color=\"red\"]");
|
||||
var qFreeLeech = row.QuerySelector("font[color=\"green\"]:contains(Free)");
|
||||
var qTitle = qDetailsLink.QuerySelector("font");
|
||||
// Get international title if available, or use the full title if not
|
||||
release.Title = Regex.Replace(qTitle.TextContent, @".* \[(.*?)\](.*)", "$1$2");
|
||||
|
||||
release.Description = "";
|
||||
foreach (var Child in qBJinfoBox.ChildNodes)
|
||||
foreach (var child in qBJinfoBox.ChildNodes)
|
||||
{
|
||||
var type = Child.NodeType;
|
||||
var type = child.NodeType;
|
||||
if (type != NodeType.Text)
|
||||
continue;
|
||||
|
||||
var line = Child.TextContent;
|
||||
var line = child.TextContent;
|
||||
if (line.StartsWith("Tamanho:"))
|
||||
{
|
||||
string Size = line.Substring("Tamanho: ".Length); ;
|
||||
release.Size = ReleaseInfo.GetBytes(Size);
|
||||
var size = line.Substring("Tamanho: ".Length); ;
|
||||
release.Size = ReleaseInfo.GetBytes(size);
|
||||
}
|
||||
else if (line.StartsWith("Lançado em: "))
|
||||
{
|
||||
string PublishDateStr = line.Substring("Lançado em: ".Length).Replace("às ", "");
|
||||
PublishDateStr += " +0";
|
||||
var PublishDate = DateTime.SpecifyKind(DateTime.ParseExact(PublishDateStr, "dd/MM/yyyy HH:mm z", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
release.PublishDate = PublishDate.ToLocalTime();
|
||||
var publishDateStr = line.Substring("Lançado em: ".Length).Replace("às ", "");
|
||||
publishDateStr += " +0";
|
||||
var publishDate = DateTime.SpecifyKind(DateTime.ParseExact(publishDateStr, "dd/MM/yyyy HH:mm z", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
release.PublishDate = publishDate.ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -166,39 +187,36 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
|
||||
var catStr = qCatLink.GetAttribute("href").Split('=')[1];
|
||||
// if result is an anime, convert title from SXXEXX to EXX
|
||||
if (catStr == "14")
|
||||
release.Title = FixAbsoluteNumbering(release.Title);
|
||||
|
||||
var quality = qQuality.TextContent;
|
||||
switch (quality)
|
||||
{
|
||||
release.Title = Regex.Replace(release.Title, @"(Ep[\.]?[ ]?)|([S]\d\d[Ee])", "E");
|
||||
case "Full HD":
|
||||
release.Title += " 1080p";
|
||||
break;
|
||||
case "HD":
|
||||
release.Title += " 720p";
|
||||
break;
|
||||
default:
|
||||
release.Title += " 480p";
|
||||
break;
|
||||
}
|
||||
|
||||
var Quality = qQuality.TextContent;
|
||||
if (Quality == "Full HD")
|
||||
release.Title += " 1080p";
|
||||
else if(Quality == "HD")
|
||||
release.Title += " 720p";
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.GetAttribute("href"));
|
||||
release.Link = new Uri(SiteLink + qDlLink.GetAttribute("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.TextContent) + release.Seeders;
|
||||
|
||||
if (qFreeLeech != null)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.DownloadVolumeFactor = qFreeLeech != null ? 0 : 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(string.Format("{0}: Error while parsing row '{1}': {2}", ID, Row.OuterHtml, ex.Message));
|
||||
logger.Error($"{ID}: Error while parsing row '{row.OuterHtml}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,10 +229,13 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
var searchUrl = BrowseUrl;
|
||||
var isSearchAnime = query.Categories.Any(s => s == TorznabCatType.TVAnime.ID);
|
||||
|
||||
query.SearchTerm = query.SearchTerm.Replace("Agents of SHIELD", "Agents of S.H.I.E.L.D.");
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
foreach (var searchTerm in _commonSearchTerms)
|
||||
{
|
||||
query.SearchTerm = query.SearchTerm.ToLower().Replace(searchTerm.Key.ToLower(), searchTerm.Value);
|
||||
}
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var queryCollection = new NameValueCollection
|
||||
{
|
||||
{"searchstr", StripSearchString(searchString, isSearchAnime)},
|
||||
@@ -235,132 +256,134 @@ namespace Jackett.Common.Indexers
|
||||
var results = await RequestStringWithCookies(searchUrl);
|
||||
try
|
||||
{
|
||||
string RowsSelector = "table.torrent_table > tbody > tr:not(tr.colhead)";
|
||||
const string rowsSelector = "table.torrent_table > tbody > tr:not(tr.colhead)";
|
||||
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var SearchResultDocument = SearchResultParser.Parse(results.Content);
|
||||
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
|
||||
var searchResultParser = new HtmlParser();
|
||||
var searchResultDocument = searchResultParser.Parse(results.Content);
|
||||
var rows = searchResultDocument.QuerySelectorAll(rowsSelector);
|
||||
|
||||
ICollection<int> GroupCategory = null;
|
||||
string GroupTitle = null;
|
||||
string GroupYearStr = null;
|
||||
Nullable<DateTime> GroupPublishDate = null;
|
||||
ICollection<int> groupCategory = null;
|
||||
string groupTitle = null;
|
||||
string groupYearStr = null;
|
||||
var categoryStr = "";
|
||||
|
||||
foreach (var Row in Rows)
|
||||
foreach (var row in rows)
|
||||
{
|
||||
try
|
||||
{
|
||||
var qDetailsLink = Row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
string Title = qDetailsLink.TextContent;
|
||||
ICollection<int> Category = null;
|
||||
string YearStr = null;
|
||||
Nullable<DateTime> YearPublishDate = null;
|
||||
string CategoryStr = "";
|
||||
var qDetailsLink = row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
var title = qDetailsLink.TextContent;
|
||||
ICollection<int> category = null;
|
||||
string yearStr = null;
|
||||
|
||||
|
||||
if (Row.ClassList.Contains("group") || Row.ClassList.Contains("torrent")) // group/ungrouped headers
|
||||
if (row.ClassList.Contains("group") || row.ClassList.Contains("torrent")) // group/ungrouped headers
|
||||
{
|
||||
var qCatLink = Row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");
|
||||
CategoryStr = qCatLink.GetAttribute("href").Split('=')[1].Split('&')[0];
|
||||
Category = MapTrackerCatToNewznab(CategoryStr);
|
||||
var qCatLink = row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");
|
||||
categoryStr = qCatLink.GetAttribute("href").Split('=')[1].Split('&')[0];
|
||||
category = MapTrackerCatToNewznab(categoryStr);
|
||||
|
||||
YearStr = qDetailsLink.NextSibling.TextContent.Trim().TrimStart('[').TrimEnd(']');
|
||||
YearPublishDate = DateTime.SpecifyKind(DateTime.ParseExact(YearStr, "yyyy", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
yearStr = qDetailsLink.NextSibling.TextContent.Trim().TrimStart('[').TrimEnd(']');
|
||||
|
||||
// if result is an anime, convert title from SXXEXX to EXX
|
||||
if (CategoryStr == "14")
|
||||
{
|
||||
Title = Regex.Replace(Title, @"(Ep[\.]?[ ]?)|([S]\d\d[Ee])", "E");
|
||||
}
|
||||
title = FixAbsoluteNumbering(title);
|
||||
|
||||
if (Row.ClassList.Contains("group")) // group headers
|
||||
if (row.ClassList.Contains("group")) // group headers
|
||||
{
|
||||
GroupCategory = Category;
|
||||
GroupTitle = Title;
|
||||
GroupYearStr = YearStr;
|
||||
GroupPublishDate = YearPublishDate;
|
||||
groupCategory = category;
|
||||
groupTitle = title;
|
||||
groupYearStr = yearStr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 0;
|
||||
|
||||
var qDLLink = Row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
|
||||
var qSize = Row.QuerySelector("td:nth-last-child(4)");
|
||||
var qGrabs = Row.QuerySelector("td:nth-last-child(3)");
|
||||
var qSeeders = Row.QuerySelector("td:nth-last-child(2)");
|
||||
var qLeechers = Row.QuerySelector("td:nth-last-child(1)");
|
||||
var qFreeLeech = Row.QuerySelector("strong[title=\"Free\"]");
|
||||
|
||||
if (Row.ClassList.Contains("group_torrent")) // torrents belonging to a group
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
release.Description = qDetailsLink.TextContent;
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 0
|
||||
};
|
||||
|
||||
string cleanTitle = Regex.Replace(GroupTitle, @" - S?(?<season>\d{1,2})?E?(?<episode>\d{1,4})?", "");
|
||||
string seasonEp = Regex.Replace(GroupTitle, @"^(.*?) - (S?(\d{1,2})?E?(\d{1,4})?)?", "$2");
|
||||
release.Title = CategoryStr == "14" ? GroupTitle : cleanTitle + " " + GroupYearStr + " " + seasonEp;
|
||||
var qDlLink = row.QuerySelector("a[href^=\"torrents.php?action=download\"]");
|
||||
var qSize = row.QuerySelector("td:nth-last-child(4)");
|
||||
var qGrabs = row.QuerySelector("td:nth-last-child(3)");
|
||||
var qSeeders = row.QuerySelector("td:nth-last-child(2)");
|
||||
var qLeechers = row.QuerySelector("td:nth-last-child(1)");
|
||||
var qFreeLeech = row.QuerySelector("strong[title=\"Free\"]");
|
||||
|
||||
release.PublishDate = GroupPublishDate.Value;
|
||||
release.Category = GroupCategory;
|
||||
if (row.ClassList.Contains("group_torrent")) // torrents belonging to a group
|
||||
{
|
||||
var description = Regex.Replace(qDetailsLink.TextContent.Trim(), @"\s+", " ");
|
||||
description = Regex.Replace(description, @"((S\d{2})(E\d{2,4})?) (.*)", "$4");
|
||||
release.Description = description;
|
||||
|
||||
var cleanTitle = Regex.Replace(groupTitle, @" - ((S(\d{2}))?E(\d{1,4}))", "");
|
||||
title = Regex.Replace(title.Trim(), @"\s+", " ");
|
||||
var seasonEp = Regex.Replace(title, @"((S\d{2})?(E\d{2,4})?) .*", "$1");
|
||||
|
||||
// do not include year to animes
|
||||
if (categoryStr == "14")
|
||||
{
|
||||
release.Title = cleanTitle + " " + seasonEp;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Title = cleanTitle + " " + groupYearStr + " " + seasonEp;
|
||||
}
|
||||
release.Category = groupCategory;
|
||||
}
|
||||
else if (Row.ClassList.Contains("torrent")) // standalone/un grouped torrents
|
||||
else if (row.ClassList.Contains("torrent")) // standalone/un grouped torrents
|
||||
{
|
||||
var qDescription = Row.QuerySelector("div.torrent_info");
|
||||
var qDescription = row.QuerySelector("div.torrent_info");
|
||||
release.Description = qDescription.TextContent;
|
||||
|
||||
string cleanTitle = Regex.Replace(Title, @" - ((S(\d{1,2}))?E(\d{1,4}))", "");
|
||||
string seasonEp = Regex.Replace(Title, @"^(.*?) - ((S(\d{1,2}))?E(\d{1,4}))", "$2");
|
||||
release.Title = CategoryStr == "14" ? Title : cleanTitle + " " + YearStr + " " + seasonEp;
|
||||
var cleanTitle = Regex.Replace(title, @" - ((S\d{2})?(E\d{2,4})?)", "");
|
||||
var seasonEp = Regex.Replace(title, @"^(.*?) - ((S\d{2})?(E\d{2,4})?)", "$2");
|
||||
|
||||
release.PublishDate = YearPublishDate.Value;
|
||||
release.Category = Category;
|
||||
// do not include year to animes
|
||||
if (categoryStr == "14")
|
||||
{
|
||||
release.Title = cleanTitle + " " + seasonEp;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Title = cleanTitle + " " + yearStr + " " + seasonEp;
|
||||
}
|
||||
|
||||
release.Category = category;
|
||||
}
|
||||
|
||||
release.Description = release.Description.Replace(" / Free", ""); // Remove Free Tag
|
||||
|
||||
release.Description = release.Description.Replace("Full HD", "1080p");
|
||||
release.Description = release.Description.Replace("/ HD / ", "/ 720p /");
|
||||
release.Description = release.Description.Replace(" / HD]", " / 720p]");
|
||||
release.Description = release.Description.Replace("4K", "2160p");
|
||||
|
||||
int nBarra = release.Title.IndexOf("[");
|
||||
if (nBarra != -1)
|
||||
{
|
||||
release.Title = release.Title.Substring(nBarra + 1);
|
||||
release.Title = release.Title.Replace("]", "");
|
||||
}
|
||||
|
||||
// Get international title if available, or use the full title if not
|
||||
release.Title = Regex.Replace(release.Title, @".* \[(.*?)\](.*)", "$1$2");
|
||||
release.Title += " " + release.Description; // add year and Description to the release Title to add some meaning to it
|
||||
|
||||
// This tracker does not provide an publish date to search terms (only on last 24h page)
|
||||
release.PublishDate = DateTime.Today;
|
||||
|
||||
// check for previously stripped search terms
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
var Size = qSize.TextContent;
|
||||
release.Size = ReleaseInfo.GetBytes(Size);
|
||||
|
||||
release.Link = new Uri(SiteLink + qDLLink.GetAttribute("href"));
|
||||
var size = qSize.TextContent;
|
||||
release.Size = ReleaseInfo.GetBytes(size);
|
||||
release.Link = new Uri(SiteLink + qDlLink.GetAttribute("href"));
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
release.Grabs = ParseUtil.CoerceLong(qGrabs.TextContent);
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.TextContent);
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.TextContent) + release.Seeders;
|
||||
|
||||
if (qFreeLeech != null)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.DownloadVolumeFactor = qFreeLeech != null ? 0 : 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(string.Format("{0}: Error while parsing row '{1}': {2}", ID, Row.OuterHtml, ex.Message));
|
||||
logger.Error($"{ID}: Error while parsing row '{row.OuterHtml}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using AutoMapper;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
@@ -12,6 +7,13 @@ using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static Jackett.Common.Models.IndexerConfig.ConfigurationData;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
@@ -32,6 +34,9 @@ namespace Jackett.Common.Indexers
|
||||
public string Type { get; protected set; }
|
||||
public virtual string ID { get { return GetIndexerID(GetType()); } }
|
||||
|
||||
[JsonConverter(typeof(EncodingJsonConverter))]
|
||||
public Encoding Encoding { get; protected set; }
|
||||
|
||||
public virtual bool IsConfigured { get; protected set; }
|
||||
protected Logger logger;
|
||||
protected IIndexerConfigurationService configurationService;
|
||||
@@ -154,8 +159,11 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
if (jsonConfig is JArray)
|
||||
{
|
||||
LoadValuesFromJson(jsonConfig, true);
|
||||
IsConfigured = true;
|
||||
if (!MigratedFromDPAPI(jsonConfig))
|
||||
{
|
||||
LoadValuesFromJson(jsonConfig, true);
|
||||
IsConfigured = true;
|
||||
}
|
||||
}
|
||||
// read and upgrade old settings file format
|
||||
else if (jsonConfig is Object)
|
||||
@@ -166,6 +174,95 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Remove this section once users have moved off DPAPI
|
||||
private bool MigratedFromDPAPI(JToken jsonConfig)
|
||||
{
|
||||
if (EnvironmentUtil.IsRunningLegacyOwin)
|
||||
{
|
||||
//Still running legacy Owin and using the DPAPI, we don't want to migrate
|
||||
logger.Debug(ID + " - Running Owin, no need to migrate from DPAPI");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runningOnDotNetCore = RuntimeInformation.FrameworkDescription.IndexOf("core", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
bool isWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
|
||||
if (!isWindows && runningOnDotNetCore)
|
||||
{
|
||||
// User isn't running Windows, but is running on .NET Core framework, no access to the DPAPI, so don't bother trying to migrate
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadValuesFromJson(jsonConfig, false);
|
||||
|
||||
StringItem passwordPropertyValue = null;
|
||||
string passwordValue = "";
|
||||
|
||||
try
|
||||
{
|
||||
// try dynamic items first (e.g. all cardigann indexers)
|
||||
passwordPropertyValue = (StringItem)configData.GetDynamicByName("password");
|
||||
|
||||
if (passwordPropertyValue == null) // if there's no dynamic password try the static property
|
||||
{
|
||||
passwordPropertyValue = (StringItem)configData.GetType().GetProperty("Password").GetValue(configData, null);
|
||||
|
||||
// protection is based on the item.Name value (property name might be different, example: Abnormal), so check the Name again
|
||||
if (!string.Equals(passwordPropertyValue.Name, "password", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
logger.Debug($"Skipping non default password property (unencrpyted password) for [{ID}] while attempting migration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
passwordValue = passwordPropertyValue.Value;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
logger.Debug($"Unable to source password for [{ID}] while attempting migration, likely a tracker without a password setting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(passwordValue))
|
||||
{
|
||||
try
|
||||
{
|
||||
protectionService.UnProtect(passwordValue);
|
||||
//Password successfully unprotected using Microsoft.AspNetCore.DataProtection, no further action needed as we've already converted the password previously
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.Message != "The provided payload cannot be decrypted because it was not protected with this protection provider.")
|
||||
{
|
||||
logger.Info($"Password could not be unprotected using Microsoft.AspNetCore.DataProtection - {ID} : " + ex);
|
||||
}
|
||||
|
||||
logger.Info($"Attempting legacy Unprotect - {ID} : ");
|
||||
|
||||
try
|
||||
{
|
||||
string unprotectedPassword = protectionService.LegacyUnProtect(passwordValue);
|
||||
//Password successfully unprotected using Windows/Mono DPAPI
|
||||
|
||||
passwordPropertyValue.Value = unprotectedPassword;
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
|
||||
logger.Info($"Password successfully migrated for {ID}");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.Info($"Password could not be unprotected using legacy DPAPI - {ID} : " + exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async Task ConfigureIfOK(string cookies, bool isLoggedin, Func<Task> onError)
|
||||
{
|
||||
if (isLoggedin)
|
||||
@@ -205,7 +302,10 @@ namespace Jackett.Common.Indexers
|
||||
if (query.HasSpecifiedCategories)
|
||||
if (!caps.SupportsCategories(query.Categories))
|
||||
return false;
|
||||
|
||||
if (caps.SupportsImdbSearch && query.IsImdbQuery)
|
||||
return true;
|
||||
else if (!caps.SupportsImdbSearch && query.IsImdbQuery && query.QueryType != "TorrentPotato") // potato query should always contain imdb+search term
|
||||
return false;
|
||||
if (caps.SearchAvailable && query.IsSearch)
|
||||
return true;
|
||||
if (caps.TVSearchAvailable && query.IsTVSearch)
|
||||
@@ -239,6 +339,10 @@ namespace Jackett.Common.Indexers
|
||||
return new IndexerResult(this, new ReleaseInfo[0]);
|
||||
var results = await PerformQuery(query);
|
||||
results = FilterResults(query, results);
|
||||
if (query.Limit > 0)
|
||||
{
|
||||
results = results.Take(query.Limit);
|
||||
}
|
||||
results = results.Select(r =>
|
||||
{
|
||||
r.Origin = this;
|
||||
@@ -748,9 +852,6 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
public override TorznabCapabilities TorznabCaps { get; protected set; }
|
||||
|
||||
[JsonConverter(typeof(EncodingJsonConverter))]
|
||||
public Encoding Encoding { get; protected set; }
|
||||
|
||||
private List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
|
||||
protected WebClient webclient;
|
||||
protected readonly string downloadUrlBase = "";
|
||||
|
@@ -43,45 +43,46 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
configData.DisplayText.Value = "Go to the general tab of your BeyondHD user profile and create/copy the Login Link.";
|
||||
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesBluRay); // Movie / Blu-ray
|
||||
AddMultiCategoryMapping(TorznabCatType.Movies3D,
|
||||
71, // Movie / 3D
|
||||
83 // FraMeSToR 3D
|
||||
);
|
||||
AddMultiCategoryMapping(TorznabCatType.MoviesHD,
|
||||
77, // Movie / 1080p/i
|
||||
94, // Movie / 4K
|
||||
78, // Movie / 720p
|
||||
54, // Movie / MP4
|
||||
17, // Movie / Remux
|
||||
50, // Internal / FraMeSToR 1080p
|
||||
75, // Internal / FraMeSToR 720p
|
||||
49, // Internal / FraMeSToR REMUX
|
||||
101, // Internal / FraMeSToR 4K REMUX
|
||||
61, // Internal / HDX REMUX
|
||||
86, // Internal / SC4R
|
||||
95, // Nightripper 1080p
|
||||
96, // Nightripper 720p
|
||||
98 // Nightripper MicroHD
|
||||
);
|
||||
AddCategoryMapping(37, TorznabCatType.MoviesBluRay, "Movie / Blu-ray");
|
||||
AddCategoryMapping(71, TorznabCatType.Movies3D, "Movie / 3D");
|
||||
AddCategoryMapping(83, TorznabCatType.Movies3D, "FraMeSToR 3D");
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.TVHD,
|
||||
40, // TV Show / Blu-ray
|
||||
44, // TV Show / Encodes
|
||||
48, // TV Show / HDTV
|
||||
89, // TV Show / Packs
|
||||
46, // TV Show / Remux
|
||||
45, // TV Show / WEB-DL
|
||||
97 // Nightripper TV Show Encodes
|
||||
);
|
||||
AddCategoryMapping(77, TorznabCatType.MoviesHD, "Movie / 1080p/i");
|
||||
AddCategoryMapping(102, TorznabCatType.MoviesUHD, "Movie / 4K Disk");
|
||||
AddCategoryMapping(94, TorznabCatType.MoviesUHD, "Movie / 4K Other");
|
||||
AddCategoryMapping(103, TorznabCatType.MoviesUHD, "Movie / 4K Remux");
|
||||
AddCategoryMapping(78, TorznabCatType.MoviesHD, "Movie / 720p");
|
||||
AddCategoryMapping(54, TorznabCatType.MoviesHD, "Movie / MP4");
|
||||
AddCategoryMapping(17, TorznabCatType.MoviesHD, "Movie / Remux");
|
||||
AddCategoryMapping(38, TorznabCatType.MoviesHD, "Movie / WEB-DL");
|
||||
AddCategoryMapping(106, TorznabCatType.MoviesHD, "Internal / BHDStudio 1080p");
|
||||
AddCategoryMapping(105, TorznabCatType.MoviesHD, "Internal / BHDStudio 720p");
|
||||
AddCategoryMapping(50, TorznabCatType.MoviesHD, "Internal / FraMeSToR 1080p");
|
||||
AddCategoryMapping(75, TorznabCatType.MoviesHD, "Internal / FraMeSToR 720p");
|
||||
AddCategoryMapping(49, TorznabCatType.MoviesHD, "Internal / FraMeSToR REMUX");
|
||||
AddCategoryMapping(101, TorznabCatType.MoviesHD, "Internal / FraMeSToR 4K REMUX");
|
||||
AddCategoryMapping(61, TorznabCatType.MoviesHD, "Internal / HDX REMUX");
|
||||
AddCategoryMapping(86, TorznabCatType.MoviesHD, "Internal / SC4R");
|
||||
AddCategoryMapping(95, TorznabCatType.MoviesHD, "Nightripper 1080p");
|
||||
AddCategoryMapping(96, TorznabCatType.MoviesHD, "Nightripper 720p");
|
||||
AddCategoryMapping(98, TorznabCatType.MoviesHD, "Nightripper MicroHD");
|
||||
|
||||
AddCategoryMapping(36, TorznabCatType.AudioLossless); // Music / Lossless
|
||||
AddCategoryMapping(69, TorznabCatType.AudioMP3); // Music / MP3
|
||||
AddMultiCategoryMapping(TorznabCatType.AudioVideo,
|
||||
55, // Music / 1080p/i
|
||||
56, // Music / 720p
|
||||
42 // Music / Blu-ray
|
||||
);
|
||||
AddCategoryMapping(104, TorznabCatType.TVUHD, "TV Show / 4K");
|
||||
AddCategoryMapping(40, TorznabCatType.TVHD, "TV Show / Blu-ray");
|
||||
AddCategoryMapping(44, TorznabCatType.TVHD, "TV Show / Encodes");
|
||||
AddCategoryMapping(48, TorznabCatType.TVHD, "TV Show / HDTV");
|
||||
AddCategoryMapping(89, TorznabCatType.TVHD, "TV Show / Packs");
|
||||
AddCategoryMapping(46, TorznabCatType.TVHD, "TV Show / Remux");
|
||||
AddCategoryMapping(99, TorznabCatType.TVHD, "TV Show / Sports");
|
||||
AddCategoryMapping(100, TorznabCatType.TVHD, "TV Show / Sports / WEB-DL");
|
||||
AddCategoryMapping(45, TorznabCatType.TVHD, "TV Show / WEB-DL");
|
||||
AddCategoryMapping(97, TorznabCatType.TVHD, "Nightripper TV Show Encodes");
|
||||
|
||||
AddCategoryMapping(36, TorznabCatType.AudioLossless, "Music / Lossless");
|
||||
AddCategoryMapping(69, TorznabCatType.AudioMP3, "Music / MP3");
|
||||
AddCategoryMapping(55, TorznabCatType.AudioVideo, "Music / 1080p/i");
|
||||
AddCategoryMapping(56, TorznabCatType.AudioVideo, "Music / 720p");
|
||||
AddCategoryMapping(42, TorznabCatType.AudioVideo, "Music / Blu-ray");
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
|
@@ -17,7 +17,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class BitCityReloaded : BaseWebIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "login/index.php"; } }
|
||||
private string BrowseUrl { get { return SiteLink + "uebersicht.php"; } }
|
||||
private TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "W. Europe Standard Time", "W. Europe Standard Time");
|
||||
|
||||
|
@@ -197,6 +197,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
Dictionary<string, object> variables = new Dictionary<string, object>();
|
||||
|
||||
variables[".Config.sitelink"] = SiteLink;
|
||||
foreach (settingsField Setting in Definition.Settings)
|
||||
{
|
||||
string value;
|
||||
@@ -363,6 +364,9 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
protected bool checkForError(WebClientStringResult loginResult, IList<errorBlock> errorBlocks)
|
||||
{
|
||||
if(loginResult.Status == HttpStatusCode.Unauthorized) // e.g. used by YGGtorrent
|
||||
throw new ExceptionWithConfigData("401 Unauthorized, check your credentials", configData);
|
||||
|
||||
if (errorBlocks == null)
|
||||
return true; // no error
|
||||
|
||||
|
@@ -40,7 +40,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
Type = "public";
|
||||
|
||||
AddCategoryMapping(5, TorznabCatType.PC0day, "Applications");
|
||||
AddCategoryMapping(17, TorznabCatType.AudioAudiobook, "Audio Books");
|
||||
@@ -135,6 +135,11 @@ namespace Jackett.Common.Indexers
|
||||
var episodeSearchUrl = string.Format(SearchUrl, cat, WebUtility.UrlEncode(query.GetQueryString()));
|
||||
var results = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Unexpected redirect to " + results.RedirectingTo + ". Check your credentials.", configData);
|
||||
}
|
||||
|
||||
if (results.Content.Contains("No torrents found"))
|
||||
{
|
||||
return releases;
|
||||
@@ -199,17 +204,15 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
release.Comments = new Uri(new Uri(SiteLink), qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
release.Link = release.Comments; // indirect download see Download() method
|
||||
|
||||
var qDownload = rowB.ChildElements.ElementAt(2).ChildElements.ElementAt(0).Cq();
|
||||
release.Link = new Uri(qDownload.Attr("href"));
|
||||
|
||||
var sizeStr = rowB.ChildElements.ElementAt(3).Cq().Text();
|
||||
var sizeStr = rowB.ChildElements.ElementAt(2).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text());
|
||||
release.Seeders = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(5).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(rowB.ChildElements.ElementAt(6).Cq().Text()) + release.Seeders;
|
||||
|
||||
var grabs = rowB.Cq().Find("td:nth-child(6)").Text();
|
||||
var grabs = rowB.Cq().Find("td:nth-child(5)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
@@ -224,5 +227,19 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var results = await RequestStringWithCookies(link.AbsoluteUri);
|
||||
//await FollowIfRedirect(results); // manual follow for better debugging (string)
|
||||
if (results.IsRedirect)
|
||||
results = await RequestStringWithCookies(results.RedirectingTo);
|
||||
CQ dom = results.Content;
|
||||
var dl = dom.Find("a:has(font:contains(\"Download torrent file\"))");
|
||||
|
||||
link = new Uri(dl.Attr("href"));
|
||||
|
||||
return await base.Download(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
@@ -22,10 +23,11 @@ namespace Jackett.Common.Indexers
|
||||
{ get { return SiteLink + "takelogin.php"; } }
|
||||
private string BrowseUrl
|
||||
{ get { return SiteLink + "browse.php"; } }
|
||||
private bool TorrentHTTPSMode => configData.TorrentHTTPSMode.Value;
|
||||
|
||||
private new ConfigurationDataBasicLogin configData
|
||||
private new ConfigurationDataEliteTracker configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
get { return (ConfigurationDataEliteTracker)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -37,10 +39,10 @@ namespace Jackett.Common.Indexers
|
||||
logger: logger,
|
||||
p: protectionService,
|
||||
client: webClient,
|
||||
configData: new ConfigurationDataBasicLogin()
|
||||
configData: new ConfigurationDataEliteTracker()
|
||||
)
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "fr-fr";
|
||||
Type = "private";
|
||||
|
||||
@@ -206,6 +208,13 @@ Encoding = Encoding.UTF8;
|
||||
release.Peers = ParseUtil.CoerceInt(Leechers.TextContent) + release.Seeders;
|
||||
release.Grabs = ParseUtil.CoerceLong(Grabs.TextContent);
|
||||
|
||||
if (TorrentHTTPSMode)
|
||||
{
|
||||
var linkHttps = Row.QuerySelector("td:nth-child(4)").QuerySelector("a").GetAttribute("href");
|
||||
var idTorrent = ParseUtil.GetArgumentFromQueryString(linkHttps, "id");
|
||||
release.Link = new Uri($"{SiteLink}download.php?id={idTorrent}&type=ssl");
|
||||
}
|
||||
|
||||
if (added.QuerySelector("img[alt^=\"TORRENT GRATUIT\"]") != null)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (added.QuerySelector("img[alt^=\"TORRENT SILVER\"]") != null)
|
||||
|
@@ -19,6 +19,10 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class FileList : BaseWebIndexer
|
||||
{
|
||||
public override string[] LegacySiteLinks { get; protected set; } = new string[] {
|
||||
"http://filelist.ro/",
|
||||
};
|
||||
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
@@ -31,7 +35,7 @@ namespace Jackett.Common.Indexers
|
||||
public FileList(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "FileList",
|
||||
description: "The best Romanian site.",
|
||||
link: "http://filelist.ro/",
|
||||
link: "https://filelist.ro/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
|
@@ -23,9 +23,9 @@ namespace Jackett.Common.Indexers
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private const int MAXPAGES = 3;
|
||||
|
||||
private new ConfigurationDataBasicLogin configData
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Jackett.Common.Indexers
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
{
|
||||
Encoding = Encoding.GetEncoding("windows-1255");
|
||||
Language = "he-il";
|
||||
@@ -95,9 +95,57 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(76, TorznabCatType.TV, "סדרות");
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var captcha = cq.Find(".g-recaptcha"); // invisible recaptcha
|
||||
if (captcha.Any())
|
||||
{
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = captcha.Attr("data-sitekey");
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = new ConfigurationDataBasicLogin();
|
||||
result.SiteLink.Value = configData.SiteLink.Value;
|
||||
result.Instructions.Value = configData.Instructions.Value;
|
||||
result.Username.Value = configData.Username.Value;
|
||||
result.Password.Value = configData.Password.Value;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
|
@@ -1,245 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CsQuery;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
//
|
||||
// Quick and dirty indexer for GFTracker.
|
||||
//
|
||||
public class GFTracker : BaseWebIndexer
|
||||
{
|
||||
private string StartPageUrl { get { return SiteLink + "login.php?returnto=%2F"; } }
|
||||
private string LoginUrl { get { return SiteLink + "loginsite.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public GFTracker(IIndexerConfigurationService configService, WebClient w, Logger l, IProtectionService ps)
|
||||
: base(name: "GFTracker",
|
||||
description: "Home of user happiness",
|
||||
link: "https://www.thegft.org/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
configService: configService,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping(2, TorznabCatType.PC0day, "0DAY");
|
||||
AddCategoryMapping(16, TorznabCatType.TVAnime, "Anime");
|
||||
AddCategoryMapping(1, TorznabCatType.PC0day, "APPS");
|
||||
AddCategoryMapping(9, TorznabCatType.Other, "E-Learning");
|
||||
AddCategoryMapping(35, TorznabCatType.TVFOREIGN, "Foreign");
|
||||
AddCategoryMapping(32, TorznabCatType.ConsoleNDS, "Games/NDS");
|
||||
AddCategoryMapping(6, TorznabCatType.PCGames, "Games/PC");
|
||||
AddCategoryMapping(36, TorznabCatType.ConsolePS4, "Games/Playstation");
|
||||
AddCategoryMapping(29, TorznabCatType.ConsolePSP, "Games/PSP");
|
||||
AddCategoryMapping(23, TorznabCatType.ConsoleWii, "Games/WII");
|
||||
AddCategoryMapping(12, TorznabCatType.ConsoleXbox, "Games/XBOX");
|
||||
AddCategoryMapping(11, TorznabCatType.Other, "Misc");
|
||||
AddCategoryMapping(48, TorznabCatType.MoviesBluRay, "Movies/BLURAY");
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesDVD, "Movies/DVDR");
|
||||
AddCategoryMapping(18, TorznabCatType.MoviesHD, "Movies/X264-HD");
|
||||
AddCategoryMapping(49, TorznabCatType.MoviesSD, "Movies/X264-SD");
|
||||
AddCategoryMapping(7, TorznabCatType.MoviesSD, "Movies/XVID");
|
||||
AddCategoryMapping(38, TorznabCatType.AudioOther, "Music/DVDR");
|
||||
AddCategoryMapping(46, TorznabCatType.AudioLossless, "Music/FLAC");
|
||||
AddCategoryMapping(5, TorznabCatType.AudioMP3, "Music/MP3");
|
||||
AddCategoryMapping(13, TorznabCatType.AudioVideo, "Music/Vids");
|
||||
AddCategoryMapping(26, TorznabCatType.TVHD, "TV/BLURAY");
|
||||
AddCategoryMapping(37, TorznabCatType.TVSD, "TV/DVDR");
|
||||
AddCategoryMapping(19, TorznabCatType.TVSD, "TV/DVDRIP");
|
||||
AddCategoryMapping(47, TorznabCatType.TVSD, "TV/SD");
|
||||
AddCategoryMapping(17, TorznabCatType.TVHD, "TV/X264");
|
||||
AddCategoryMapping(4, TorznabCatType.TVSD, "TV/XVID");
|
||||
AddCategoryMapping(22, TorznabCatType.XXX, "XXX/0DAY");
|
||||
AddCategoryMapping(25, TorznabCatType.XXXDVD, "XXX/DVDR");
|
||||
AddCategoryMapping(20, TorznabCatType.XXX, "XXX/HD");
|
||||
AddCategoryMapping(3, TorznabCatType.XXXXviD, "XXX/XVID");
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(StartPageUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var result = this.configData;
|
||||
CQ recaptcha = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
if (recaptcha.Length != 0) // recaptcha not always present in login form, perhaps based on cloudflare uid or just phase of the moon
|
||||
{
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = cq.Find(".g-recaptcha").Attr("data-sitekey");
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var stdResult = new ConfigurationDataBasicLogin();
|
||||
stdResult.SiteLink.Value = configData.SiteLink.Value;
|
||||
stdResult.Username.Value = configData.Username.Value;
|
||||
stdResult.Password.Value = configData.Password.Value;
|
||||
stdResult.CookieHeader.Value = loginPage.Cookies;
|
||||
return stdResult;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, configData.CookieHeader.Value, true, SearchUrl, StartPageUrl);
|
||||
UpdateCookieHeader(response.Cookies);
|
||||
UpdateCookieHeader("mybbuser=;"); // add dummy cookie, otherwise we get logged out after each request
|
||||
|
||||
await ConfigureIfOK(configData.CookieHeader.Value, response.Content != null && response.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var messageEl = dom["div:has(h2)"].Last();
|
||||
messageEl.Children("a").Remove();
|
||||
messageEl.Children("style").Remove();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
IsConfigured = false;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
// search in normal + gems view
|
||||
foreach (var view in new string[] { "0", "1" })
|
||||
{
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
queryCollection.Add("view", view);
|
||||
queryCollection.Add("searchtype", "1");
|
||||
queryCollection.Add("incldead", "1");
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
queryCollection.Add("search", searchString);
|
||||
}
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add(string.Format("c{0}", cat), "1");
|
||||
}
|
||||
|
||||
var searchUrl = SearchUrl + "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
// re-login
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
var rows = dom["#torrentBrowse > table > tbody > tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
CQ qRow = row.Cq();
|
||||
|
||||
release.MinimumRatio = 0;
|
||||
release.MinimumSeedTime = 2 * 24 * 60 * 60;
|
||||
|
||||
var qLink = qRow.Find("a[title][href^=\"details.php?id=\"]");
|
||||
release.Title = qLink.Attr("title");
|
||||
release.Guid = new Uri(SiteLink + qLink.Attr("href").TrimStart('/'));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
qLink = qRow.Children().ElementAt(3).Cq().Children("a").First();
|
||||
release.Link = new Uri(string.Format("{0}{1}", SiteLink, qLink.Attr("href")));
|
||||
|
||||
var catUrl = qRow.Children().ElementAt(0).FirstElementChild.Cq().Attr("href");
|
||||
var catNum = catUrl.Split(new char[] { '=', '&' })[2].Replace("c", "");
|
||||
release.Category = MapTrackerCatToNewznab(catNum);
|
||||
|
||||
var dateString = qRow.Children().ElementAt(6).Cq().Text().Trim();
|
||||
if (dateString.Contains("ago"))
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateString);
|
||||
else
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);
|
||||
|
||||
var sizeStr = qRow.Children().ElementAt(7).Cq().Text().Split(new char[] { '/' })[0];
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[0].Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Children().ElementAt(8).Cq().Text().Split(new char[] { '/' })[1].Trim()) + release.Seeders;
|
||||
release.Files = ParseUtil.CoerceLong(qRow.Find("td:nth-child(5)").Text());
|
||||
release.Grabs = ParseUtil.CoerceLong(qRow.Find("a[href^=\"snatches.php?id=\"]").Text().Split(' ')[0]);
|
||||
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
var desc = qRow.Find("td:nth-child(2)");
|
||||
desc.Find("a").Remove();
|
||||
desc.Find("small").Remove(); // Remove release name (if enabled in the user cp)
|
||||
release.Description = desc.Text().Trim(new char[] { '-', ' ' });
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -219,6 +219,11 @@ namespace Jackett.Common.Indexers
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookies(searchUrl);
|
||||
if (results.IsRedirect && results.RedirectingTo.EndsWith("login.php"))
|
||||
{
|
||||
throw new Exception("relogin needed, please update your cookie");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string RowsSelector = ".torrent_table > tbody > tr";
|
||||
|
@@ -123,7 +123,7 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
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.Link = new Uri(SiteLink + qRow.Find("a[href^=\"download.php\"]").Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var dateString = qRow.Find("div:last-child").Text().Trim();
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
@@ -30,6 +31,7 @@ namespace Jackett.Common.Indexers
|
||||
string Language { get; }
|
||||
string LastError { get; set; }
|
||||
string ID { get; }
|
||||
Encoding Encoding { get; }
|
||||
|
||||
TorznabCapabilities TorznabCaps { get; }
|
||||
|
||||
|
@@ -389,7 +389,15 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
var dateString = document.QuerySelector("div.title-block > div.details-pane > div.left-box").TextContent;
|
||||
dateString = TrimString(dateString, "eng: ", " г."); // '... Дата выхода eng: 09 марта 2012 г. ...' -> '09 марта 2012'
|
||||
var date = DateTime.Parse(dateString, new CultureInfo(Language)); // dd mmmm yyyy
|
||||
DateTime date;
|
||||
if (dateString.Length == 4) //dateString might be just a year, e.g. https://www.lostfilm.tv/series/Ghosted/season_1/episode_14/
|
||||
{
|
||||
date = DateTime.ParseExact(dateString, "yyyy", CultureInfo.InvariantCulture).ToLocalTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
date = DateTime.Parse(dateString, new CultureInfo(Language)); // dd mmmm yyyy
|
||||
}
|
||||
|
||||
var urlDetails = new TrackerUrlDetails(playButton);
|
||||
var episodeReleases = await FetchTrackerReleases(urlDetails);
|
||||
|
976
src/Jackett.Common/Indexers/MejorTorrent.cs
Normal file
976
src/Jackett.Common/Indexers/MejorTorrent.cs
Normal file
@@ -0,0 +1,976 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom.Html;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Jackett.Common.Helpers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
class MejorTorrent : BaseWebIndexer
|
||||
{
|
||||
public static Uri WebUri = new Uri("http://www.mejortorrent.com/");
|
||||
public static Uri DownloadUri = new Uri(WebUri, "secciones.php?sec=descargas&ap=contar_varios");
|
||||
private static Uri SearchUriBase = new Uri(WebUri, "secciones.php");
|
||||
public static Uri NewTorrentsUri = new Uri(WebUri, "secciones.php?sec=ultimos_torrents");
|
||||
public static Encoding MEEncoding = Encoding.GetEncoding("windows-1252");
|
||||
|
||||
public MejorTorrent(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "MejorTorrent",
|
||||
description: "MejorTorrent - Hay veces que un torrent viene mejor! :)",
|
||||
link: WebUri.AbsoluteUri,
|
||||
caps: new TorznabCapabilities(TorznabCatType.TV,
|
||||
TorznabCatType.TVSD,
|
||||
TorznabCatType.TVHD,
|
||||
TorznabCatType.Movies),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationData())
|
||||
{
|
||||
Encoding = MEEncoding;
|
||||
Language = "es-es";
|
||||
Type = "public";
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
WebUri = new Uri(configData.SiteLink.Value);
|
||||
DownloadUri = new Uri(WebUri, "secciones.php?sec=descargas&ap=contar_varios");
|
||||
SearchUriBase = new Uri(WebUri, "secciones.php");
|
||||
NewTorrentsUri = new Uri(WebUri, "secciones.php?sec=ultimos_torrents");
|
||||
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
return await PerformQuery(query, 0);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query, int attempts)
|
||||
{
|
||||
var originalSearchTerm = query.SearchTerm;
|
||||
if (query.SearchTerm == null)
|
||||
{
|
||||
query.SearchTerm = "";
|
||||
}
|
||||
query.SearchTerm = query.SearchTerm.Replace("'", "");
|
||||
|
||||
var requester = new MejorTorrentRequester(this);
|
||||
var tvShowScraper = new TvShowScraper();
|
||||
var seasonScraper = new SeasonScraper();
|
||||
var downloadScraper = new DownloadScraper();
|
||||
var rssScraper = new RssScraper();
|
||||
var downloadGenerator = new DownloadGenerator(requester, downloadScraper);
|
||||
var tvShowPerformer = new TvShowPerformer(requester, tvShowScraper, seasonScraper, downloadGenerator);
|
||||
var rssPerformer = new RssPerformer(requester, rssScraper, seasonScraper, downloadGenerator);
|
||||
var movieSearchScraper = new MovieSearchScraper();
|
||||
var movieInfoScraper = new MovieInfoScraper();
|
||||
var movieDownloadScraper = new MovieDownloadScraper();
|
||||
var moviePerformer = new MoviePerformer(requester, movieSearchScraper, movieInfoScraper, movieDownloadScraper);
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
if (string.IsNullOrEmpty(query.SanitizedSearchTerm))
|
||||
{
|
||||
releases = (await rssPerformer.PerformQuery(query)).ToList();
|
||||
var movie = releases.First();
|
||||
movie.Category.Add(TorznabCatType.Movies.ID);
|
||||
releases.ToList().Add(movie);
|
||||
if (releases.Count() == 0)
|
||||
{
|
||||
releases = (await AliveCheck(tvShowPerformer)).ToList();
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
if (query.Categories.Contains(TorznabCatType.Movies.ID) || query.Categories.Count() == 0)
|
||||
{
|
||||
releases.AddRange(await moviePerformer.PerformQuery(query));
|
||||
}
|
||||
if (query.Categories.Contains(TorznabCatType.TV.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.TVSD.ID) ||
|
||||
query.Categories.Contains(TorznabCatType.TVHD.ID) ||
|
||||
query.Categories.Count() == 0)
|
||||
{
|
||||
releases.AddRange(await tvShowPerformer.PerformQuery(query));
|
||||
}
|
||||
|
||||
query.SearchTerm = originalSearchTerm;
|
||||
return releases;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> AliveCheck(TvShowPerformer tvShowPerformer)
|
||||
{
|
||||
IEnumerable<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
var tests = new Queue<string>(new[] { "stranger things", "westworld", "friends" });
|
||||
while (releases.Count() == 0 && tests.Count > 0)
|
||||
{
|
||||
var query = new TorznabQuery();
|
||||
query.SearchTerm = tests.Dequeue();
|
||||
releases = await tvShowPerformer.PerformQuery(query);
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
public static Uri CreateSearchUri(string search)
|
||||
{
|
||||
var finalUri = SearchUriBase.AbsoluteUri;
|
||||
finalUri += "?sec=buscador&valor=" + WebUtilityHelpers.UrlEncode(search, MEEncoding);
|
||||
return new Uri(finalUri);
|
||||
}
|
||||
|
||||
interface IScraper<T>
|
||||
{
|
||||
T Extract(IHtmlDocument html);
|
||||
}
|
||||
|
||||
class RssScraper : IScraper<IEnumerable<KeyValuePair<MTReleaseInfo, Uri>>>
|
||||
{
|
||||
private readonly string LinkQuerySelector = "a[href*=\"/serie\"]";
|
||||
|
||||
public IEnumerable<KeyValuePair<MTReleaseInfo, Uri>> Extract(IHtmlDocument html)
|
||||
{
|
||||
var episodes = GetNewEpisodesScratch(html);
|
||||
var links = GetLinks(html);
|
||||
var results = new List<KeyValuePair<MTReleaseInfo, Uri>>();
|
||||
for (var i = 0; i < episodes.Count(); i++)
|
||||
{
|
||||
results.Add(new KeyValuePair<MTReleaseInfo, Uri>(episodes.ElementAt(i), links.ElementAt(i)));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<MTReleaseInfo> GetNewEpisodesScratch(IHtmlDocument html)
|
||||
{
|
||||
var tvShowsElements = html.QuerySelectorAll(LinkQuerySelector);
|
||||
var seasonLinks = tvShowsElements.Select(e => e.Attributes["href"].Value);
|
||||
var dates = GetDates(html);
|
||||
var titles = GetTitles(html);
|
||||
var qualities = GetQualities(html);
|
||||
var seasonsFirstEpisodesAndLast = GetSeasonsFirstEpisodesAndLast(html);
|
||||
|
||||
var episodes = new List<MTReleaseInfo>();
|
||||
for(var i = 0; i < tvShowsElements.Count(); i++)
|
||||
{
|
||||
var e = new MTReleaseInfo();
|
||||
e.TitleOriginal = titles.ElementAt(i);
|
||||
e.PublishDate = dates.ElementAt(i);
|
||||
e.CategoryText = qualities.ElementAt(i);
|
||||
var sfeal = seasonsFirstEpisodesAndLast.ElementAt(i);
|
||||
e.Season = sfeal.Key;
|
||||
e.EpisodeNumber = sfeal.Value.Key;
|
||||
if (sfeal.Value.Value != null && sfeal.Value.Value > sfeal.Value.Key)
|
||||
{
|
||||
e.Files = sfeal.Value.Value - sfeal.Value.Key + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Files = 1;
|
||||
}
|
||||
episodes.Add(e);
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
private List<Uri> GetLinks(IHtmlDocument html)
|
||||
{
|
||||
return html.QuerySelectorAll(LinkQuerySelector)
|
||||
.Select(e => e.Attributes["href"].Value)
|
||||
.Select(relativeLink => new Uri(WebUri, relativeLink))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<DateTime> GetDates(IHtmlDocument html)
|
||||
{
|
||||
return html.QuerySelectorAll(LinkQuerySelector)
|
||||
.Select(e => e.PreviousElementSibling.TextContent)
|
||||
.Select(dateString => dateString.Split('-'))
|
||||
.Select(parts => new int[] { Int32.Parse(parts[0]), Int32.Parse(parts[1]), Int32.Parse(parts[2]) })
|
||||
.Select(intParts => new DateTime(intParts[0], intParts[1], intParts[2]))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<string> GetTitles(IHtmlDocument html)
|
||||
{
|
||||
var texts = LinkTexts(html);
|
||||
var completeTitles = texts.Select(text => text.Substring(0, text.IndexOf('-') - 1));
|
||||
var regex = new Regex(@".+\((.+)\)");
|
||||
var finalTitles = completeTitles.Select(title =>
|
||||
{
|
||||
var match = regex.Match(title);
|
||||
if (!match.Success) return title;
|
||||
return match.Groups[1].Value;
|
||||
});
|
||||
return finalTitles.ToList();
|
||||
}
|
||||
|
||||
private List<string> GetQualities(IHtmlDocument html)
|
||||
{
|
||||
var texts = LinkTexts(html);
|
||||
var regex = new Regex(@".+\[(.*)\].+");
|
||||
var qualities = texts.Select(text =>
|
||||
{
|
||||
var match = regex.Match(text);
|
||||
if (!match.Success) return "HDTV";
|
||||
var quality = match.Groups[1].Value;
|
||||
switch(quality)
|
||||
{
|
||||
case "720p":
|
||||
return "HDTV-720p";
|
||||
case "1080p":
|
||||
return "HDTV-1080p";
|
||||
default:
|
||||
return "HDTV";
|
||||
}
|
||||
});
|
||||
return qualities.ToList();
|
||||
}
|
||||
|
||||
private List<KeyValuePair<int, KeyValuePair<int,int?>>> GetSeasonsFirstEpisodesAndLast(IHtmlDocument html)
|
||||
{
|
||||
var texts = LinkTexts(html);
|
||||
// SEASON | START EPISODE | [END EPISODE]
|
||||
var regex = new Regex(@"(\d{1,2})x(\d{1,2})(?:.*\d{1,2}x(\d{1,2})?)?", RegexOptions.IgnoreCase);
|
||||
var seasonsFirstEpisodesAndLast = texts.Select(text =>
|
||||
{
|
||||
var match = regex.Match(text);
|
||||
int season = 0;
|
||||
int episode = 0;
|
||||
int? finalEpisode = null;
|
||||
if (!match.Success) return new KeyValuePair<int, KeyValuePair<int, int?>>(season, new KeyValuePair<int, int?>(episode, finalEpisode));
|
||||
season = Int32.Parse(match.Groups[1].Value);
|
||||
episode = Int32.Parse(match.Groups[2].Value);
|
||||
if (match.Groups[3].Success)
|
||||
{
|
||||
finalEpisode = Int32.Parse(match.Groups[3].Value);
|
||||
}
|
||||
return new KeyValuePair<int, KeyValuePair<int, int?>>(season, new KeyValuePair<int, int?>(episode, finalEpisode));
|
||||
});
|
||||
return seasonsFirstEpisodesAndLast.ToList();
|
||||
}
|
||||
|
||||
private List<string> LinkTexts(IHtmlDocument html)
|
||||
{
|
||||
return html.QuerySelectorAll(LinkQuerySelector)
|
||||
.Select(e => e.TextContent).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TvShowScraper : IScraper<IEnumerable<Season>>
|
||||
{
|
||||
public IEnumerable<Season> Extract(IHtmlDocument html)
|
||||
{
|
||||
var tvSelector = "a[href*=\"/serie-\"]";
|
||||
var seasonsElements = html.QuerySelectorAll(tvSelector).Select(e => e.ParentElement);
|
||||
|
||||
var newTvShows = new List<Season>();
|
||||
|
||||
// EXAMPLES:
|
||||
// Stranger Things - 1ª Temporada (HDTV)
|
||||
// Stranger Things - 1ª Temporada [720p] (HDTV-720p)
|
||||
var regex = new Regex(@"(.+) - ([0-9]+).*\((.*)\)");
|
||||
foreach (var seasonElement in seasonsElements)
|
||||
{
|
||||
var link = seasonElement.QuerySelector("a[href*=\"/serie-\"]").Attributes["href"].Value;
|
||||
var info = seasonElement.TextContent; // Stranger Things - 1 ...
|
||||
var searchMatch = regex.Match(info);
|
||||
if (!searchMatch.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int seasonNumber;
|
||||
if (!Int32.TryParse(searchMatch.Groups[2].Value, out seasonNumber))
|
||||
{
|
||||
seasonNumber = 0;
|
||||
}
|
||||
var season = new Season
|
||||
{
|
||||
Title = searchMatch.Groups[1].Value,
|
||||
Number = seasonNumber,
|
||||
Type = searchMatch.Groups[3].Value,
|
||||
Link = new Uri(WebUri, link)
|
||||
};
|
||||
|
||||
// EXAMPLE: El cuento de la criada (Handmaids Tale)
|
||||
var originalTitleRegex = new Regex(@".+\((.+)\)");
|
||||
var originalTitleMath = originalTitleRegex.Match(season.Title);
|
||||
if (originalTitleMath.Success)
|
||||
{
|
||||
season.Title = originalTitleMath.Groups[1].Value;
|
||||
}
|
||||
newTvShows.Add(season);
|
||||
}
|
||||
return newTvShows;
|
||||
}
|
||||
}
|
||||
|
||||
class SeasonScraper : IScraper<IEnumerable<MTReleaseInfo>>
|
||||
{
|
||||
public IEnumerable<MTReleaseInfo> Extract(IHtmlDocument html)
|
||||
{
|
||||
var episodesLinksHtml = html.QuerySelectorAll("a[href*=\"/serie-episodio-descargar-torrent\"]");
|
||||
var episodesTexts = episodesLinksHtml.Select(l => l.TextContent).ToList();
|
||||
var episodesLinks = episodesLinksHtml.Select(e => e.Attributes["href"].Value).ToList();
|
||||
var dates = episodesLinksHtml
|
||||
.Select(e => e.ParentElement.ParentElement.QuerySelector("div").TextContent)
|
||||
.Select(stringDate => stringDate.Replace("Fecha: ", ""))
|
||||
.Select(stringDate => stringDate.Split('-'))
|
||||
.Select(stringParts => new int[]{ Int32.Parse(stringParts[0]), Int32.Parse(stringParts[1]), Int32.Parse(stringParts[2]) })
|
||||
.Select(intParts => new DateTime(intParts[0], intParts[1], intParts[2]));
|
||||
|
||||
var episodes = episodesLinks.Select(e => new MTReleaseInfo()).ToList();
|
||||
|
||||
for (var i = 0; i < episodes.Count(); i++)
|
||||
{
|
||||
GuessEpisodes(episodes.ElementAt(i), episodesTexts.ElementAt(i));
|
||||
ExtractLinkInfo(episodes.ElementAt(i), episodesLinks.ElementAt(i));
|
||||
episodes.ElementAt(i).PublishDate = dates.ElementAt(i);
|
||||
}
|
||||
|
||||
return episodes;
|
||||
}
|
||||
|
||||
private void GuessEpisodes(MTReleaseInfo release, string episodeText)
|
||||
{
|
||||
var seasonEpisodeRegex = new Regex(@"(\d{1,2}).*?(\d{1,2})", RegexOptions.IgnoreCase);
|
||||
var matchSeasonEpisode = seasonEpisodeRegex.Match(episodeText);
|
||||
if (!matchSeasonEpisode.Success) return;
|
||||
release.Season = Int32.Parse(matchSeasonEpisode.Groups[1].Value);
|
||||
release.EpisodeNumber = Int32.Parse(matchSeasonEpisode.Groups[2].Value);
|
||||
|
||||
char[] textArray = episodeText.ToCharArray();
|
||||
Array.Reverse(textArray);
|
||||
var reversedText = new string(textArray);
|
||||
var finalEpisodeRegex = new Regex(@"(\d{1,2})");
|
||||
var matchFinalEpisode = finalEpisodeRegex.Match(reversedText);
|
||||
if (!matchFinalEpisode.Success) return;
|
||||
var finalEpisodeArray = matchFinalEpisode.Groups[1].Value.ToCharArray();
|
||||
Array.Reverse(finalEpisodeArray);
|
||||
var finalEpisode = Int32.Parse(new string(finalEpisodeArray));
|
||||
if (finalEpisode > release.EpisodeNumber)
|
||||
{
|
||||
release.Files = (finalEpisode + 1) - release.EpisodeNumber;
|
||||
release.Size = release.Size * release.Files;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractLinkInfo(MTReleaseInfo release, String link)
|
||||
{
|
||||
// LINK FORMAT: /serie-episodio-descargar-torrent-${ID}-${TITLE}-${SEASON_NUMBER}x${EPISODE_NUMBER}[range].html
|
||||
var regex = new Regex(@"\/serie-episodio-descargar-torrent-(\d+)-(.*)-(\d{1,2}).*(\d{1,2}).*\.html", RegexOptions.IgnoreCase);
|
||||
var linkMatch = regex.Match(link);
|
||||
|
||||
if (!linkMatch.Success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
release.MejorTorrentID = linkMatch.Groups[1].Value;
|
||||
release.Title = linkMatch.Groups[2].Value;
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadScraper : IScraper<IEnumerable<Uri>>
|
||||
{
|
||||
public IEnumerable<Uri> Extract(IHtmlDocument html)
|
||||
{
|
||||
return html.QuerySelectorAll("a[href*=\".torrent\"]")
|
||||
.Select(e => e.Attributes["href"].Value)
|
||||
.Select(link => new Uri(WebUri, link));
|
||||
}
|
||||
}
|
||||
|
||||
class Season
|
||||
{
|
||||
public String Title;
|
||||
public int Number;
|
||||
public Uri Link;
|
||||
public TorznabCategory Category; // HDTV or HDTV-720
|
||||
private string _type;
|
||||
public string Type
|
||||
{
|
||||
get { return _type; }
|
||||
set
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case "HDTV":
|
||||
Category = TorznabCatType.TVSD;
|
||||
_type = "SDTV";
|
||||
break;
|
||||
case "HDTV-720p":
|
||||
Category = TorznabCatType.TVHD;
|
||||
_type = "HDTV-720p";
|
||||
break;
|
||||
case "HDTV-1080p":
|
||||
Category = TorznabCatType.TVHD;
|
||||
_type = "HDTV-1080p";
|
||||
break;
|
||||
default:
|
||||
Category = TorznabCatType.TV;
|
||||
_type = "HDTV-720p";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MTReleaseInfo : ReleaseInfo
|
||||
{
|
||||
public string MejorTorrentID;
|
||||
public bool IsMovie;
|
||||
public int? Year;
|
||||
public int _season;
|
||||
public int _episodeNumber;
|
||||
private string _categoryText;
|
||||
private string _originalTitle;
|
||||
|
||||
public MTReleaseInfo()
|
||||
{
|
||||
this.Category = new List<int>();
|
||||
this.Grabs = 5;
|
||||
this.Files = 1;
|
||||
this.PublishDate = new DateTime();
|
||||
this.Peers = 1;
|
||||
this.Seeders = 1;
|
||||
this.Size = ReleaseInfo.BytesFromGB(1);
|
||||
this._originalTitle = "";
|
||||
}
|
||||
|
||||
public int Season { get { return _season; } set { _season = value; TitleOriginal = _originalTitle; } }
|
||||
|
||||
public int EpisodeNumber { get { return _episodeNumber; } set { _episodeNumber = value; TitleOriginal = _originalTitle; } }
|
||||
|
||||
public string CategoryText {
|
||||
get { return _categoryText; }
|
||||
set
|
||||
{
|
||||
if (IsMovie)
|
||||
{
|
||||
Category.Add(TorznabCatType.Movies.ID);
|
||||
_categoryText = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case "SDTV":
|
||||
Category.Add(TorznabCatType.TVSD.ID);
|
||||
_categoryText = "SDTV";
|
||||
break;
|
||||
case "HDTV":
|
||||
Category.Add(TorznabCatType.TVSD.ID);
|
||||
_categoryText = "SDTV";
|
||||
break;
|
||||
case "HDTV-720p":
|
||||
Category.Add(TorznabCatType.TVHD.ID);
|
||||
_categoryText = "HDTV-720p";
|
||||
break;
|
||||
case "HDTV-1080p":
|
||||
Category.Add(TorznabCatType.TVHD.ID);
|
||||
_categoryText = "HDTV-1080p";
|
||||
break;
|
||||
default:
|
||||
Category.Add(TorznabCatType.TV.ID);
|
||||
_categoryText = "HDTV-720p";
|
||||
break;
|
||||
}
|
||||
}
|
||||
TitleOriginal = _originalTitle;
|
||||
}
|
||||
}
|
||||
|
||||
public int FinalEpisodeNumber { get { return (int)(EpisodeNumber + Files - 1); } }
|
||||
|
||||
public string TitleOriginal
|
||||
{
|
||||
get { return _originalTitle; }
|
||||
set
|
||||
{
|
||||
_originalTitle = value;
|
||||
if (_originalTitle != "")
|
||||
{
|
||||
Title = _originalTitle;
|
||||
Title = char.ToUpper(Title[0]) + Title.Substring(1);
|
||||
}
|
||||
var seasonAndEpisode = "";
|
||||
if (!Category.Contains(TorznabCatType.Movies.ID))
|
||||
{
|
||||
seasonAndEpisode = "S" + Season.ToString("00") + "E" + EpisodeNumber.ToString("00");
|
||||
if (Files > 1)
|
||||
{
|
||||
seasonAndEpisode += "-" + FinalEpisodeNumber.ToString("00");
|
||||
}
|
||||
}
|
||||
Title = String.Join(".", new List<string>() { Title, seasonAndEpisode, CategoryText, "Spanish" }.Where(s => s != ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MoviePerformer : IPerformer
|
||||
{
|
||||
private IRequester requester;
|
||||
private IScraper<IEnumerable<Uri>> movieSearchScraper;
|
||||
private IScraper<MTReleaseInfo> movieInfoScraper;
|
||||
private IScraper<Uri> movieDownloadScraper;
|
||||
|
||||
public MoviePerformer(
|
||||
IRequester requester,
|
||||
IScraper<IEnumerable<Uri>> movieSearchScraper,
|
||||
IScraper<MTReleaseInfo> movieInfoScraper,
|
||||
IScraper<Uri> movieDownloadScraper)
|
||||
{
|
||||
this.requester = requester;
|
||||
this.movieSearchScraper = movieSearchScraper;
|
||||
this.movieInfoScraper = movieInfoScraper;
|
||||
this.movieDownloadScraper = movieDownloadScraper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
query = SanitizeQuery(query);
|
||||
var movies = await FetchMoviesBasedOnLongestWord(query);
|
||||
return movies;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<MTReleaseInfo>> FetchMoviesBasedOnLongestWord(TorznabQuery query)
|
||||
{
|
||||
var originalSearch = query.SearchTerm;
|
||||
var regexStr = ".*" + originalSearch.Replace(" ", ".*") + ".*";
|
||||
var regex = new Regex(regexStr, RegexOptions.IgnoreCase);
|
||||
query.SearchTerm = LongestWord(query);
|
||||
var movies = await FetchMovies(query);
|
||||
return movies.Where(m => regex.Match(m.Title).Success);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<MTReleaseInfo>> FetchMovies(TorznabQuery query)
|
||||
{
|
||||
var uriSearch = CreateSearchUri(query.SearchTerm);
|
||||
var htmlSearch = await requester.MakeRequest(uriSearch);
|
||||
var moviesInfoUris = movieSearchScraper.Extract(htmlSearch);
|
||||
var infoHtmlTasks = moviesInfoUris.Select(async u => await requester.MakeRequest(u));
|
||||
var infoHtmls = await Task.WhenAll(infoHtmlTasks);
|
||||
var movies = infoHtmls.Select(h => movieInfoScraper.Extract(h));
|
||||
|
||||
var tasks = movies.Select(async m =>
|
||||
{
|
||||
var html = await requester.MakeRequest(m.Link);
|
||||
return new KeyValuePair<MTReleaseInfo, IHtmlDocument>(m, html);
|
||||
});
|
||||
var moviesWithHtml = await Task.WhenAll(tasks.ToArray());
|
||||
movies = moviesWithHtml.Select(movieWithHtml =>
|
||||
{
|
||||
var movie = movieWithHtml.Key;
|
||||
var html = movieWithHtml.Value;
|
||||
movie.Link = movieDownloadScraper.Extract(html);
|
||||
movie.Guid = movieWithHtml.Key.Link;
|
||||
return movie;
|
||||
});
|
||||
|
||||
if (query.Year != null)
|
||||
{
|
||||
movies = movies
|
||||
.Where(m =>
|
||||
m.Year == query.Year ||
|
||||
m.Year == query.Year + 1 ||
|
||||
m.Year == query.Year - 1)
|
||||
.Select(m =>
|
||||
{
|
||||
m.TitleOriginal = m.TitleOriginal.Replace("(" + m.Year + ")", "(" + query.Year + ")");
|
||||
return m;
|
||||
});
|
||||
}
|
||||
|
||||
return movies;
|
||||
}
|
||||
|
||||
private string LongestWord(TorznabQuery query)
|
||||
{
|
||||
var words = query.SearchTerm.Split(' ');
|
||||
if (words.Count() == 0) return null;
|
||||
var longestWord = words.First();
|
||||
foreach(var word in words)
|
||||
{
|
||||
if (word.Length >= longestWord.Length)
|
||||
{
|
||||
longestWord = word;
|
||||
}
|
||||
}
|
||||
return longestWord;
|
||||
}
|
||||
|
||||
private TorznabQuery SanitizeQuery(TorznabQuery query)
|
||||
{
|
||||
var regex = new Regex(@"\d{4}$");
|
||||
var match = regex.Match(query.SanitizedSearchTerm);
|
||||
if (match.Success)
|
||||
{
|
||||
var yearStr = match.Groups[0].Value;
|
||||
query.Year = Int32.Parse(yearStr);
|
||||
query.SearchTerm = query.SearchTerm.Replace(yearStr, "").Trim();
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
class MovieSearchScraper : IScraper<IEnumerable<Uri>>
|
||||
{
|
||||
public IEnumerable<Uri> Extract(IHtmlDocument html)
|
||||
{
|
||||
return html.QuerySelectorAll("a[href*=\"/peli-\"]")
|
||||
.Select(e => e.GetAttribute("href"))
|
||||
.Select(relativeUri => new Uri(WebUri, relativeUri));
|
||||
}
|
||||
}
|
||||
|
||||
class MovieInfoScraper : IScraper<MTReleaseInfo>
|
||||
{
|
||||
public MTReleaseInfo Extract(IHtmlDocument html)
|
||||
{
|
||||
var release = new MTReleaseInfo();
|
||||
release.IsMovie = true;
|
||||
var selectors = html.QuerySelectorAll("b");
|
||||
var titleSelector = html.QuerySelector("span>b");
|
||||
try
|
||||
{
|
||||
var title = titleSelector.TextContent;
|
||||
if (title.Contains("("))
|
||||
{
|
||||
title = title.Substring(0, title.IndexOf("(")).Trim();
|
||||
}
|
||||
release.TitleOriginal = title;
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
var year = selectors.Where(s => s.TextContent.ToLower().Contains("año"))
|
||||
.First().NextSibling.TextContent.Trim();
|
||||
release.Year = Int32.Parse(year);
|
||||
release.TitleOriginal += " (" + year + ")";
|
||||
} catch { }
|
||||
try
|
||||
{
|
||||
var dateStr = selectors.Where(s => s.TextContent.ToLower().Contains("fecha"))
|
||||
.First().NextSibling.TextContent.Trim();
|
||||
var date = Convert.ToDateTime(dateStr);
|
||||
release.PublishDate = date;
|
||||
} catch { }
|
||||
try
|
||||
{
|
||||
var sizeStr = selectors.Where(s => s.TextContent.ToLower().Contains("tamaño"))
|
||||
.First().NextSibling.TextContent.Trim();
|
||||
Regex rgx = new Regex(@"[^0-9,.]");
|
||||
long size;
|
||||
if (sizeStr.ToLower().Trim().EndsWith("mb"))
|
||||
{
|
||||
size = ReleaseInfo.BytesFromMB(float.Parse(rgx.Replace(sizeStr, "")));
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeStr = rgx.Replace(sizeStr, "").Replace(",", ".");
|
||||
size = ReleaseInfo.BytesFromGB(float.Parse(rgx.Replace(sizeStr, "")));
|
||||
}
|
||||
release.Size = size;
|
||||
} catch { }
|
||||
try
|
||||
{
|
||||
var category = selectors.Where(s => s.TextContent.ToLower().Contains("formato"))
|
||||
.First().NextSibling.TextContent.Trim();
|
||||
release.CategoryText = category;
|
||||
} catch { }
|
||||
try
|
||||
{
|
||||
var title = titleSelector.TextContent;
|
||||
if (title.Contains("(") && title.Contains(")") && title.Contains("4k"))
|
||||
{
|
||||
release.CategoryText = "2160p";
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
var link = html.QuerySelector("a[href*=\"sec=descargas\"]").GetAttribute("href");
|
||||
release.Link = new Uri(WebUri, link);
|
||||
release.Guid = release.Link;
|
||||
}
|
||||
catch { }
|
||||
return release;
|
||||
}
|
||||
}
|
||||
|
||||
class MovieDownloadScraper : IScraper<Uri>
|
||||
{
|
||||
public Uri Extract(IHtmlDocument html)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Uri(WebUri, html.QuerySelector("a[href*=\".torrent\"]").GetAttribute("href"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IRequester
|
||||
{
|
||||
Task<IHtmlDocument> MakeRequest(
|
||||
Uri uri,
|
||||
RequestType method = RequestType.GET,
|
||||
IEnumerable<KeyValuePair<string, string>> data = null,
|
||||
Dictionary<string, string> headers = null);
|
||||
}
|
||||
|
||||
class MejorTorrentRequester : IRequester
|
||||
{
|
||||
private MejorTorrent mt;
|
||||
|
||||
public MejorTorrentRequester(MejorTorrent mt)
|
||||
{
|
||||
this.mt = mt;
|
||||
}
|
||||
|
||||
public async Task<IHtmlDocument> MakeRequest(
|
||||
Uri uri,
|
||||
RequestType method = RequestType.GET,
|
||||
IEnumerable<KeyValuePair<string, string>> data = null,
|
||||
Dictionary<string, string> headers = null)
|
||||
{
|
||||
var result = await mt.RequestBytesWithCookies(uri.AbsoluteUri, null, method, null, data, headers);
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var doc = SearchResultParser.Parse(mt.Encoding.GetString(result.Content));
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
|
||||
class MejorTorrentDownloadRequesterDecorator
|
||||
{
|
||||
private IRequester r;
|
||||
|
||||
public MejorTorrentDownloadRequesterDecorator(IRequester r)
|
||||
{
|
||||
this.r = r;
|
||||
}
|
||||
|
||||
public async Task<IHtmlDocument> MakeRequest(IEnumerable<string> ids)
|
||||
{
|
||||
var downloadHtmlTasks = new List<Task<IHtmlDocument>>();
|
||||
var formData = new List<KeyValuePair<string, string>>();
|
||||
int index = 1;
|
||||
ids.ToList().ForEach(id =>
|
||||
{
|
||||
var episodeID = new KeyValuePair<string, string>("episodios[" + index + "]", id);
|
||||
formData.Add(episodeID);
|
||||
index++;
|
||||
});
|
||||
formData.Add(new KeyValuePair<string, string>("total_capis", index.ToString()));
|
||||
formData.Add(new KeyValuePair<string, string>("tabla", "series"));
|
||||
return await r.MakeRequest(DownloadUri, RequestType.POST, formData);
|
||||
}
|
||||
}
|
||||
|
||||
interface IPerformer
|
||||
{
|
||||
Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query);
|
||||
}
|
||||
|
||||
class RssPerformer : IPerformer
|
||||
{
|
||||
private IRequester requester;
|
||||
private IScraper<IEnumerable<KeyValuePair<MTReleaseInfo, Uri>>> rssScraper;
|
||||
private IScraper<IEnumerable<MTReleaseInfo>> seasonScraper;
|
||||
private IDownloadGenerator downloadGenerator;
|
||||
|
||||
public RssPerformer(
|
||||
IRequester requester,
|
||||
IScraper<IEnumerable<KeyValuePair<MTReleaseInfo, Uri>>> rssScraper,
|
||||
IScraper<IEnumerable<MTReleaseInfo>> seasonScraper,
|
||||
IDownloadGenerator downloadGenerator)
|
||||
{
|
||||
this.requester = requester;
|
||||
this.rssScraper = rssScraper;
|
||||
this.seasonScraper = seasonScraper;
|
||||
this.downloadGenerator = downloadGenerator;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var html = await requester.MakeRequest(NewTorrentsUri);
|
||||
var episodesAndSeasonsUri = rssScraper.Extract(html);
|
||||
|
||||
Task.WaitAll(episodesAndSeasonsUri.ToList().Select(async epAndSeasonUri =>
|
||||
{
|
||||
var episode = epAndSeasonUri.Key;
|
||||
var seasonUri = epAndSeasonUri.Value;
|
||||
await AddMejorTorrentIDs(episode, seasonUri);
|
||||
}).ToArray());
|
||||
|
||||
var episodes = episodesAndSeasonsUri.Select(epAndSeason => epAndSeason.Key).ToList();
|
||||
await downloadGenerator.AddDownloadLinks(episodes);
|
||||
return episodes;
|
||||
}
|
||||
|
||||
private async Task AddMejorTorrentIDs(MTReleaseInfo episode, Uri seasonUri)
|
||||
{
|
||||
var html = await requester.MakeRequest(seasonUri);
|
||||
var newEpisodes = seasonScraper.Extract(html);
|
||||
// GET BY EPISODE NUMBER
|
||||
newEpisodes = newEpisodes.Where(e => e.EpisodeNumber == episode.EpisodeNumber);
|
||||
if (newEpisodes.Count() == 0)
|
||||
{
|
||||
throw new Exception("Imposible to detect episode ID in RSS");
|
||||
}
|
||||
episode.MejorTorrentID = newEpisodes.First().MejorTorrentID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TvShowPerformer : IPerformer
|
||||
{
|
||||
private IRequester requester;
|
||||
private IScraper<IEnumerable<Season>> tvShowScraper;
|
||||
private IScraper<IEnumerable<MTReleaseInfo>> seasonScraper;
|
||||
private IDownloadGenerator downloadGenerator;
|
||||
|
||||
public TvShowPerformer(
|
||||
IRequester requester,
|
||||
IScraper<IEnumerable<Season>> tvShowScraper,
|
||||
IScraper<IEnumerable<MTReleaseInfo>> seasonScraper,
|
||||
IDownloadGenerator downloadGenerator)
|
||||
{
|
||||
this.requester = requester;
|
||||
this.tvShowScraper = tvShowScraper;
|
||||
this.seasonScraper = seasonScraper;
|
||||
this.downloadGenerator = downloadGenerator;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
query = FixQuery(query);
|
||||
var seasons = await GetSeasons(query);
|
||||
var episodes = await GetEpisodes(query, seasons);
|
||||
await downloadGenerator.AddDownloadLinks(episodes);
|
||||
if (seasons.Count() > 0)
|
||||
{
|
||||
episodes.ForEach(e => e.TitleOriginal = seasons.First().Title);
|
||||
}
|
||||
return episodes;
|
||||
}
|
||||
|
||||
private TorznabQuery FixQuery(TorznabQuery query)
|
||||
{
|
||||
var seasonRegex = new Regex(@".*?(s\d{1,2})", RegexOptions.IgnoreCase);
|
||||
var episodeRegex = new Regex(@".*?(e\d{1,2})", RegexOptions.IgnoreCase);
|
||||
var seasonMatch = seasonRegex.Match(query.SearchTerm);
|
||||
var episodeMatch = episodeRegex.Match(query.SearchTerm);
|
||||
if (seasonMatch.Success)
|
||||
{
|
||||
query.Season = Int32.Parse(seasonMatch.Groups[1].Value.Substring(1));
|
||||
query.SearchTerm = query.SearchTerm.Replace(seasonMatch.Groups[1].Value, "");
|
||||
}
|
||||
if (episodeMatch.Success)
|
||||
{
|
||||
query.Episode = episodeMatch.Groups[1].Value.Substring(1);
|
||||
query.SearchTerm = query.SearchTerm.Replace(episodeMatch.Groups[1].Value, "");
|
||||
}
|
||||
query.SearchTerm = query.SearchTerm.Trim();
|
||||
return query;
|
||||
}
|
||||
|
||||
private async Task<List<Season>> GetSeasons(TorznabQuery query)
|
||||
{
|
||||
var seasonHtml = await requester.MakeRequest(CreateSearchUri(query.SanitizedSearchTerm));
|
||||
var seasons = tvShowScraper.Extract(seasonHtml);
|
||||
if (query.Season != 0)
|
||||
{
|
||||
seasons = seasons.Where(s => s.Number == query.Season);
|
||||
}
|
||||
if (query.Categories.Count() != 0)
|
||||
{
|
||||
seasons = seasons.Where(s => new List<int>(query.Categories).Contains(s.Category.ID));
|
||||
}
|
||||
return seasons.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<MTReleaseInfo>> GetEpisodes(TorznabQuery query, IEnumerable<Season> seasons)
|
||||
{
|
||||
var episodesHtmlTasks = new Dictionary<Season, Task<IHtmlDocument>>();
|
||||
seasons.ToList().ForEach(season =>
|
||||
{
|
||||
episodesHtmlTasks.Add(season, requester.MakeRequest(new Uri(WebUri, season.Link)));
|
||||
});
|
||||
var episodesHtml = await Task.WhenAll(episodesHtmlTasks.Values);
|
||||
var episodes = episodesHtmlTasks.SelectMany(seasonAndHtml =>
|
||||
{
|
||||
var season = seasonAndHtml.Key;
|
||||
var html = seasonAndHtml.Value.Result;
|
||||
var eps = seasonScraper.Extract(html);
|
||||
return eps.ToList().Select(e =>
|
||||
{
|
||||
e.CategoryText = season.Type;
|
||||
return e;
|
||||
});
|
||||
});
|
||||
if (!string.IsNullOrEmpty(query.Episode))
|
||||
{
|
||||
var episodeNumber = Int32.Parse(query.Episode);
|
||||
episodes = episodes.Where(e => e.EpisodeNumber <= episodeNumber && episodeNumber <= e.FinalEpisodeNumber);
|
||||
}
|
||||
return episodes.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
interface IDownloadGenerator
|
||||
{
|
||||
Task AddDownloadLinks(IEnumerable<MTReleaseInfo> episodes);
|
||||
}
|
||||
|
||||
class DownloadGenerator : IDownloadGenerator
|
||||
{
|
||||
private IRequester requester;
|
||||
private IScraper<IEnumerable<Uri>> downloadScraper;
|
||||
|
||||
public DownloadGenerator(IRequester requester, IScraper<IEnumerable<Uri>> downloadScraper)
|
||||
{
|
||||
this.requester = requester;
|
||||
this.downloadScraper = downloadScraper;
|
||||
}
|
||||
|
||||
public async Task AddDownloadLinks(IEnumerable<MTReleaseInfo> episodes)
|
||||
{
|
||||
var downloadRequester = new MejorTorrentDownloadRequesterDecorator(requester);
|
||||
var downloadHtml = await downloadRequester.MakeRequest(episodes.Select(e => e.MejorTorrentID));
|
||||
var downloads = downloadScraper.Extract(downloadHtml).ToList();
|
||||
|
||||
for (var i = 0; i < downloads.Count; i++)
|
||||
{
|
||||
var e = episodes.ElementAt(i);
|
||||
episodes.ElementAt(i).Link = downloads.ElementAt(i);
|
||||
episodes.ElementAt(i).Guid = downloads.ElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -180,7 +180,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(urlSearch);
|
||||
if (response.Status == System.Net.HttpStatusCode.Forbidden)
|
||||
if (response.Status == System.Net.HttpStatusCode.Forbidden || CookieHeader.Contains("pass=deleted"))
|
||||
{
|
||||
// re-login
|
||||
await ApplyConfiguration(null);
|
||||
|
@@ -117,12 +117,17 @@ namespace Jackett.Common.Indexers
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
protected async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query, String seasonep)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var pairs = new List<KeyValuePair<string, string>>();
|
||||
|
||||
if (seasonep != null)
|
||||
{
|
||||
searchString = query.SanitizedSearchTerm;
|
||||
}
|
||||
|
||||
pairs.Add(new KeyValuePair<string, string>("nyit_sorozat_resz", "true"));
|
||||
pairs.Add(new KeyValuePair<string, string>("miben", "name"));
|
||||
pairs.Add(new KeyValuePair<string, string>("tipus", "kivalasztottak_kozott"));
|
||||
@@ -198,8 +203,45 @@ namespace Jackett.Common.Indexers
|
||||
string catlink = qRow.Find("a:has(img[class='categ_link'])").First().Attr("href");
|
||||
string cat = ParseUtil.GetArgumentFromQueryString(catlink, "tipus");
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
if (seasonep == null)
|
||||
releases.Add(release);
|
||||
|
||||
else
|
||||
{
|
||||
if (query.MatchQueryStringAND(release.Title, null, seasonep))
|
||||
{
|
||||
/* For sonnar if the search querry was english the title must be english also so we need to change the Description and Title */
|
||||
var temp = release.Title;
|
||||
|
||||
// releasedata everithing after Name.S0Xe0X
|
||||
String releasedata =release.Title.Split(new[] { seasonep }, StringSplitOptions.None)[1].Trim();
|
||||
|
||||
/* if the release name not contains the language we add it because it is know from category */
|
||||
if (cat.Contains("hun") && !releasedata.Contains("hun"))
|
||||
releasedata += ".hun";
|
||||
|
||||
// release description contains [imdb: ****] but we only need the data before it for title
|
||||
String[] description = {release.Description, ""};
|
||||
if (release.Description.Contains("[imdb:"))
|
||||
{
|
||||
description = release.Description.Split('[');
|
||||
description[1] = "[" + description[1];
|
||||
}
|
||||
|
||||
release.Title = (description[0].Trim() + "." + seasonep.Trim() + "." + releasedata.Trim('.')).Replace(' ', '.');
|
||||
|
||||
// if search is done for S0X than we dont want to put . between S0X and E0X
|
||||
Match match = Regex.Match(releasedata, @"^E\d\d?");
|
||||
if (seasonep.Length==3 && match.Success)
|
||||
release.Title = (description[0].Trim() + "." + seasonep.Trim() + releasedata.Trim('.')).Replace(' ', '.');
|
||||
|
||||
// add back imdb points to the description [imdb: 8.7]
|
||||
release.Description = temp+" "+ description[1];
|
||||
release.Description = release.Description.Trim();
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -209,5 +251,16 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var results = await PerformQuery(query, null);
|
||||
if (results.Count()==0 && query.IsTVSearch) // if we search for a localized title ncore can't handle any extra S/E information, search without it and AND filter the results. See #1450
|
||||
{
|
||||
results = await PerformQuery(query,query.GetEpisodeSearchString());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -180,7 +180,7 @@ namespace Jackett.Common.Indexers
|
||||
var qCatLink = qRow.Find("a[href^=browse.php?cat=]").First();
|
||||
var qSeeders = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(3)");
|
||||
var qLeechers = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(4)");
|
||||
var qDateStr = qRow.Find("td > table.testtable > tbody > tr > td:eq(6)");
|
||||
var qDateStr = qRow.Find("td > table.testtable > tbody > tr > td:eq(7)");
|
||||
var qSize = qRow.Find("td > table.testtable > tbody > tr > td > strong:eq(1)");
|
||||
var qDownloadLink = qRow.Find("a[href*=download]").First();
|
||||
|
||||
|
486
src/Jackett.Common/Indexers/Newpct.cs
Normal file
486
src/Jackett.Common/Indexers/Newpct.cs
Normal file
@@ -0,0 +1,486 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using static Jackett.Common.Models.IndexerConfig.ConfigurationData;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class Newpct : BaseCachingWebIndexer
|
||||
{
|
||||
enum ReleaseType
|
||||
{
|
||||
TV,
|
||||
Movie,
|
||||
}
|
||||
|
||||
class NewpctRelease : ReleaseInfo
|
||||
{
|
||||
public string SerieName;
|
||||
public int? Season;
|
||||
public int? Episode;
|
||||
public int? EpisodeTo;
|
||||
}
|
||||
|
||||
private static Uri SiteLinkUri = new Uri("http://www.tvsinpagar.com/");
|
||||
private ReleaseInfo _mostRecentRelease;
|
||||
private Regex _searchStringRegex = new Regex(@"(.+?)S0?(\d+)(E0?(\d+))?$", RegexOptions.IgnoreCase);
|
||||
private Regex _titleListRegex = new Regex(@"Serie(.+?)(Temporada(.+?)(\d+)(.+?))?Capitulos?(.+?)(\d+)((.+?)(\d+))?(.+?)-(.+?)Calidad(.*)", RegexOptions.IgnoreCase);
|
||||
private Regex _titleClassicRegex = new Regex(@"(\[[^\]]*\])?\[Cap\.(\d{1,2})(\d{2})([_-](\d{1,2})(\d{2}))?\]", RegexOptions.IgnoreCase);
|
||||
private Regex _titleClassicTvQualityRegex = new Regex(@"\[([^\]]*HDTV[^\]]*)", RegexOptions.IgnoreCase);
|
||||
|
||||
private int _maxDailyPages = 7;
|
||||
private int _maxEpisodesListPages = 100;
|
||||
private int[] _allTvCategories = TorznabCatType.TV.SubCategories.Select(c => c.ID).ToArray();
|
||||
|
||||
private string _dailyUrl = "/ultimas-descargas/pg/{0}";
|
||||
private string[] _seriesLetterUrls = new string[] { "/series/letter/{0}", "/series-hd/letter/{0}" };
|
||||
private string[] _seriesVOLetterUrls = new string[] { "/series-vo/letter/{0}" };
|
||||
private string _seriesUrl = "{0}/pg/{1}";
|
||||
|
||||
public Newpct(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Newpct",
|
||||
description: "Newpct - descargar torrent peliculas, series",
|
||||
link: SiteLinkUri.AbsoluteUri,
|
||||
caps: new TorznabCapabilities(TorznabCatType.TV,
|
||||
TorznabCatType.TVSD,
|
||||
TorznabCatType.TVHD,
|
||||
TorznabCatType.Movies),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationData())
|
||||
{
|
||||
Encoding = Encoding.GetEncoding("windows-1252");
|
||||
Language = "es-es";
|
||||
Type = "public";
|
||||
|
||||
var voItem = new BoolItem() { Name = "Include original versions in search results", Value = false };
|
||||
configData.AddDynamic("IncludeVo", voItem);
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
SiteLinkUri = new Uri(configData.SiteLink.Value);
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Count() > 0, () =>
|
||||
{
|
||||
throw new Exception("Could not find releases from this URL");
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
return await PerformQuery(query, 0);
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var results = await RequestStringWithCookies(link.AbsoluteUri);
|
||||
var content = results.Content;
|
||||
|
||||
Regex regex = new Regex("[^\"]*/descargar-torrent/\\d+_[^\"]*");
|
||||
Match match = regex.Match(content);
|
||||
if (match.Success)
|
||||
link = new Uri(match.Groups[0].Value);
|
||||
else
|
||||
this.logger.Warn("Newpct - download link not found in " + link);
|
||||
|
||||
return await base.Download(link);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query, int attempts)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
bool rssMode = string.IsNullOrEmpty(query.SanitizedSearchTerm);
|
||||
|
||||
if (rssMode)
|
||||
{
|
||||
int pg = 1;
|
||||
while (pg <= _maxDailyPages)
|
||||
{
|
||||
Uri url = new Uri(SiteLinkUri, string.Format(_dailyUrl, pg));
|
||||
var results = await RequestStringWithCookies(url.AbsoluteUri);
|
||||
|
||||
var items = ParseDailyContent(results.Content);
|
||||
if (items == null || !items.Any())
|
||||
break;
|
||||
|
||||
releases.AddRange(items);
|
||||
|
||||
//Check if we need to go to next page
|
||||
bool recentFound = _mostRecentRelease != null &&
|
||||
items.Any(r => r.Title == _mostRecentRelease.Title && r.Link.AbsoluteUri == _mostRecentRelease.Link.AbsoluteUri);
|
||||
if (pg == 1)
|
||||
_mostRecentRelease = (ReleaseInfo)items.First().Clone();
|
||||
if (recentFound)
|
||||
break;
|
||||
|
||||
pg++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Only tv search supported. (newpct web search is useless)
|
||||
bool isTvSearch = query.Categories == null || query.Categories.Length == 0 ||
|
||||
query.Categories.Any(c => _allTvCategories.Contains(c));
|
||||
if (isTvSearch)
|
||||
{
|
||||
return await TvSearch(query);
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> TvSearch(TorznabQuery query)
|
||||
{
|
||||
var newpctReleases = new List<ReleaseInfo>();
|
||||
|
||||
string seriesName = query.SanitizedSearchTerm;
|
||||
int? season = query.Season > 0 ? (int?)query.Season : null;
|
||||
int? episode = null;
|
||||
if (!string.IsNullOrWhiteSpace(query.Episode) && int.TryParse(query.Episode, out int episodeTemp))
|
||||
episode = episodeTemp;
|
||||
|
||||
//If query has no season/episode info, try to parse title
|
||||
if (season == null && episode == null)
|
||||
{
|
||||
Match searchMatch = _searchStringRegex.Match(query.SanitizedSearchTerm);
|
||||
if (searchMatch.Success)
|
||||
{
|
||||
seriesName = searchMatch.Groups[1].Value.Trim();
|
||||
season = int.Parse(searchMatch.Groups[2].Value);
|
||||
episode = searchMatch.Groups[4].Success ? (int?)int.Parse(searchMatch.Groups[4].Value) : null;
|
||||
}
|
||||
}
|
||||
|
||||
//Try to reuse cache
|
||||
bool cacheFound = false;
|
||||
lock (cache)
|
||||
{
|
||||
CleanCache();
|
||||
var cachedResult = cache.FirstOrDefault(i => i.Query == seriesName.ToLower());
|
||||
if (cachedResult != null && cachedResult.Results != null)
|
||||
{
|
||||
cacheFound = true;
|
||||
newpctReleases = cachedResult.Results.Where(r => (r as NewpctRelease) != null).ToList();
|
||||
if (!newpctReleases.Any() && cachedResult.Results.Any())
|
||||
cacheFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cacheFound)
|
||||
{
|
||||
//Search series url
|
||||
foreach (Uri seriesListUrl in SeriesListUris(seriesName))
|
||||
{
|
||||
newpctReleases.AddRange(await GetReleasesFromUri(seriesListUrl, seriesName));
|
||||
}
|
||||
|
||||
//Sonarr removes "the" from shows. If there is nothing try prepending "the"
|
||||
if (newpctReleases.Count == 0 && !(seriesName.ToLower().StartsWith("the")))
|
||||
{
|
||||
seriesName = "The " + seriesName;
|
||||
foreach (Uri seriesListUrl in SeriesListUris(seriesName))
|
||||
{
|
||||
newpctReleases.AddRange(await GetReleasesFromUri(seriesListUrl, seriesName));
|
||||
}
|
||||
}
|
||||
|
||||
//Cache ALL episodes
|
||||
lock (cache)
|
||||
{
|
||||
cache.Add(new CachedQueryResult(seriesName.ToLower(), newpctReleases));
|
||||
}
|
||||
}
|
||||
|
||||
//Filter only episodes needed
|
||||
return newpctReleases.Where(r =>
|
||||
{
|
||||
NewpctRelease nr = r as NewpctRelease;
|
||||
return nr.Season.HasValue != season.HasValue || //Can't determine if same season
|
||||
nr.Season.HasValue && season.Value == nr.Season.Value && //Same season and ...
|
||||
(
|
||||
nr.Episode.HasValue != episode.HasValue || //Can't determine if same episode
|
||||
nr.Episode.HasValue &&
|
||||
(
|
||||
nr.Episode.Value == episode.Value || //Same episode
|
||||
nr.EpisodeTo.HasValue && episode.Value >= nr.Episode.Value && episode.Value <= nr.EpisodeTo.Value //Episode in interval
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> GetReleasesFromUri(Uri uri, string seriesName)
|
||||
{
|
||||
var newpctReleases = new List<ReleaseInfo>();
|
||||
var results = await RequestStringWithCookies(uri.AbsoluteUri);
|
||||
|
||||
//Episodes list
|
||||
string seriesEpisodesUrl = ParseSeriesListContent(results.Content, seriesName);
|
||||
if (!string.IsNullOrEmpty(seriesEpisodesUrl))
|
||||
{
|
||||
int pg = 1;
|
||||
while (pg < _maxEpisodesListPages)
|
||||
{
|
||||
Uri episodesListUrl = new Uri(string.Format(_seriesUrl, seriesEpisodesUrl, pg));
|
||||
results = await RequestStringWithCookies(episodesListUrl.AbsoluteUri);
|
||||
|
||||
var items = ParseEpisodesListContent(results.Content);
|
||||
if (items == null || !items.Any())
|
||||
break;
|
||||
|
||||
newpctReleases.AddRange(items);
|
||||
|
||||
pg++;
|
||||
}
|
||||
}
|
||||
return newpctReleases;
|
||||
}
|
||||
|
||||
private IEnumerable<Uri> SeriesListUris(string seriesName)
|
||||
{
|
||||
IEnumerable<string> lettersUrl;
|
||||
if (!((BoolItem)configData.GetDynamic("IncludeVo")).Value)
|
||||
{
|
||||
lettersUrl = _seriesLetterUrls;
|
||||
}
|
||||
else
|
||||
{
|
||||
lettersUrl = _seriesLetterUrls.Concat(_seriesVOLetterUrls);
|
||||
}
|
||||
string seriesLetter = !char.IsDigit(seriesName[0]) ? seriesName[0].ToString() : "0-9";
|
||||
return lettersUrl.Select(urlFormat =>
|
||||
{
|
||||
return new Uri(SiteLinkUri, string.Format(urlFormat, seriesLetter.ToLower()));
|
||||
});
|
||||
}
|
||||
|
||||
private IEnumerable<NewpctRelease> ParseDailyContent(string content)
|
||||
{
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var doc = SearchResultParser.Parse(content);
|
||||
|
||||
List<NewpctRelease> releases = new List<NewpctRelease>();
|
||||
|
||||
try
|
||||
{
|
||||
var rows = doc.QuerySelectorAll(".content .info");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var anchor = row.QuerySelector("a");
|
||||
var title = anchor.TextContent.Replace("\t", "").Trim();
|
||||
var detailsUrl = anchor.GetAttribute("href");
|
||||
|
||||
var span = row.QuerySelector("span");
|
||||
var quality = span.ChildNodes[0].TextContent.Trim();
|
||||
ReleaseType releaseType = ReleaseTypeFromQuality(quality);
|
||||
var sizeText = span.ChildNodes[1].TextContent.Replace("Tama\u00F1o", "").Trim();
|
||||
|
||||
var div = row.QuerySelector("div");
|
||||
var language = div.ChildNodes[1].TextContent.Trim();
|
||||
|
||||
NewpctRelease newpctRelease;
|
||||
if (releaseType == ReleaseType.TV)
|
||||
newpctRelease = GetReleaseFromData(releaseType,
|
||||
string.Format("Serie {0} - {1} Calidad [{2}]", title, language, quality),
|
||||
detailsUrl, quality, language, ReleaseInfo.GetBytes(sizeText), DateTime.Now);
|
||||
else
|
||||
newpctRelease = GetReleaseFromData(releaseType,
|
||||
string.Format("{0} [{1}][{2}]", title, quality, language),
|
||||
detailsUrl, quality, language, ReleaseInfo.GetBytes(sizeText), DateTime.Now);
|
||||
|
||||
releases.Add(newpctRelease);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private string ParseSeriesListContent(string content, string title)
|
||||
{
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var doc = SearchResultParser.Parse(content);
|
||||
|
||||
Dictionary<string, string> results = new Dictionary<string, string>();
|
||||
|
||||
try
|
||||
{
|
||||
var rows = doc.QuerySelectorAll(".pelilist li a");
|
||||
foreach (var anchor in rows)
|
||||
{
|
||||
var h2 = anchor.QuerySelector("h2");
|
||||
if (h2.TextContent.Trim().ToLower() == title.Trim().ToLower())
|
||||
return anchor.GetAttribute("href");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(content, ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IEnumerable<NewpctRelease> ParseEpisodesListContent(string content)
|
||||
{
|
||||
var SearchResultParser = new HtmlParser();
|
||||
var doc = SearchResultParser.Parse(content);
|
||||
|
||||
List<NewpctRelease> releases = new List<NewpctRelease>();
|
||||
|
||||
try
|
||||
{
|
||||
var rows = doc.QuerySelectorAll(".content .info");
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var anchor = row.QuerySelector("a");
|
||||
var title = anchor.TextContent.Replace("\t", "").Trim();
|
||||
var detailsUrl = anchor.GetAttribute("href");
|
||||
|
||||
var span = row.QuerySelector("span");
|
||||
var pubDateText = row.ChildNodes[3].TextContent.Trim();
|
||||
var sizeText = row.ChildNodes[5].TextContent.Trim();
|
||||
|
||||
long size = ReleaseInfo.GetBytes(sizeText);
|
||||
DateTime publishDate = DateTime.ParseExact(pubDateText, "dd-MM-yyyy", null);
|
||||
NewpctRelease newpctRelease = GetReleaseFromData(ReleaseType.TV, title, detailsUrl, null, null, size, publishDate);
|
||||
|
||||
releases.Add(newpctRelease);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
ReleaseType ReleaseTypeFromQuality(string quality)
|
||||
{
|
||||
if (quality.Trim().ToLower().StartsWith("hdtv"))
|
||||
return ReleaseType.TV;
|
||||
else
|
||||
return ReleaseType.Movie;
|
||||
}
|
||||
|
||||
NewpctRelease GetReleaseFromData(ReleaseType releaseType, string title, string detailsUrl, string quality, string language, long size, DateTime publishDate)
|
||||
{
|
||||
NewpctRelease result = new NewpctRelease();
|
||||
|
||||
//Sanitize
|
||||
title = title.Replace("\t", "").Replace("\x2013", "-");
|
||||
|
||||
Match match = _titleListRegex.Match(title);
|
||||
if (match.Success)
|
||||
{
|
||||
result.SerieName = match.Groups[1].Value.Trim(' ', '-');
|
||||
result.Season = int.Parse(match.Groups[4].Success ? match.Groups[4].Value.Trim() : "1");
|
||||
result.Episode = int.Parse(match.Groups[7].Value.Trim().PadLeft(2, '0'));
|
||||
result.EpisodeTo = match.Groups[10].Success ? (int?)int.Parse(match.Groups[10].Value.Trim()) : null;
|
||||
string audioQuality = match.Groups[12].Value.Trim(' ', '[', ']');
|
||||
quality = match.Groups[13].Value.Trim(' ', '[', ']');
|
||||
|
||||
string seasonText = result.Season.ToString();
|
||||
string episodeText = seasonText + result.Episode.ToString().PadLeft(2, '0');
|
||||
string episodeToText = result.EpisodeTo.HasValue ? "_" + seasonText + result.EpisodeTo.ToString().PadLeft(2, '0') : "";
|
||||
|
||||
result.Title = string.Format("{0} - Temporada {1} [{2}][Cap.{3}{4}][{5}]",
|
||||
result.SerieName, seasonText, quality, episodeText, episodeToText, audioQuality);
|
||||
}
|
||||
else
|
||||
{
|
||||
Match matchClassic = _titleClassicRegex.Match(title);
|
||||
if (matchClassic.Success)
|
||||
{
|
||||
result.Season = matchClassic.Groups[2].Success ? (int?)int.Parse(matchClassic.Groups[2].Value) : null;
|
||||
result.Episode = matchClassic.Groups[3].Success ? (int?)int.Parse(matchClassic.Groups[3].Value) : null;
|
||||
result.EpisodeTo = matchClassic.Groups[6].Success ? (int?)int.Parse(matchClassic.Groups[6].Value) : null;
|
||||
if (matchClassic.Groups[1].Success)
|
||||
quality = matchClassic.Groups[1].Value;
|
||||
}
|
||||
|
||||
result.Title = title;
|
||||
}
|
||||
|
||||
if (releaseType == ReleaseType.TV)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(quality) && (quality.Contains("720") || quality.Contains("1080")))
|
||||
result.Category = new List<int> { TorznabCatType.TVHD.ID };
|
||||
else
|
||||
result.Category = new List<int> { TorznabCatType.TV.ID };
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Title = title;
|
||||
result.Category = new List<int> { TorznabCatType.Movies.ID };
|
||||
}
|
||||
|
||||
result.Size = size;
|
||||
result.Link = new Uri(detailsUrl);
|
||||
result.Guid = result.Link;
|
||||
result.PublishDate = publishDate;
|
||||
result.Seeders = 1;
|
||||
result.Peers = 1;
|
||||
|
||||
result.Title = FixedTitle(result, quality);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string FixedTitle(NewpctRelease release, string quality)
|
||||
{
|
||||
if (String.IsNullOrEmpty(release.SerieName))
|
||||
{
|
||||
release.SerieName = release.Title;
|
||||
if (release.Title.Contains("-"))
|
||||
{
|
||||
release.SerieName = release.Title.Substring(0, release.Title.IndexOf('-') - 1);
|
||||
}
|
||||
}
|
||||
if (String.IsNullOrEmpty(quality))
|
||||
{
|
||||
quality = "HDTV";
|
||||
}
|
||||
var seasonAndEpisode = "S" + release.Season.ToString().PadLeft(2, '0');
|
||||
seasonAndEpisode += "E" + release.Episode.ToString().PadLeft(2, '0');
|
||||
if (release.EpisodeTo != release.Episode && release.EpisodeTo != null && release.EpisodeTo != 0)
|
||||
{
|
||||
seasonAndEpisode += "-" + release.EpisodeTo.ToString().PadLeft(2, '0');
|
||||
}
|
||||
var titleParts = new List<string>();
|
||||
titleParts.Add(release.SerieName);
|
||||
titleParts.Add(seasonAndEpisode);
|
||||
titleParts.Add(quality.Replace("[", "").Replace("]", ""));
|
||||
if (release.Title.ToLower().Contains("esp") || release.Title.ToLower().Contains("cast"))
|
||||
{
|
||||
titleParts.Add("Spanish");
|
||||
}
|
||||
return String.Join(".", titleParts);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -8,6 +9,7 @@ using System.Threading.Tasks;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
@@ -38,7 +40,7 @@ namespace Jackett.Common.Indexers
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithFilterAndPasskey(@"Enter filter options below to restrict search results.
|
||||
Separate options with a space if using more than one option.<br>Filter options available:
|
||||
<br><code>GoldenPopcorn</code><br><code>Scene</code><br><code>Checked</code>"))
|
||||
<br><code>GoldenPopcorn</code><br><code>Scene</code><br><code>Checked</code><br><code>Free</code>"))
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
@@ -98,19 +100,28 @@ namespace Jackett.Common.Indexers
|
||||
bool configGoldenPopcornOnly = configData.FilterString.Value.ToLowerInvariant().Contains("goldenpopcorn");
|
||||
bool configSceneOnly = configData.FilterString.Value.ToLowerInvariant().Contains("scene");
|
||||
bool configCheckedOnly = configData.FilterString.Value.ToLowerInvariant().Contains("checked");
|
||||
string movieListSearchUrl;
|
||||
bool configFreeOnly = configData.FilterString.Value.ToLowerInvariant().Contains("free");
|
||||
|
||||
|
||||
string movieListSearchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("json", "noredirect");
|
||||
|
||||
if (!string.IsNullOrEmpty(query.ImdbID))
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect&searchstr={1}", SearchUrl, WebUtility.UrlEncode(query.ImdbID));
|
||||
queryCollection.Add("searchstr", query.ImdbID);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(query.GetQueryString()))
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect&searchstr={1}", SearchUrl, WebUtility.UrlEncode(query.GetQueryString()));
|
||||
queryCollection.Add("searchstr", query.GetQueryString());
|
||||
}
|
||||
else
|
||||
|
||||
if(configFreeOnly)
|
||||
queryCollection.Add("freetorrent", "1");
|
||||
|
||||
if (queryCollection.Count > 0)
|
||||
{
|
||||
movieListSearchUrl = string.Format("{0}?json=noredirect", SearchUrl);
|
||||
movieListSearchUrl += "?" + queryCollection.GetQueryString();
|
||||
}
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(movieListSearchUrl);
|
||||
|
@@ -116,7 +116,7 @@ namespace Jackett.Common.Indexers
|
||||
//AddCategoryMapping("cat_id", TorznabCatType.AudioForeign);
|
||||
AddCategoryMapping("21", TorznabCatType.PC);
|
||||
AddCategoryMapping("22", TorznabCatType.PC0day);
|
||||
AddCategoryMapping("4", TorznabCatType.PCISO);
|
||||
AddCategoryMapping("1", TorznabCatType.PCISO);
|
||||
AddCategoryMapping("2", TorznabCatType.PCMac);
|
||||
//AddCategoryMapping("cat_id", TorznabCatType.PCPhoneOther);
|
||||
//Games/PC-ISO, Games/PC-Rips
|
||||
@@ -155,7 +155,7 @@ namespace Jackett.Common.Indexers
|
||||
// RSS Textual categories
|
||||
AddCategoryMapping("Anime", TorznabCatType.TVAnime);
|
||||
AddCategoryMapping("Appz/Misc", TorznabCatType.PC0day);
|
||||
AddCategoryMapping("Appz/PC-ISO", TorznabCatType.Books);
|
||||
AddCategoryMapping("Appz/PC-ISO", TorznabCatType.PCISO);
|
||||
AddCategoryMapping("E-Book", TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping("Games/PC-ISO", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("Games/PC-Rips", TorznabCatType.PCGames);
|
||||
@@ -265,6 +265,10 @@ namespace Jackett.Common.Indexers
|
||||
imdbID = l;
|
||||
}
|
||||
}
|
||||
var Now = DateTime.Now;
|
||||
var PublishDate = DateTime.ParseExact(date, "ddd, dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture);
|
||||
var PublishDateLocal = PublishDate.ToLocalTime();
|
||||
var diff = Now - PublishDateLocal;
|
||||
|
||||
var release = new ReleaseInfo()
|
||||
{
|
||||
@@ -272,7 +276,8 @@ namespace Jackett.Common.Indexers
|
||||
Description = title,
|
||||
Guid = new Uri(string.Format(DetailsURL, torrentId)),
|
||||
Comments = new Uri(string.Format(DetailsURL, torrentId)),
|
||||
PublishDate = DateTime.ParseExact(infoMatch.Groups["added"].Value, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31
|
||||
//PublishDate = DateTime.ParseExact(infoMatch.Groups["added"].Value, "yyyy-MM-dd H:mm:ss", CultureInfo.InvariantCulture), //2015-08-08 21:20:31 TODO: correct timezone (always -4)
|
||||
PublishDate = PublishDateLocal,
|
||||
Link = new Uri(link),
|
||||
Seeders = ParseUtil.CoerceInt(infoMatch.Groups["seeders"].Value == "no" ? "0" : infoMatch.Groups["seeders"].Value),
|
||||
Peers = ParseUtil.CoerceInt(infoMatch.Groups["leechers"].Value == "no" ? "0" : infoMatch.Groups["leechers"].Value),
|
||||
@@ -344,6 +349,12 @@ namespace Jackett.Common.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:nth-child(9)").Text());
|
||||
release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:nth-child(10)").Text());
|
||||
|
||||
var grabsStr = qRow.Find("td:nth-child(8)").Text();
|
||||
release.Grabs = ParseUtil.GetLongFromString(grabsStr);
|
||||
|
||||
var filesStr = qRow.Find("td:nth-child(7) > a").Text();
|
||||
release.Files = ParseUtil.GetLongFromString(filesStr);
|
||||
|
||||
var category = qRow.Find(".br_type > a").Attr("href").Replace("browse.php?cat=", string.Empty);
|
||||
release.Category = MapTrackerCatToNewznab(category);
|
||||
}
|
||||
@@ -359,4 +370,4 @@ namespace Jackett.Common.Indexers
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -19,7 +20,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
private string StartPageUrl { get { return SiteLink + "login.php"; } }
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse_API.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "browse.php"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php/{0}/download.torrent"; } }
|
||||
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
@@ -53,13 +54,14 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(59, TorznabCatType.MoviesHD, "Movies/HD");
|
||||
AddCategoryMapping(61, TorznabCatType.Movies, "Movies/Classic");
|
||||
AddCategoryMapping(64, TorznabCatType.Movies3D, "Movies/3D");
|
||||
AddCategoryMapping(78, TorznabCatType.XXX, "0day/XxX");
|
||||
AddCategoryMapping(80, TorznabCatType.MoviesForeign, "Movies/Non-English");
|
||||
AddCategoryMapping(81, TorznabCatType.MoviesBluRay, "Movies/BluRay");
|
||||
AddCategoryMapping(82, TorznabCatType.MoviesOther, "Movies/CAM-TS");
|
||||
AddCategoryMapping(102, TorznabCatType.MoviesOther, "Movies/Remux");
|
||||
AddCategoryMapping(103, TorznabCatType.MoviesWEBDL, "Movies/Web-Rip");
|
||||
AddCategoryMapping(105, TorznabCatType.Movies, "Movies/Kids");
|
||||
AddCategoryMapping(16, TorznabCatType.MoviesUHD, "Movies/4K");
|
||||
AddCategoryMapping(17, TorznabCatType.MoviesBluRay, "Movies/4K bluray");
|
||||
|
||||
//TV
|
||||
AddCategoryMapping(2, TorznabCatType.TVSD, "TV/XviD");
|
||||
@@ -71,6 +73,8 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(100, TorznabCatType.TVFOREIGN, "TV/Non-English");
|
||||
AddCategoryMapping(83, TorznabCatType.TVWEBDL, "TV/Web-Rip");
|
||||
AddCategoryMapping(8, TorznabCatType.TVOTHER, "TV-Mobile");
|
||||
AddCategoryMapping(18, TorznabCatType.TVAnime, "TV/Anime");
|
||||
AddCategoryMapping(19, TorznabCatType.TVHD, "TV-x265");
|
||||
|
||||
// Games
|
||||
AddCategoryMapping(6, TorznabCatType.PCGames, "Games/PC ISO");
|
||||
@@ -164,7 +168,7 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
Dictionary<string, string> qParams = new Dictionary<string, string>();
|
||||
var qParams = new NameValueCollection();
|
||||
qParams.Add("cata", "yes");
|
||||
qParams.Add("sec", "jax");
|
||||
|
||||
@@ -179,7 +183,9 @@ namespace Jackett.Common.Indexers
|
||||
qParams.Add("search", query.GetQueryString());
|
||||
}
|
||||
|
||||
var results = await PostDataWithCookiesAndRetry(SearchUrl, qParams);
|
||||
var searchUrl = SearchUrl + "?" + qParams.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookies(searchUrl);
|
||||
List<ReleaseInfo> releases = ParseResponse(query, results.Content);
|
||||
|
||||
return releases;
|
||||
|
@@ -1,36 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Jackett.Common.Indexers.Abstract;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class Synthesiz3r : GazelleTracker
|
||||
{
|
||||
public Synthesiz3r(IIndexerConfigurationService configService, WebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "Synthesiz3r",
|
||||
desc: "Synthesiz3r (ST3) is a Private Torrent Tracker for ELECTRONIC MUSIC",
|
||||
link: "https://synthesiz3r.com/",
|
||||
configService: configService,
|
||||
logger: logger,
|
||||
protectionService: protectionService,
|
||||
webClient: webClient,
|
||||
supportsFreeleechTokens: true
|
||||
)
|
||||
{
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
TorznabCaps.SupportedMusicSearchParamsList = new List<string>() { "q", "album", "artist", "label", "year" };
|
||||
|
||||
AddCategoryMapping(1, TorznabCatType.Audio, "Music");
|
||||
AddCategoryMapping(2, TorznabCatType.PC, "Applications");
|
||||
AddCategoryMapping(3, TorznabCatType.Books, "E-Books");
|
||||
AddCategoryMapping(4, TorznabCatType.AudioAudiobook, "Audiobooks");
|
||||
AddCategoryMapping(5, TorznabCatType.Movies, "E-Learning Videos");
|
||||
AddCategoryMapping(6, TorznabCatType.TV, "Comedy");
|
||||
AddCategoryMapping(7, TorznabCatType.Books, "Comics");
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using static Jackett.Common.Utils.ParseUtil;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
@@ -195,6 +196,7 @@ namespace Jackett.Common.Indexers
|
||||
if (query.IsTest || string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
var rssPage = await RequestStringWithCookiesAndRetry(string.Format(RSSUrl, configData.RSSKey.Value));
|
||||
rssPage.Content = RemoveInvalidXmlChars(rssPage.Content);
|
||||
var rssDoc = XDocument.Parse(rssPage.Content);
|
||||
|
||||
foreach (var item in rssDoc.Descendants("item"))
|
||||
@@ -213,7 +215,7 @@ namespace Jackett.Common.Indexers
|
||||
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*)\W\/\WSnatched:\W(?<snatched>[\d,]*) x times");
|
||||
if (!infoMatch.Success)
|
||||
throw new Exception(string.Format("Unable to find info in {0}: ", description));
|
||||
|
||||
|
||||
var release = new ReleaseInfo()
|
||||
{
|
||||
Title = title,
|
||||
@@ -279,7 +281,15 @@ namespace Jackett.Common.Indexers
|
||||
release.Guid = new Uri(qRow.Find("td:eq(2) a").Attr("href"));
|
||||
release.Link = release.Guid;
|
||||
release.Comments = new Uri(qRow.Find("td:eq(1) .tooltip-target a").Attr("href"));
|
||||
release.PublishDate = DateTime.ParseExact(qRow.Find("td:eq(1) div").Last().Text().Trim(), "dd-MM-yyyy H:mm", CultureInfo.InvariantCulture); //08-08-2015 12:51
|
||||
var qDate = qRow.Find("td:eq(1) div").Last();
|
||||
var date = qDate.Text().Trim();
|
||||
if (date.StartsWith("Pre Release Time:")) // in some cases the pre time is shown
|
||||
{
|
||||
date = date.Replace("Pre Release Time:", "");
|
||||
date = date.Split(new string[] { "Uploaded:" }, StringSplitOptions.None)[0];
|
||||
date = date.Trim();
|
||||
}
|
||||
release.PublishDate = DateTime.ParseExact(date, "dd-MM-yyyy H:mm", CultureInfo.InvariantCulture); //08-08-2015 12:51
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text());
|
||||
release.Peers = release.Seeders + ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim());
|
||||
release.Size = ReleaseInfo.GetBytes(qRow.Find("td:eq(4)").Text().Trim());
|
||||
|
32
src/Jackett.Common/Indexers/TehConnectionMe.cs
Normal file
32
src/Jackett.Common/Indexers/TehConnectionMe.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
using Jackett.Common.Indexers.Abstract;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class TehConnectionMe : GazelleTracker
|
||||
{
|
||||
public TehConnectionMe(IIndexerConfigurationService configService, WebClient webClient, Logger logger, IProtectionService protectionService)
|
||||
: base(name: "TehConnection.me",
|
||||
desc: "A movies tracker",
|
||||
link: "https://tehconnection.me/",
|
||||
configService: configService,
|
||||
logger: logger,
|
||||
protectionService: protectionService,
|
||||
webClient: webClient,
|
||||
supportsFreeleechTokens: true
|
||||
)
|
||||
{
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping(1, TorznabCatType.Movies, "Feature Film");
|
||||
AddCategoryMapping(2, TorznabCatType.Movies, "Short Film");
|
||||
AddCategoryMapping(3, TorznabCatType.Movies, "Miniseries");
|
||||
AddCategoryMapping(4, TorznabCatType.Movies, "Other");
|
||||
}
|
||||
}
|
||||
}
|
@@ -90,7 +90,7 @@ namespace Jackett.Common.Indexers
|
||||
var loginPage = await RequestStringWithCookies(SiteLink, string.Empty);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, SiteLink, SiteLink);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("my.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var messageEl = dom["td.embedded"].First();
|
||||
@@ -156,7 +156,14 @@ namespace Jackett.Common.Indexers
|
||||
var link = row.Cq().Find("td:eq(1) a:eq(1)").First();
|
||||
release.Guid = new Uri(SiteLink + link.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
release.Title = link.Get(0).FirstChild.ToString();
|
||||
release.Title = link.Attr("title");
|
||||
|
||||
// There isn't a title attribute if the release name isn't truncated.
|
||||
if (string.IsNullOrWhiteSpace(release.Title))
|
||||
{
|
||||
release.Title = link.Get(0).FirstChild.ToString();
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
|
@@ -22,18 +22,21 @@ namespace Jackett.Common.Indexers
|
||||
private string LoginUrl { get { return SiteLink + "tak3login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "t.json"; } }
|
||||
|
||||
public override string[] LegacySiteLinks { get; protected set; } = new string[] {
|
||||
"https://torrentday.com/"
|
||||
};
|
||||
|
||||
public override string[] AlternativeSiteLinks { get; protected set; } = new string[] {
|
||||
"https://tdonline.org/",
|
||||
"https://secure.torrentday.com/",
|
||||
"https://torrentday.eu/",
|
||||
"https://torrentday.it/",
|
||||
"https://classic.torrentday.com/",
|
||||
"https://www.torrentday.com/",
|
||||
"https://td-update.com/",
|
||||
"https://www.torrentday.me/",
|
||||
"https://www.torrentday.ru/",
|
||||
"https://www.torrentday.com/",
|
||||
"https://www.td.af/",
|
||||
"https://torrentday.it/",
|
||||
};
|
||||
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
@@ -45,7 +48,7 @@ namespace Jackett.Common.Indexers
|
||||
public TorrentDay(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "TorrentDay",
|
||||
description: "TorrentDay (TD) is a Private site for TV / MOVIES / GENERAL",
|
||||
link: "https://torrentday.it/",
|
||||
link: "https://www.torrentday.it/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
configService: configService,
|
||||
client: wc,
|
||||
@@ -68,6 +71,7 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(47, TorznabCatType.Other, "Fonts");
|
||||
AddCategoryMapping(43, TorznabCatType.PCMac, "Mac");
|
||||
|
||||
AddCategoryMapping(96, TorznabCatType.MoviesUHD, "Movie/4K");
|
||||
AddCategoryMapping(25, TorznabCatType.MoviesSD, "Movies/480p");
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesBluRay, "Movies/Bluray");
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesBluRay, "Movies/Bluray-Full");
|
||||
@@ -76,7 +80,7 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(22, TorznabCatType.MoviesForeign, "Movies/Non-English");
|
||||
AddCategoryMapping(13, TorznabCatType.Movies, "Movies/Packs");
|
||||
AddCategoryMapping(44, TorznabCatType.MoviesSD, "Movies/SD/x264");
|
||||
AddCategoryMapping(48, TorznabCatType.MoviesUHD, "Movies/x265");
|
||||
AddCategoryMapping(48, TorznabCatType.Movies, "Movies/x265");
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD, "Movies/XviD");
|
||||
|
||||
AddCategoryMapping(17, TorznabCatType.Audio, "Music/Audio");
|
||||
|
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using CsQuery;
|
||||
using Jackett.Common.Models;
|
||||
@@ -17,12 +19,16 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class TorrentLeech : BaseWebIndexer
|
||||
{
|
||||
public override string[] LegacySiteLinks { get; protected set; } = new string[] {
|
||||
"https://v4.torrentleech.org/",
|
||||
};
|
||||
|
||||
private string LoginUrl { get { return SiteLink + "user/account/login/"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents/browse/list/"; } }
|
||||
|
||||
private new ConfigurationDataBasicLogin configData
|
||||
private new ConfigurationDataRecaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
@@ -36,7 +42,7 @@ namespace Jackett.Common.Indexers
|
||||
logger: l,
|
||||
p: ps,
|
||||
downloadBase: "https://www.torrentleech.org/download/",
|
||||
configData: new ConfigurationDataBasicLogin("For best results, change the 'Default Number of Torrents per Page' setting to the maximum in your profile on the TorrentLeech webpage."))
|
||||
configData: new ConfigurationDataRecaptchaLogin("For best results, change the 'Default Number of Torrents per Page' setting to the maximum in your profile on the TorrentLeech webpage."))
|
||||
{
|
||||
Encoding = Encoding.GetEncoding("iso-8859-1");
|
||||
Language = "en-us";
|
||||
@@ -92,9 +98,62 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(38, TorznabCatType.Other, "Education");
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
CQ cq = loginPage.Content;
|
||||
var captcha = cq.Find(".g-recaptcha");
|
||||
if (captcha.Any())
|
||||
{
|
||||
var result = this.configData;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = captcha.Attr("data-sitekey");
|
||||
result.Captcha.Version = "2";
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = new ConfigurationDataBasicLogin();
|
||||
result.SiteLink.Value = configData.SiteLink.Value;
|
||||
result.Instructions.Value = configData.Instructions.Value;
|
||||
result.Username.Value = configData.Username.Value;
|
||||
result.Password.Value = configData.Password.Value;
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "g-recaptcha-response", configData.Captcha.Value }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(configData.Captcha.Cookie))
|
||||
{
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
await DoLogin();
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -110,7 +169,7 @@ namespace Jackett.Common.Indexers
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("/user/account/logout"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["div#login_heading + div.card-panel-error"].Text();
|
||||
var errorMessage = dom["p.text-danger:contains(\"Error:\")"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
}
|
||||
@@ -119,7 +178,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
searchString = searchString.Replace('-', ' '); // remove dashes as they exclude search strings
|
||||
searchString = Regex.Replace(searchString, @"(^|\s)-", " "); // remove dashes at the beginning of keywords as they exclude search strings (see issue #3096)
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchString))
|
||||
|
@@ -164,7 +164,8 @@ namespace Jackett.Common.Indexers
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchTerm = query.GetQueryString();
|
||||
var searchTerm = query.GetEpisodeSearchString() + " " + query.SanitizedSearchTerm; // use episode search string first, see issue #1202
|
||||
searchTerm = searchTerm.Trim();
|
||||
searchTerm = searchTerm.ToLower();
|
||||
|
||||
// Check cache first so we don't query the server (if search term used or not in dev mode)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user