Compare commits

...

327 Commits

Author SHA1 Message Date
flightlevel
9f03f8129a TorrentDay: Correct UHD movies
Fixes https://github.com/Jackett/Jackett/issues/3393
2018-07-14 14:45:40 +10:00
flightlevel
51aa6fdf13 Deprecate libcurl 2018-07-14 14:44:23 +10:00
flightlevel
f2f602dcc5 Mono 5.8 or greater is now required to start Jackett 2018-07-14 14:43:46 +10:00
flightlevel
a217381668 Fix blackhole decoding issue 2018-07-14 14:42:46 +10:00
flightlevel
4bd7befb50 Update to .NET Core 2.1.2 2018-07-13 22:18:11 +10:00
flightlevel
56074155e9 Reverse proxy support for Kestrel 2018-07-13 22:15:09 +10:00
flightlevel
86a5a9cd25 Bump build version 2018-07-12 21:29:44 +10:00
flightlevel
1792ed276e Remove Windows Owin from build 2018-07-12 21:16:38 +10:00
flightlevel
4d1e4b59c8 Update to .NET Core 2.1.1 2018-07-11 19:37:19 +10:00
kaso17
e1ff4b9e12 GkTorrent: update links 2018-07-10 18:44:30 +02:00
kaso17
86400e1b8a Racing4Everyone: fix definition 2018-07-10 18:41:08 +02:00
kaso17
b3d53d1c01 WorldOfP2P: fix login 2018-07-10 18:27:02 +02:00
kaso17
471d494b3b Blutopia: fix definition 2018-07-10 18:09:53 +02:00
flightlevel
6e7d983fc3 Add extra logging 2018-07-10 22:22:02 +10:00
flightlevel
fd1073d0b5 Package update 2018-07-09 21:05:15 +10:00
flightlevel
7815615112 BTXpress: Change music to Audio 2018-07-09 21:00:42 +10:00
flightlevel
a6d8e68ca9 Minor changes for OMDB update 2018-07-09 21:00:17 +10:00
natalia2493
9593ba2414 Update BTXpress (#3365)
* Add btxpress.yml

* Update README.md

* Delete btworld

BTWorld is now known as BTXpress

* Update BTXpress

There was a issue with the selectors which has been fixed

* Update BTXpress

td:nth-child selectors were giving errors.

* Update btxpress.yml
2018-07-09 20:41:56 +10:00
HDVinnie
8c95045a4a (Update) blutopia.yml (#3357)
- added new input field "description" which is used to be able to search into torrent descriptions and media info using keywords
- This doesn't fix #3355 . Im not sure what the issue is. Maybe @kaso17 can check it out. I believe you still have a account there. If not let me know.
2018-07-09 20:41:32 +10:00
Tanel Puhu
667317e513 let's format the Imdb (#3354) 2018-07-09 20:40:37 +10:00
Jay Otterbein
0026a4f26e Allow configuration of omdb url (#3353)
OMDB recently added a private.omdbapi.com domain for supporters and this option allows the user to customize it while also allowing that url to change more rapidly in the future
2018-07-09 20:37:24 +10:00
Ivan de la Beldad Fernandez
2fac90df9f feat(mejortorrent): improve movie search (#3352)
* feat(newpct): rename title to make them more standarized

* fix: add site link configurable

* feat: add tests if news are not available

Sometimes the web has no news, and even if the tv show searcher is
working it throws an error. This way it will check news, and, if not
available, will check some series to check aliveness.

* fix: remove apostrophes from search

closes #3315

* fix: initialize search term if it's null

* fix: add multiepisode parsing and minor improvements

Now a quality it's ensured
Now quality it's cleaned from brackets
Add dash as multiepisode separator

* refactor: move tv search to function and change site link

Sitelink is now a property class

* refactor: extract function to get series uris

* refactor: extract function to get releases from one uri

* feat: add fallback to shows starting with "the"

Whenever a shows doesn't return results and doesn't start with "the"
search one more time prepending "the".

The motivation of this is because of Sonarr sanitizes queries before
sending them to Jackett. This indexer needs the article but Sonarr
removes it before.

* fix(newpct): add missing guid based on link

That was causing missing files on applications consuming the newpct
indexer.

* feat(mejortorrent): add movie search

closes: #3310

* feat(mejortorrent): add parse and filter by year

* feat(mejortorrent): change movie search algorithm

Now movie search is based on longest word, then results are filtered
ignoring special characters.

* fix(mejortorrent): restore query so jackett log it correctly

* fix: add small year correction

Some movies are tagged with the year before or after, I think it's safe to
fix years with exactly the same name and only a year of difference.
2018-07-09 20:35:30 +10:00
flightlevel
424e7b773d Mono 5.8 in readme 2018-07-06 21:04:31 +10:00
ATAlgaba
d25506e595 frozenlayer: Parse seasons greater or equal than 10 (#3341)
* Parse seasons greater than 10

* Update frozenlayer.yml

change * to +
2018-07-06 11:36:02 +02:00
ATAlgaba
a83e90e9ce XbytesV2: replace file languages (#3340)
Replace Xbytes language tag to language name in English
2018-07-06 11:33:49 +02:00
natalia2493
ea93182d96 Add BTXpress (#3339)
* Add btxpress.yml

* Update README.md

* Delete btworld

BTWorld is now known as BTXpress
2018-07-06 11:31:19 +02:00
HDVinnie
d60ee24111 blutopia: remove imdb field (#3313)
This doesn't fix anything but the IMDB link is no longer available
2018-07-04 18:57:57 +02:00
Ivan de la Beldad Fernandez
40e5c999b3 mejortorrent: add parse and filter by year (#3336)
* mejortorrent: add parse and filter by year
2018-07-04 18:54:40 +02:00
kaso17
6175b805d2 update firefox cookie screenshot 2018-07-04 18:42:27 +02:00
kaso17
5b6ba8d75f add firefox cookie screenshot 2018-07-04 18:40:02 +02:00
kaso17
10b0f0b1ce add cookies-chrome screenshot 2018-07-04 18:11:42 +02:00
Ivan de la Beldad Fernandez
a81ef63075 mejortorrent: add movies search (#3334)
* feat(mejortorrent): add movie search

fixes #3310
2018-07-04 17:10:21 +02:00
kaso17
2dc5edbb55 Merge branch 'master' of https://github.com/Jackett/Jackett 2018-07-04 09:49:08 +02:00
kaso17
636be458a6 TehConnection.me: add indexer 2018-07-04 09:48:56 +02:00
kaso17
64283f137c Zooqle: add andmatch 2018-07-04 09:47:48 +02:00
kaso17
ec1c9a9461 Mega-Bliz: add andmatch 2018-07-04 09:47:33 +02:00
Ivan de la Beldad Fernandez
d9a806d236 newpct: add guid (#3329)
* fix(newpct): add missing guid based on link

That was causing missing files on applications consuming the newpct
indexer.
2018-07-03 16:17:16 +02:00
Ivan de la Beldad Fernandez
ebd294b602 feat(newpct): add fallback when no releases are found (#3328)
* refactor: move tv search to function and change site link

Sitelink is now a property class

* refactor: extract function to get series uris

* refactor: extract function to get releases from one uri

* feat: add fallback to shows starting with "the"

Whenever a shows doesn't return results and doesn't start with "the"
search one more time prepending "the".

The motivation of this is because of Sonarr sanitizes queries before
sending them to Jackett. This indexer needs the article but Sonarr
removes it before.
2018-07-03 14:29:06 +02:00
Ivan de la Beldad Fernandez
6081094b73 newpct: fix parser multi-episode and minor improvements (#3327)
* fix: add multiepisode parsing and minor improvements

Now a quality it's ensured
Now quality it's cleaned from brackets
Add dash as multiepisode separator

* fix: prevent an error if cannot guess serie name from title

* fix: now title is being extracted correctly
2018-07-03 11:01:08 +02:00
Ivan de la Beldad Fernandez
115dfd20e7 Mejortorrent: improvements (#3324)
* feat(newpct): rename title to make them more standarized

* fix: add site link configurable

* feat: add tests if news are not available

Sometimes the web has no news, and even if the tv show searcher is
working it throws an error. This way it will check news, and, if not
available, will check some series to check aliveness.

* fix: remove apostrophes from search

closes #3315

* fix: initialize search term if it's null
2018-07-02 19:10:31 +02:00
kaso17
038fe2866b Merge branch 'master' of https://github.com/Jackett/Jackett 2018-07-02 14:44:43 +02:00
kaso17
aec40373d3 New Real World: fix parser 2018-07-02 14:44:34 +02:00
flightlevel
c9b8d27139 Disable .NET Core builds for now 2018-07-02 22:37:48 +10:00
flightlevel
d0793ebcba Merge branch 'master' into dotnetcore 2018-07-02 21:11:13 +10:00
flightlevel
00e4e8109e Reserve all addresses for port 2018-07-02 21:09:42 +10:00
kaso17
bcc1dc1948 fix custom certificate validation handler (#3297)
* fix netcore custom certificate validator

* conditional HttpWebClientNetCore register

* deprecate IgnoreSslErrors option

* Use httpclient when running full framework
2018-07-02 13:05:24 +02:00
flightlevel
1e67996df0 Tidy up logging renderer 2018-07-02 21:03:03 +10:00
Ivan de la Beldad Fernandez
947bed0a46 feat(newpct): rename title to make them more standarized (#3321) 2018-07-02 12:56:53 +02:00
flightlevel
0dd9842e84 Update Tests to net461 2018-07-01 14:40:06 +10:00
flightlevel
42728e2694 Update installer so that it kills Jackett when uninstalling 2018-06-30 22:49:11 +10:00
Celedhrim
44d14dc19c downloadville: fix definition (#3308) 2018-06-29 17:34:21 +02:00
kaso17
2e95c491a1 Blutopia: fix for new layout 2018-06-29 17:33:21 +02:00
kaso17
acfc776462 Torrent9: update domain 2018-06-29 15:16:13 +02:00
kaso17
b6692a7dab LostFilm: fix date parsing 2018-06-29 15:16:04 +02:00
kaso17
34fc2a0d15 cpasbien: update domain 2018-06-29 14:49:26 +02:00
kaso17
07938dcef8 BJShare: enable tracker specific categories 2018-06-29 14:16:41 +02:00
kaso17
847c5dfdcb Frozen Layer: improve title parsing (#3301)
Thank you @IhToN
2018-06-29 14:01:37 +02:00
ATAlgaba
bd47603f0d xbytes2: fix downloadvolumefactor parsing
Update _downloadvolumefactor_ as now they are using .png files
2018-06-28 18:22:00 +02:00
kaso17
8b86146715 YGGtorrent: fix typo 2018-06-28 16:50:45 +02:00
kaso17
5343c9109b YGGtorrent: fix size and add grabs 2018-06-28 16:46:00 +02:00
kaso17
687acae90b Torrent.LT: replace dots 2018-06-28 16:30:57 +02:00
kaso17
871e540957 The New Retro: add support for PIN 2018-06-28 16:21:47 +02:00
Akuma737
b0ce0b5350 metaltracker: Use Artist instead of keyword (#3295)
use artist instead of keyword if artist is supplied to make it somewhat compatible with lidarr(by default supplies artist and album with empty keyword)
2018-06-27 16:31:36 +02:00
flightlevel
bc965b1a0c Merge branch 'master' into dotnetcore 2018-06-27 22:12:53 +10:00
flightlevel
89b60c4c15 Update README.md 2018-06-27 22:11:57 +10:00
flightlevel
47a84775c5 Launch tray icon when Windows service updates 2018-06-27 22:02:51 +10:00
kaso17
8de0b0cbad ProtectionService: fix empty password handling 2018-06-26 17:58:46 +02:00
kaso17
b95fbd76a7 Merge branch 'master' of https://github.com/Jackett/Jackett 2018-06-26 17:47:29 +02:00
kaso17
6b44cc9b74 fix migration logic 2018-06-26 17:47:19 +02:00
flightlevel
0612c70ca1 Mono 5.8 is the minimum required 2018-06-26 20:21:21 +10:00
flightlevel
060972475f Windows Tray/Serice update improvements 2018-06-26 19:44:12 +10:00
kaso17
28bbeec462 MyAnonamouse: improve re-login detection 2018-06-24 18:38:49 +02:00
flightlevel
099adadbdc Don't need to clean BuildOutput 2018-06-24 18:07:41 +10:00
flightlevel
43511c6ecb Merge branch 'master' into dotnetcore 2018-06-24 18:06:09 +10:00
flightlevel
aa3e9c6fde Updater: Add version element 2018-06-24 18:04:59 +10:00
flightlevel
e53cdcb909 Update build script for Kestrel builds 2018-06-24 12:13:33 +10:00
flightlevel
fd14ad3f93 Remove Jackett.Service.Windows from solution file 2018-06-24 12:06:28 +10:00
flightlevel
ca1af97e42 Remove Jackett.Service.Windows
No longer needed now that Jackett.Service covers both scenarios
2018-06-24 12:04:27 +10:00
flightlevel
4970219ea7 Remove call to PlatformAbstractions 2018-06-24 12:00:59 +10:00
flightlevel
54c5b66ecf Merge branch 'master' into dotnetcore 2018-06-24 11:43:02 +10:00
flightlevel
8ae09d453d Remove Microsoft.Extensions.PlatformAbstractions dependency
Was only needed for .NET Core 1
2018-06-24 11:38:57 +10:00
flightlevel
f2e7ec25c8 Update build in preparation for Kestrel builds 2018-06-24 11:32:22 +10:00
flightlevel
7f9fff4683 Jackett.Updater: Make compatible with both legacy & .NET Core 2018-06-24 11:31:08 +10:00
Jorman
13f2eea298 TNTVillage: Update for better regex expression (#3276)
This tracker always respect the format
SXXEYY so I cleaned up the code, I only updated the code adding a simple regex, just in case a rel with XXxYY came up!
2018-06-24 00:50:58 +02:00
kaso17
d6f5a1b242 Merge branch 'master' of https://github.com/Jackett/Jackett 2018-06-23 10:26:31 +02:00
kaso17
ab37f0c2c7 Fuzer: add invisible recaptcha detection 2018-06-23 10:26:02 +02:00
flightlevel
9710b37064 Merge branch 'master' into dotnetcore 2018-06-23 11:39:19 +10:00
flightlevel
54f7568111 InnoSetup: Make paths parameters 2018-06-23 11:38:32 +10:00
flightlevel
06758964c0 Jackett.Service: Make compatible with both legacy & .NET Core 2018-06-23 11:37:49 +10:00
flightlevel
b56552e0f4 Updater: Remove unused reference 2018-06-23 11:36:29 +10:00
kaso17
ef8653f7d2 AST4u: add support for alternative layout 2018-06-22 17:32:44 +02:00
Ivan de la Beldad Fernandez
d5a6987390 add mejortorrent indexer (#3268)
* feat: start mejortorrent indexer, create basic configuration

* feat: add basic tv show scraper for series search

* fix: add guid so more than one episode is returned

* feat: add date to episodes

* feat: add support for multi-episodes

* feat: add proper filtering

* fix: change filter when there is no season or no episode

* feat: implement tv show rss scraper with no links by the moment

* feat: finish tv-shows rss

* fix: prevent sdtv to be marked as hdtv

* docs: add mejortorrent indexer to readme

* add url encode

* fix encoding

Content seems to be UTF8

* add missing include

* fix my fixes

* fix encoding
2018-06-22 16:48:33 +02:00
kaso17
8fb92ca05c Cinemageddon: attempt to fix download 2018-06-22 16:16:26 +02:00
morpheus133
11c7015c17 NCore: fix title generation (#3270) 2018-06-22 16:12:22 +02:00
flightlevel
8bc5d813b7 Tray Icon fix log message 2018-06-22 22:49:17 +10:00
flightlevel
012f5f05cc Update Logging 2018-06-22 22:35:58 +10:00
flightlevel
cd65ec7a71 Merge branch 'master' into dotnetcore 2018-06-22 22:32:48 +10:00
flightlevel
45826df4fe Make Jackett Tray compatible with both legacy & .NET Core projects 2018-06-22 22:29:45 +10:00
flightlevel
0645bab613 .NET Core preparation: Access Windows service 2018-06-22 22:21:10 +10:00
Jorman
4e409dfb50 Update certificate (#3257) 2018-06-19 18:32:22 +10:00
Raffaele
52a39b7a71 Updated categories (#3254)
After the "reboot" of the tracker many categories have changed.
2018-06-19 18:31:51 +10:00
flightlevel
5278b9fb47 Log Asp.Net.Core web requests 2018-06-18 22:01:25 +10:00
flightlevel
1ecafe3667 ResultsController - get BlackHole from ServerConfig without engine 2018-06-18 21:54:39 +10:00
flightlevel
190a415907 Remove unused runtimeSettings parameter 2018-06-18 21:50:09 +10:00
flightlevel
ca609a9e62 Add Legacy routes rewrite 2018-06-18 21:48:45 +10:00
flightlevel
b72ade7b27 Add ListenPublic/Private overrides 2018-06-17 14:39:49 +10:00
flightlevel
e7098d01c5 CommandLineParser: Case insensitive 2018-06-17 14:12:52 +10:00
flightlevel
b04ee56612 Process settings before creating WebHost 2018-06-17 14:02:13 +10:00
flightlevel
72a18e9b73 Get startup configuration without using DI 2018-06-17 12:39:03 +10:00
flightlevel
6be64bbe36 Merge branch 'master' into dotnetcore 2018-06-17 11:49:51 +10:00
flightlevel
cf3848a54f .NET Core preparation: Access ServerConfig without using JackettModule 2018-06-17 11:48:59 +10:00
flightlevel
1a14e8dd4b improve BEncode error handling: Pull in changes from master
3468e7d404
2018-06-16 18:40:31 +10:00
flightlevel
a25eb7f951 Merge branch 'master' into dotnetcore 2018-06-16 18:36:54 +10:00
flightlevel
9437cd54d3 Setup logging before DI 2018-06-16 18:32:08 +10:00
flightlevel
be55c5e4a6 Restart WebHost after port change in UI 2018-06-16 18:06:14 +10:00
kaso17
f1d774aa07 Gazelle: don't use hardcoded encoding 2018-06-15 17:18:47 +02:00
kaso17
c109133fcc Redacted: fix download without FL tokens 2018-06-15 17:14:56 +02:00
kaso17
3538fdfaf7 Synthesiz3r: removed (dead) 2018-06-15 11:58:29 +02:00
kaso17
3468e7d404 improve BEncode error handling 2018-06-15 11:12:03 +02:00
kaso17
ec4afda184 Audiobook Torrents: improve compatibility 2018-06-15 10:44:26 +02:00
kaso17
67b1835264 Bit-City Reloaded: fix login 2018-06-15 10:30:43 +02:00
kaso17
aee64aa589 Shareisland: update URL 2018-06-14 19:28:51 +02:00
kaso17
687e6e237f TorrentWTF: removed (dead) 2018-06-14 19:28:36 +02:00
kaso17
b48dd5e930 cpasbien: URL update 2018-06-14 18:38:07 +02:00
kaso17
5ee6833610 NCore: fix else 2018-06-14 17:53:46 +02:00
morpheus133
c998ba3762 Additional fix for #1450 (#3227)
* Additional fix for #1450
Sonarr is search for a tilte if the result title is different it reject it.
So it is not enough that Jackett give the result it must give with the same language.

Workaround create a new title from the original one and from the title.
It also make some fine tunning:
if title not contains the language we add it from category

* Make decision safer
2018-06-14 17:44:22 +02:00
kaso17
2d4f7ab0e9 Waffles: fix category parsing 2018-06-14 17:39:16 +02:00
kaso17
676d03eb88 mono: redirect workaround 2018-06-14 17:28:57 +02:00
flightlevel
68351a480b Merge branch 'master' into dotnetcore 2018-06-14 19:29:02 +10:00
flightlevel
33e35f1bd3 Re-enable the updater 2018-06-14 19:21:31 +10:00
flightlevel
e82b54994e Update build for Mono 2018-06-14 19:18:17 +10:00
garfield69
6f7ecbfb7b Yggtorrent: domain changed, fixes #3228 2018-06-13 13:01:32 +12:00
Raphael Barreiros
c4aa49eb32 Update BJ-Share to new domain name (#3225)
* Update BJ-Share to new domain name

BJ-Share changed its domain from bj-share.me to bj-share.info

* Added LegacySiteLinks method
2018-06-13 07:44:42 +12:00
garfield69
32aae44ffc Btbit: fix slash in wrong place #3902 2018-06-13 07:04:35 +12:00
garfield69
7883534c5e Btbit: apply sort for all results #3209 2018-06-13 06:49:10 +12:00
kaso17
b58c9fb718 HDHome: try to fix search 2018-06-11 18:31:27 +02:00
kaso17
99d8f63f9e HDChina: try to fix search 2018-06-11 18:31:10 +02:00
la55u
635e8240d2 RevTT: added files count (#3213) 2018-06-11 17:36:29 +02:00
R91g
117a670aa3 Update readme (add HD-Spain tracker) (#3223)
Add new private tracker HD-Spain
2018-06-11 17:33:49 +02:00
R91g
f49c58a1fa HD-Spain: add indexer (#3222)
Add Spanish Private HD tracker https://www.hd-spain.com/
Based on Albvadi's code from HD-Spain forum all credit to him, I only added some changes for better search, ISO correct and spain flag detection
2018-06-11 17:33:26 +02:00
kaso17
2492f1b797 NCore: add comment 2018-06-11 17:31:46 +02:00
kaso17
d6781f67b2 NCore: improve search fix 2018-06-11 17:28:24 +02:00
morpheus133
2e0c22eb6d NCore: fix for #1450 (#3220)
Some workoround to "Ncore - not forward all search results to Sonarr, Radarr"
In case of TV shows if nothing is founded, retry the search without SxxExx after the show name.
This will list all torrent also if their title or description are changed.
Than add the result only if it contains the skipped SxxExx
2018-06-11 17:15:21 +02:00
flightlevel
211e152863 Merge branch 'master' into dotnetcore 2018-06-11 17:30:57 +10:00
flightlevel
f7bf4060ea Don't publish experimental artifacts 2018-06-11 17:21:34 +10:00
flightlevel
8c953bbf01 Avoid Engine for AspNetCore 2018-06-11 17:17:56 +10:00
aurelien
4e91761fdf Elite Tracker: Add HTTPS tracker option (#3217)
* Add option for Elite Tracker (FR) for download torrent using https for tracker URL.

* change return type.
clean code.

* use the SiteLink variable instead of hard coding
2018-06-11 06:42:21 +02:00
kaso17
53f8465e67 Demonoid: change to public 2018-06-10 15:55:51 +02:00
flightlevel
5818b914a3 Include Mono version check 2018-06-10 22:31:55 +10:00
flightlevel
0680d39d90 Torznab: Pull in improved error handling
7f163c3945
2018-06-10 21:56:45 +10:00
flightlevel
4ca6676be0 Blackhole: Pull in fix magnet links
f3290800d8
2018-06-10 21:54:12 +10:00
flightlevel
3d85e751b7 Rewrite rule: Handle no path after base path 2018-06-10 21:52:13 +10:00
flightlevel
965da06214 Merge branch 'master' into dotnetcore 2018-06-10 12:53:02 +10:00
flightlevel
e8bc2816ef Update migration logging 2018-06-10 12:51:34 +10:00
flightlevel
28ed7cc8a5 BJShare: Remove unused variable
Remove warning in build
2018-06-10 12:40:02 +10:00
flightlevel
089d9f2e3d Ignore launchSettings.json 2018-06-10 12:38:32 +10:00
flightlevel
9abce7a586 Update build script so that binaries get pushed 2018-06-10 12:34:37 +10:00
flightlevel
53162b4bd3 Add back Windows Service functionality 2018-06-10 12:33:16 +10:00
flightlevel
80d78a027b Copy ServiceConfigService over untouched 2018-06-10 12:29:07 +10:00
garfield69
b4eda2ed54 Ettv: sort by created for rss, test and no-keyword search #3209 2018-06-09 08:01:24 +12:00
garfield69
4d8d21a815 Btbit: sort by created for rss, test, and no-keyword searches #3209 2018-06-09 07:39:08 +12:00
kaso17
f3290800d8 bloackhole: fix magnet links 2018-06-08 16:04:54 +02:00
kaso17
22a858c076 SceneTime: fix search 2018-06-07 18:33:44 +02:00
kaso17
823419c032 Shareisland: fix legacylinks 2018-06-07 18:19:57 +02:00
Jorman
908d3f64f4 Added Audiobook category (#3203) 2018-06-06 12:44:49 +12:00
flightlevel
3843d68766 Merge branch 'master' into dotnetcore 2018-06-05 21:53:56 +10:00
flightlevel
4b599f391c Mono 5.8 is the minimum supported runtime
#3181 fix. Can get away without using RuntimeInformation and didn't
consider public trackers without a password
2018-06-05 21:47:20 +10:00
kaso17
6d8239caab Demonoid: improve download debugging 2018-06-05 13:34:54 +02:00
Jorman
dae37f273a ilcorsaroblu: Update url (#3202) 2018-06-05 13:21:37 +02:00
kaso17
1615bff2d0 handle RuntimeInformation exception 2018-06-05 13:14:32 +02:00
kaso17
b303befbb9 SceneTime: fix indexer 2018-06-05 13:09:07 +02:00
kaso17
e243c11cc0 update mono reference 2018-06-05 07:24:05 +02:00
Travis Boss
c860bca320 changed log button from btn-danger to btn-success, less scary (#3196) 2018-06-04 14:59:16 +12:00
flightlevel
a60c1fca36 .NET 4.6.1 or Mono 5.4 is now the minimum supported runtime 2018-06-03 21:27:47 +10:00
flightlevel
23f55ef33a Continue moving configuration across 2018-06-03 21:11:18 +10:00
la55u
5ad2c7a371 RevTT: Minor fixes (#3193)
* fixed RevTT pc/iso category

* added grabs count to RevTT
2018-06-03 01:11:42 +02:00
flightlevel
e4c729a588 Merge branch 'master' into dotnetcore 2018-06-02 17:45:27 +10:00
Jorman
3df0218347 Changed link after site fix (#3188) 2018-06-02 17:43:46 +10:00
flightlevel
601783aef6 .NET Core preparation 2 2018-06-02 17:42:01 +10:00
flightlevel
f09c5722be Update to .NET core 2.1 RTM and setup builds 2018-06-02 17:33:26 +10:00
flightlevel
e73ae99e38 Implement AspNetCore.DataProtection to replace DPAPI 2018-05-30 21:43:58 +10:00
flightlevel
10c5c99385 Merge branch 'master' into dotnetcore 2018-05-30 21:30:58 +10:00
flightlevel
ac5af81344 .NET Core preparation (#3177)
The DPAPI won't be present, will be using AspNetCore DataProtection
instead
2018-05-30 21:28:20 +10:00
Celedhrim
823563c84f downloadville: fix typo (#3175)
Fix typo if MULTI replacement is check
2018-05-29 22:07:31 +02:00
Celedhrim
47410c5eb6 yggtorrent: fix typo (#3174)
Fix typo if multi language replacement is activate
2018-05-29 22:07:05 +02:00
kaso17
af135f4ae9 Torrent9: update links 2018-05-29 14:41:08 +02:00
kaso17
3eeced3a04 Racing4Everyone: update links 2018-05-29 14:40:56 +02:00
kaso17
8ea99b548d PolishSource: add login detection 2018-05-29 14:09:47 +02:00
kaso17
ae73e8188d Torrent.LT: update categories
fixes #2279
2018-05-29 13:48:07 +02:00
kaso17
9c5cda72da Demonoid: fix download handling 2018-05-29 13:26:35 +02:00
Jonas Stendahl
fb1e24799d TorrentBytes: Don't use truncated release names (#3168) 2018-05-29 12:24:13 +02:00
bpikap
5721948434 torrent-turk: add indexer (#3161)
* Add tracker torrent-turk

* Add torrent-turk to readme
2018-05-29 12:23:39 +02:00
Jorman
d7b6f413be README: Update for Girotorrent (#3159)
Added new girotorrent
2018-05-29 12:23:01 +02:00
Jorman
959ec4667d Girotorrent:_ add indexer (#3158)
Italian Private Tracker
2018-05-29 12:22:45 +02:00
kaso17
20433db169 RevolutionTT: remove debug output 2018-05-24 16:28:14 +02:00
kaso17
54465798e9 GazelleGames: add relogin detection 2018-05-24 16:13:13 +02:00
kaso17
313147d224 Demonoid: add redirect error detection 2018-05-24 16:05:45 +02:00
kaso17
84bd947eca ICE Torrent: update definition 2018-05-24 16:05:23 +02:00
kaso17
366abc4431 Mega-Bliz: fix and improve definition 2018-05-24 15:45:23 +02:00
DarkSupremo
2f7fa2f063 Bjshare: fix broken regex (#3137) 2018-05-23 15:40:23 +02:00
Celedhrim
205f6cac12 Yggtorrent and downloadville: Better MULTI replace (#3135) 2018-05-23 14:11:22 +02:00
Jorman
f602b3db24 README: add Il Corsaro Blu (#3132)
Update file to adding new traker file
2018-05-23 14:10:34 +02:00
Jorman
0d72f1f228 add tracker Il Corsaro Blu (#3131) 2018-05-23 14:10:13 +02:00
kaso17
508125e68f RGU: fix AND search 2018-05-22 18:52:47 +02:00
kaso17
cfb714e13c TorrentSeeds: add andmatch 2018-05-22 18:30:49 +02:00
kaso17
b9dcfd1b02 Torlock: add andmatch 2018-05-22 18:30:29 +02:00
kaso17
a1b2dc67b8 Elit Tracker: add andmatch 2018-05-22 18:30:10 +02:00
Celedhrim
2207c5a961 yggtorrents and downloadville: support multi to french title rewriting (#3130) 2018-05-22 18:07:15 +02:00
kaso17
2caa09bb1e fix bee 2018-05-21 15:51:07 +02:00
kaso17
68906f6e40 readme: fix link 2018-05-21 12:26:02 +02:00
kaso17
2cf3cf15e3 README: add Roslyn note 2018-05-21 12:24:39 +02:00
Jorman
88202c1f7f Isohunt2: fix size parsing (#3127)
Sometimes when parsing, the size of the rel was not enough to have Gb instead Mb or Kb to Mb or whatever
So the size in row is for example 1.015.22 Mb (less than 1024)
So in this case the parser return error

I searched a way to replace this, I found a solution, hope that works in a log way
2018-05-20 21:08:22 +02:00
DarkSupremo
6293c787e7 Bj share: improve search results (#3126)
* Improved anime search and speed-share resolution detection

* - Code Refactored to new standards
- Removed publish date from search mode, since the tracker does not provide that information, it was based on the serie year (but it does provide it on last 24h page, that's still prssent)
- Code clean
- Added season to all animes but One Piece (every anime that i searched in this tracker have the correct season and episode numbering, except One Piece that have an incorrect season set and episode is in absolute format, its added automatically on every new release, so must be the source from where they get that info that is wrong, since its an popular show, added it as an workaround and explained on code as comment)
2018-05-20 21:07:00 +02:00
flightlevel
f67fda3bf4 Add detail to build steps and remove Bountysource 2018-05-20 21:56:50 +10:00
flightlevel
0a70f91bf1 Target full framework (Mono) as well for transition period 2018-05-20 21:51:52 +10:00
flightlevel
88945be5d7 Start configuration 2018-05-20 21:21:08 +10:00
kaso17
c81dd24fe7 RevolutionTT: add time debug output 2018-05-20 10:10:30 +02:00
kaso17
af94dd2757 TVChaosUK: fix parse error 2018-05-20 09:51:18 +02:00
kaso17
0a07738c5b HDME: fix passkey 2018-05-19 06:33:11 +02:00
kaso17
e05783a25a Cardigann: add support for .Config.sitelink 2018-05-19 06:32:16 +02:00
kaso17
27d4ab3967 TorrentBytes: support hidden userbars 2018-05-18 18:10:32 +02:00
kaso17
04b3efbbc2 Gazelle Trackers: fix freeleech token option 2018-05-17 17:21:46 +02:00
kaso17
ccd5347be2 RGU: fix login 2018-05-17 17:13:59 +02:00
kaso17
e1c5f3ed6d Cardigann: handle unauthorized error code 2018-05-17 16:31:24 +02:00
kaso17
c1fb41204c CloudFlare: increase retries 2018-05-15 12:50:14 +02:00
kaso17
b50733054f RevolutionTT: fix time parsing 2018-05-15 12:46:57 +02:00
kaso17
e324773c91 Union Fansub: fix column parsing for some users
thank you @eduarditotv
2018-05-14 16:53:00 +02:00
kaso17
6a4c34d5df torznab: fix limit parameter 2018-05-14 16:48:57 +02:00
kaso17
bc34b9f176 KickAssTorrent (kat.li): fix zise parsing 2018-05-14 02:32:38 +02:00
kaso17
96af05fbef Torrent9: fix download 2018-05-14 02:23:29 +02:00
kaso17
842d9e79ce YIFI: cleanup 2018-05-14 02:12:09 +02:00
kaso17
086d8b32e9 YIFI: use date_uploaded_unix 2018-05-14 02:11:12 +02:00
kaso17
a6390f2bc5 Hebits: fix download links
fixes #3037
2018-05-13 18:23:40 +02:00
kaso17
1ab0827ae7 Torrenting: add imdbid support 2018-05-13 18:06:50 +02:00
kaso17
db0f651f33 TorrentLeech: improve dash remove code
fixes #3096
2018-05-13 17:42:22 +02:00
kaso17
7f163c3945 improve torznab error handling 2018-05-13 17:15:59 +02:00
kaso17
cb53867b37 Torrent9: remove category option, can't make it work reliable with support for All and others 2018-05-13 15:35:00 +02:00
kaso17
b5b907c9ed RGU: remove dupe submitme 2018-05-13 15:08:09 +02:00
Jachryl
2f8ef4aec2 add rgu tracker (#3084)
* add rgu tracker

* add / to url
2018-05-13 15:00:24 +02:00
kaso17
e5929c850c Updater: remove tehconnection.yml 2018-05-13 14:58:44 +02:00
kaso17
86771a87c6 CzTorrent: use cookie login 2018-05-13 14:58:30 +02:00
flightlevel
5df131140f Upgrade to .NET Core 2.1 RC1 (ARM support) 2018-05-12 13:16:56 +10:00
flightlevel
8002483e68 Add response compression
Reduces dashboard load size from 677kb to 153kb
2018-05-12 12:51:46 +10:00
flightlevel
ce84264490 Add Authorization 2018-05-12 12:44:47 +10:00
flightlevel
fca6ac0dbc Make Configuration buttons clearer 2018-05-12 12:09:37 +10:00
flightlevel
da61eb8988 AnimeBytes username is case sensitive 2018-05-12 11:59:15 +10:00
snamds
ef22d43f46 Add spanish tracker Newpct (#3077)
* Rss mode working

* Fully working. Beta testing

* Added vo and minor changes

* Alternative link support
2018-05-12 11:55:24 +10:00
HDVinnie
f63f1361ce Remove TehConnection From README (#3083) 2018-05-12 11:35:31 +10:00
HDVinnie
547c9174b1 Delete tehconnection.yml (#3082)
- Site went down and came back under Gazelle codebase. This `.yml` is no longer useful.
2018-05-12 11:35:13 +10:00
Garfield69
cb292bbf06 nextorrent: back to original domain #3081 2018-05-10 09:04:04 +12:00
Garfield69
784c41b83a Revert "LostFilm: process the release date for #3087"
This reverts commit 8067f1948e.
2018-05-09 20:13:47 +12:00
Garfield69
42fbe9270a Revert "WiHD: renew deprecated category ids as per #3085"
This reverts commit 628ab0ca82.
2018-05-09 20:08:59 +12:00
Garfield69
8067f1948e LostFilm: process the release date for #3087
instead of the episode air date
2018-05-09 19:17:03 +12:00
Garfield69
143cc6e8d0 btworld: fix for #3090 2018-05-09 15:54:53 +12:00
Garfield69
628ab0ca82 WiHD: renew deprecated category ids as per #3085 2018-05-09 15:41:50 +12:00
proton-ab
cc13d7edf6 Update AnimeBytes required passkey length (#3079)
Allow length of 48 for passkey for future changes in AnimeBytes codebase.
2018-05-08 17:25:04 +12:00
proton-ab
6ea2c18384 Update AnimeBytes passkey note message (#3078) 2018-05-08 17:24:43 +12:00
Garfield69
fb316d9068 kickasstorrent-kathow: make category selector optional to prevent error #3072 2018-05-08 14:52:05 +12:00
Garfield69
fda730dcad nextorrent: fix the downloadlink and make seeds/leech 999 #3081 2018-05-08 14:21:54 +12:00
flightlevel
8d921202fa Animebytes username requirement (#3076) 2018-05-06 10:36:53 +10:00
flightlevel
615794a4bf Minor controller updates as the web app expects no content 2018-05-05 17:10:36 +10:00
flightlevel
5eed9d7038 Add middleware: Exception handling and rewrite/redirect 2018-05-05 17:08:46 +10:00
Kicker83
e6b135e151 Added new replace (#3068)
xbytesv2 adds two years in title, when the show has season episodes released in two different years. For example, supergirl season 3, has episodes in 2017 and 2018. In this case xbytesv2 format the title like this: "Supergirl (2017 18/S03/E13/AMZ WEB DL 1080p/AC3 5 1 /DUAL/SUB) Grupo V2" 
This new filter, deletes both years. 
Please don't remove this replace:
          - name: re_replace
            args: ["S(\\d{1,2}) E(\\d{1,2})", "S$1E$2"]
2018-05-05 18:03:54 +12:00
mitsu375
dbde3b469a Create btworld.yml (#3055) 2018-05-03 16:10:58 +12:00
mitsu375
7841bcab06 Update README.md (#3057) 2018-05-03 16:10:43 +12:00
Garfield69
efe1346d41 torrentDownloads: add optional .torrent or magnet download 2018-05-02 17:34:46 +12:00
flightlevel
f162902b36 Refactor controllers for ASP.NET Core (Authentication disabled for now) 2018-05-01 22:55:09 +10:00
flightlevel
a752683965 Copy controllers untouced from Jackett project 2018-05-01 22:03:16 +10:00
flightlevel
683bd6e2d4 Add DI and Initialisation 2018-05-01 22:00:02 +10:00
flightlevel
13426fe7ec Refactor services for ASP.NET Core 2018-05-01 21:41:34 +10:00
flightlevel
d87d0f87b1 Add packages 2018-05-01 21:26:42 +10:00
flightlevel
85ec169755 Copy Services (except Windows Service) untouched from Jackett project 2018-05-01 21:17:59 +10:00
flightlevel
9cc40144a9 Add new DotNET Core API project Jackett.Server 2018-05-01 21:13:20 +10:00
flightlevel
42beb6018e DotNet Core preparation (#3046)
.NET core uses HttpRequest instead of HttpRequestMessage
2018-05-01 21:06:18 +10:00
kaso17
2d0e82159d AnimeBytes: more fixes 2018-04-27 16:52:21 +02:00
kaso17
08ad94a2f5 RuTracker: update regex 2018-04-27 16:36:57 +02:00
kaso17
0eebea0ef7 cpasbien: update URL 2018-04-27 16:32:48 +02:00
kaso17
e4888b83d8 AnimeBytes: finish new API 2018-04-27 16:32:26 +02:00
kaso17
37f8066901 TorrentDay: make .it the default again 2018-04-27 15:50:49 +02:00
kaso17
92f976916f AnimeBytes: update config items 2018-04-26 19:30:21 +02:00
kaso17
530ad7ecf5 AnimeBytes: migrate to new API (useless reults for now) 2018-04-26 19:30:03 +02:00
kaso17
c1bc750059 allow other options to be edited with in case of a recaptcha 2018-04-26 19:29:03 +02:00
Kicker83
364061fde0 Fixes and optimizations (#3029)
Fixes and optimizations suggested by kaso17
2018-04-26 13:09:18 +00:00
kaso17
1b4826f966 BaseIndexer: fix potato API 2018-04-25 20:35:35 +02:00
kaso17
580eacdb18 TorrentDay: add .it domain again 2018-04-25 19:11:51 +02:00
kaso17
270b8c9041 AnimeBytes: disable login 2018-04-24 23:54:21 +02:00
kaso17
9f7590783d TorrentDay: add missing / 2018-04-24 23:51:57 +02:00
kaso17
63dceed010 AnimeBytes: disable indexer 2018-04-24 23:49:38 +02:00
Garfield69
ee3e0dd0e1 scenetime: add cat 19 tv-hd hevc/x265 resolves #3020 2018-04-25 07:50:56 +12:00
Robin Edbom
e19d59c4ab Fix for #3010 (#3014)
* Fix for #3010

This will fix the problem with torrentday.it being down and https:///torrentday.com will redirect you to https://www.torrentday.com. And since we don't allow redirects, this will not work without the www.

* Added the feedback from #3014
2018-04-24 12:21:59 +12:00
kaso17
d29132a540 TorrentLeech: add v4 site link to legacy list 2018-04-23 18:19:50 +02:00
kaso17
0037811fb5 JPopsuki: fix error detection 2018-04-23 17:52:57 +02:00
kaso17
1f1b8d0074 3D Torrents: fix DL link 2018-04-23 17:37:15 +02:00
kaso17
dae6aeb4b5 TorrentDay: disable .it domain 2018-04-23 17:13:57 +02:00
Ivan PIdov
9bfb249425 Zamunda.net: Improve torrent names (#3003) 2018-04-23 15:06:22 +00:00
kaso17
0bbcecc1c3 PTP: add free option 2018-04-23 16:33:29 +02:00
kaso17
a871e35449 BeyondHD: enable tracker specific categories 2018-04-23 16:33:08 +02:00
Brian Hardisty
9961031b0e BeyondHD: Add new / missing categories (#3005)
This adds the following categories:

- 102: Movie / 4K Disk
- 103: Movie / 4K Remux
-  38: Movie / WEB-DL
- 106: Internal / BHDStudio 1080p
- 105: Internal / BHDStudio 720p
- 104: TV Show / 4K
-  99: TV Show / Sports
- 100: TV Show / Sports / WEB-DL

Additionally 94: `Movie / 4K` was renamed to `Movie / 4K Other` to
match the updated category name on the tracker.
2018-04-23 14:26:35 +00:00
Connor Smith
c2d3214c01 Grey's Anatomy manual fix for 1337x (#2998)
Implementing Magico fix for Grey's Anatomy as 1337x does the same with apostrophes.
2018-04-23 14:21:44 +00:00
Garfield69
13e14f3e62 ettv: add optional .torrent download 2018-04-20 19:02:31 +12:00
kaso17
ffe435051b Merge branch 'master' of https://github.com/Jackett/Jackett 2018-04-18 11:44:36 +02:00
kaso17
5e00b2ec33 Freedom-HD: improve re-login detection 2018-04-18 11:44:17 +02:00
Akuma737
a73fbccd22 Anidex: Add language id support (#2988) 2018-04-18 09:32:58 +00:00
kaso17
48cdc3bb5e TorrentLeech: fix error message 2018-04-17 20:56:01 +02:00
kaso17
535f71c18c TorrentLeech: add recaptcha handling 2018-04-17 13:55:18 +02:00
kaso17
9ceeb71448 BaseIndexer: fix meta indexer fallback decision 2018-04-17 12:43:00 +02:00
kaso17
457449866e UpdateService: first update check after 1 hour 2018-04-17 11:35:34 +02:00
kaso17
427428d09b AlphaRatio: remove dots from search string 2018-04-17 11:03:40 +02:00
kaso17
82b203c9c3 update readme 2018-04-17 10:38:40 +02:00
kaso17
9677c2deee Merge branch 'master' of https://github.com/Jackett/Jackett 2018-04-17 10:38:07 +02:00
kaso17
9000745877 Torrent9: fix category 2018-04-17 10:37:53 +02:00
DarkSupremo
2fb413123c Improved anime search and speed-share resolution detection (#2973) 2018-04-16 14:38:20 +12:00
Garfield69
6316b82600 worldwidetorrents: andmatch 2018-04-15 18:55:18 +12:00
Garfield69
2746d797ee cpabien: domain change 2018-04-15 12:36:30 +12:00
Garfield69
7e5a7ad153 torrentdownloads: fix date from "2017y ago" to "0m ago" 2018-04-14 15:51:28 +12:00
kaso17
e947059da1 HDBits: attempt to fix download 2018-04-13 20:27:34 +02:00
kaso17
65aaaa3d77 Racing4Everyone: fix categories 2018-04-13 18:07:00 +02:00
HDVinnie
c2b2c94e1b Racing4Everyone: add definition (#2965)
* (Create) racing4everyone.yml

- I have yet to test this. If anyone is willing to please do. Currently is open reg for 3 more days.

* (Update) Add R4E to README

- added racing4everyone (R4E)
2018-04-13 14:48:33 +00:00
Harald Weber
e8465f23b9 Bittorrentfiles: Fixed indexer (#2964) 2018-04-12 10:49:50 +00:00
kaso17
09d37ab232 GFTracker: removed (dead) 2018-04-11 18:45:30 +02:00
kaso17
f413355610 NextTorrent: update site link 2018-04-11 18:43:25 +02:00
kaso17
5a97cd7dd2 Andraste: removed (dead) 2018-04-11 18:39:24 +02:00
kaso17
7b354d1582 TVChaosUK: add support for pre times 2018-04-11 18:36:22 +02:00
kaso17
b964a33ae2 Xthor: use episode search string first (3) 2018-04-11 18:15:15 +02:00
kaso17
dfa513da24 Xthor: use episode search string first (2) 2018-04-11 18:14:25 +02:00
kaso17
dcee9128f9 Xthor: search for 2018-04-11 18:12:06 +02:00
kaso17
30a8e1b4c5 FileList: update sitelink 2018-04-11 18:06:18 +02:00
173 changed files with 17150 additions and 13304 deletions

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

1
.gitignore vendored
View File

@@ -200,3 +200,4 @@ FakesAssemblies/
/TestResults
*.DS_Store
.idea/
launchSettings.json

View File

@@ -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

View File

@@ -2,7 +2,6 @@
[![GitHub issues](https://img.shields.io/github/issues/Jackett/Jackett.svg?maxAge=60&style=flat-square)](https://github.com/Jackett/Jackett/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/Jackett/Jackett.svg?maxAge=60&style=flat-square)](https://github.com/Jackett/Jackett/pulls)
[![Bountysource](https://img.shields.io/bountysource/team/jackett/activity.svg?style=flat-square)](https://www.bountysource.com/teams/jackett)
[![Build status](https://ci.appveyor.com/api/projects/status/gaybh5mvyx418nsp/branch/master?svg=true)](https://ci.appveyor.com/project/Jackett/jackett)
[![Github Releases](https://img.shields.io/github/downloads/Jackett/Jackett/total.svg?maxAge=60&style=flat-square)](https://github.com/Jackett/Jackett/releases/latest)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/jackett.svg?maxAge=60&style=flat-square)](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

View File

@@ -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

View File

@@ -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
//////////////////////////////////////////////////////////////////////

View File

@@ -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,

View File

@@ -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>

View File

@@ -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/"]

View File

@@ -48,9 +48,6 @@
- selector: span.errormsg
test:
path: index.php
download:
selector: a[href^="download.php?id="]
search:
paths:

View File

@@ -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"

View File

@@ -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:

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -87,6 +87,7 @@
- selector: div.myFrame:has(font.error)
test:
path: torrents-search.php
selector: a.logout
search:
paths:

View File

@@ -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

View 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", ""]

View File

@@ -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/

View File

@@ -33,9 +33,6 @@
- selector: table.main:contains("Login Failed!")
test:
path: my.php
download:
selector: a[href^="/download.php"]
search:
paths:

View File

@@ -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}}"

View File

@@ -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}}"

View File

@@ -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"

View 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"]

View File

@@ -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

View 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)

View File

@@ -9,6 +9,7 @@
- https://ilcorsaronero.info/
certificates:
- aa7c40aa360a1cec8a9687312fd50402b912e618 # incomplete CA chain
- 83174ec1f92fa13cdef9d51888ea1dfba2166e17 # incomplete CA chain
caps:
categorymappings:

View File

@@ -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"

View File

@@ -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

View File

@@ -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:

View File

@@ -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.

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View 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"

View 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"

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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"

View File

@@ -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:

View File

@@ -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

View File

@@ -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 }}"

View File

@@ -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

View File

@@ -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:

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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"));
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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}");
}
}
}

View File

@@ -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 = "";

View File

@@ -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)

View File

@@ -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");

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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> {

View File

@@ -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;
}
}
}

View File

@@ -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";

View File

@@ -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();

View File

@@ -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; }

View File

@@ -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);

View 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);
}
}
}
}
}

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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();

View 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);
}
}
}

View File

@@ -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);

View File

@@ -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;
}
}
}
}

View File

@@ -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;

View File

@@ -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");
}
}
}

View File

@@ -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());

View 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");
}
}
}

View File

@@ -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.

View File

@@ -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");

View File

@@ -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))

View File

@@ -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