Compare commits
385 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
599d1c9c91 | ||
![]() |
37dae02a1b | ||
![]() |
441023a336 | ||
![]() |
56d7d42836 | ||
![]() |
59b90d3896 | ||
![]() |
4283ea5e67 | ||
![]() |
32fa8cf5fd | ||
![]() |
412df7c6f6 | ||
![]() |
e275ebba87 | ||
![]() |
c38a2533e1 | ||
![]() |
bb6f56bfa6 | ||
![]() |
82ae611c81 | ||
![]() |
64e537e77d | ||
![]() |
245855c2ab | ||
![]() |
186136cc86 | ||
![]() |
0295bc68ab | ||
![]() |
9da7866321 | ||
![]() |
9930430026 | ||
![]() |
e56ff204f8 | ||
![]() |
6a3374ea86 | ||
![]() |
c134e72979 | ||
![]() |
d84bd074c0 | ||
![]() |
51b4326920 | ||
![]() |
253bda68e9 | ||
![]() |
7fa558c743 | ||
![]() |
b01bf3e669 | ||
![]() |
dd4eaed4fa | ||
![]() |
ac4787e292 | ||
![]() |
88002d4b53 | ||
![]() |
f30ba7fd59 | ||
![]() |
470b212f32 | ||
![]() |
3ab45960da | ||
![]() |
870c0592fe | ||
![]() |
da2a252848 | ||
![]() |
88f73f185c | ||
![]() |
d3cef38f15 | ||
![]() |
5292e8f342 | ||
![]() |
ef637792c8 | ||
![]() |
09a2e8c084 | ||
![]() |
d3161823ad | ||
![]() |
289844099c | ||
![]() |
98a916b4c1 | ||
![]() |
69ff142596 | ||
![]() |
80fd717970 | ||
![]() |
ab083cdec8 | ||
![]() |
e16454f30c | ||
![]() |
23c13969ae | ||
![]() |
dcc3f2df63 | ||
![]() |
10f7f83ecf | ||
![]() |
b5e6eac3e3 | ||
![]() |
9df337c3c2 | ||
![]() |
d2f0e5ada7 | ||
![]() |
d3aa897b1a | ||
![]() |
0abcf401bb | ||
![]() |
560d257f5b | ||
![]() |
1a619e698a | ||
![]() |
52aea3de5a | ||
![]() |
8fa1268de2 | ||
![]() |
5a4c1282ef | ||
![]() |
3aecc53b5f | ||
![]() |
328f652e1b | ||
![]() |
52a56f5088 | ||
![]() |
5e4c64bf68 | ||
![]() |
930e9db67a | ||
![]() |
27627aa79e | ||
![]() |
ddb94e3f92 | ||
![]() |
f1f5b7028d | ||
![]() |
90959c1fc3 | ||
![]() |
3f79aa62d9 | ||
![]() |
cd1d30fc4f | ||
![]() |
3093651f89 | ||
![]() |
d969305cbe | ||
![]() |
22119dab16 | ||
![]() |
23958ffba7 | ||
![]() |
d9673501fc | ||
![]() |
f9341818da | ||
![]() |
22ab8aca3d | ||
![]() |
dd79de1321 | ||
![]() |
d8035d4245 | ||
![]() |
4642fa4da0 | ||
![]() |
58ee30aa99 | ||
![]() |
5922795587 | ||
![]() |
5eccf08690 | ||
![]() |
037d907028 | ||
![]() |
7cfdaf4b41 | ||
![]() |
e7918bddbc | ||
![]() |
6b253178b6 | ||
![]() |
87448eea31 | ||
![]() |
5e2fa1408e | ||
![]() |
809362e08c | ||
![]() |
c8831b157e | ||
![]() |
2198aebf73 | ||
![]() |
f3f8c2aea5 | ||
![]() |
83c231749d | ||
![]() |
79f3b96c1d | ||
![]() |
fb1c016704 | ||
![]() |
a3febf8b85 | ||
![]() |
254cec98ad | ||
![]() |
9a2ae6aa24 | ||
![]() |
840037aa7f | ||
![]() |
dd24f12eac | ||
![]() |
d1e767bd41 | ||
![]() |
72d3f2ea49 | ||
![]() |
5e8e620df9 | ||
![]() |
0a61887474 | ||
![]() |
2002b9db9d | ||
![]() |
e54e99d8d6 | ||
![]() |
c3a5c7afa3 | ||
![]() |
6f38081cf1 | ||
![]() |
58b35b290b | ||
![]() |
8c1086719d | ||
![]() |
4074ae197b | ||
![]() |
3381d67565 | ||
![]() |
e3c7f3de28 | ||
![]() |
70dfe3a021 | ||
![]() |
377dc3f586 | ||
![]() |
b1a4a98563 | ||
![]() |
366b1ff80b | ||
![]() |
05abb0151e | ||
![]() |
1bf52b8c1e | ||
![]() |
f0a302f7f4 | ||
![]() |
a027bfae54 | ||
![]() |
6576770fa0 | ||
![]() |
45f6b9f768 | ||
![]() |
3dc7aabb6e | ||
![]() |
0f5e18492c | ||
![]() |
2f140fd2c0 | ||
![]() |
661c5f2120 | ||
![]() |
28043e0070 | ||
![]() |
e6bf8918a3 | ||
![]() |
4737abcf58 | ||
![]() |
f7d96c108a | ||
![]() |
96017dd3bc | ||
![]() |
94fd201507 | ||
![]() |
3dbe5774d7 | ||
![]() |
0a577e64d1 | ||
![]() |
64bed2a8b3 | ||
![]() |
2b9b4282f4 | ||
![]() |
595f006f22 | ||
![]() |
0abec9c1b6 | ||
![]() |
6867c4a234 | ||
![]() |
4d2759bcd2 | ||
![]() |
982c3df1ac | ||
![]() |
3dfe2faa6b | ||
![]() |
21cffe2d35 | ||
![]() |
fa9bbaa18c | ||
![]() |
769ceb016f | ||
![]() |
51cb8762b1 | ||
![]() |
f04a97f237 | ||
![]() |
2a020f691c | ||
![]() |
5abdd35e1f | ||
![]() |
5a04e66abe | ||
![]() |
0c02c22654 | ||
![]() |
cd68d06dc3 | ||
![]() |
465e607415 | ||
![]() |
cbf6e45f64 | ||
![]() |
c95d5fd9e2 | ||
![]() |
5991fd62c1 | ||
![]() |
5384f85b5a | ||
![]() |
c33315f59c | ||
![]() |
7eaff55955 | ||
![]() |
71414b805b | ||
![]() |
50a06f640f | ||
![]() |
6cab5fbba8 | ||
![]() |
66e3f8eea5 | ||
![]() |
a26e07210f | ||
![]() |
e612a826d0 | ||
![]() |
4d2240ec6e | ||
![]() |
50fb9ccb5f | ||
![]() |
28d3f22fef | ||
![]() |
8612387e39 | ||
![]() |
7d93361839 | ||
![]() |
9c7ce468ee | ||
![]() |
1ad0e93d01 | ||
![]() |
b53cbce24a | ||
![]() |
087635f22a | ||
![]() |
0263a5f869 | ||
![]() |
b910e42668 | ||
![]() |
33b3740fca | ||
![]() |
6d1d78cb92 | ||
![]() |
d4c9c6d82b | ||
![]() |
485d2705b5 | ||
![]() |
ecd2b33dfc | ||
![]() |
c12736716d | ||
![]() |
c0bdedbae1 | ||
![]() |
2abf4f0ee6 | ||
![]() |
1fdd165d2e | ||
![]() |
55818f9cb6 | ||
![]() |
a4c670df78 | ||
![]() |
c8d558c1eb | ||
![]() |
f56b51e9e3 | ||
![]() |
2e966b674c | ||
![]() |
aa1e31a110 | ||
![]() |
79418d3b1f | ||
![]() |
76f01273d3 | ||
![]() |
ef99687187 | ||
![]() |
5d03e6ec99 | ||
![]() |
3e9165ce4a | ||
![]() |
2a7a950d63 | ||
![]() |
d3d78e48cb | ||
![]() |
791471a716 | ||
![]() |
7f7f6680b2 | ||
![]() |
128591d355 | ||
![]() |
593c2f083e | ||
![]() |
7743267501 | ||
![]() |
268f5a8ae5 | ||
![]() |
121736358b | ||
![]() |
1b0ea00c88 | ||
![]() |
4be1c19c50 | ||
![]() |
aa55849e62 | ||
![]() |
1d2093fc32 | ||
![]() |
5a5d83e5c1 | ||
![]() |
155632e85d | ||
![]() |
cac185f747 | ||
![]() |
6d0d502bd3 | ||
![]() |
ccde6fb53b | ||
![]() |
2afce9f2d9 | ||
![]() |
2647457706 | ||
![]() |
ac5e69a3b2 | ||
![]() |
45fb2a27c3 | ||
![]() |
fb3d7ae81b | ||
![]() |
9b62e8af71 | ||
![]() |
3f292b5e47 | ||
![]() |
bd2abddb09 | ||
![]() |
5135748d1d | ||
![]() |
0fc3d224ab | ||
![]() |
ac07cc34cd | ||
![]() |
3730e05f20 | ||
![]() |
2644fd813e | ||
![]() |
ece16d1075 | ||
![]() |
3b13fa84a4 | ||
![]() |
cda5ea3207 | ||
![]() |
0746616b43 | ||
![]() |
28199ab4be | ||
![]() |
b29c578adb | ||
![]() |
b42f2a0972 | ||
![]() |
040deb2bfb | ||
![]() |
ef8b4e685e | ||
![]() |
364860199c | ||
![]() |
3f2a6fd3f7 | ||
![]() |
2671cf00e0 | ||
![]() |
6a6941d01c | ||
![]() |
5b3862bc3d | ||
![]() |
0452f5ad06 | ||
![]() |
264fc995b2 | ||
![]() |
acc75acb9c | ||
![]() |
a5b1332f95 | ||
![]() |
8871a631b1 | ||
![]() |
bfb58f53f5 | ||
![]() |
d14717c88e | ||
![]() |
d8ff110d8b | ||
![]() |
23737d3b19 | ||
![]() |
b11516760b | ||
![]() |
3a7f8ce268 | ||
![]() |
7a5b2e5c6a | ||
![]() |
ead129eda9 | ||
![]() |
b7cd0aeca8 | ||
![]() |
708b45b02f | ||
![]() |
75f4342499 | ||
![]() |
24ad51ad15 | ||
![]() |
ecc3dd26db | ||
![]() |
aecee29219 | ||
![]() |
bda73dc9c1 | ||
![]() |
63d2407e4f | ||
![]() |
baf44314e9 | ||
![]() |
29ef28b6d7 | ||
![]() |
86dad52919 | ||
![]() |
6ff05656ef | ||
![]() |
71c195cafb | ||
![]() |
dda0ae2485 | ||
![]() |
69dc63c726 | ||
![]() |
ee65721da1 | ||
![]() |
8ffb91f414 | ||
![]() |
50d931b4fb | ||
![]() |
6f475b18f3 | ||
![]() |
782211d06a | ||
![]() |
4f5d7a3d54 | ||
![]() |
f26f2d6f25 | ||
![]() |
6ccbfd6443 | ||
![]() |
c896ed8238 | ||
![]() |
1879ed89df | ||
![]() |
11f99a44d3 | ||
![]() |
f8fcf2fb79 | ||
![]() |
c36a3f558a | ||
![]() |
07a88919b4 | ||
![]() |
d02cb3fefc | ||
![]() |
f05eca3a9f | ||
![]() |
ccc2441a55 | ||
![]() |
5aaa402287 | ||
![]() |
97849dfcaf | ||
![]() |
f2a899eea3 | ||
![]() |
80686c81ee | ||
![]() |
189483b2b7 | ||
![]() |
e7cc147121 | ||
![]() |
73f044c0f2 | ||
![]() |
a268893475 | ||
![]() |
c20e4d0dfa | ||
![]() |
73536b11e2 | ||
![]() |
a35695358d | ||
![]() |
7312c8c230 | ||
![]() |
04fea52956 | ||
![]() |
cd642a48b6 | ||
![]() |
89e0498224 | ||
![]() |
35f9c05cb9 | ||
![]() |
8a54f9d825 | ||
![]() |
34caa78de1 | ||
![]() |
e28fadbd68 | ||
![]() |
0930048d8b | ||
![]() |
42495e36e5 | ||
![]() |
4f36d19b00 | ||
![]() |
2f682ca53f | ||
![]() |
aa7a33496c | ||
![]() |
1f94dec089 | ||
![]() |
e57cbe3b44 | ||
![]() |
7b74f981e3 | ||
![]() |
2901cceea9 | ||
![]() |
ea4d0fe701 | ||
![]() |
02a57533f9 | ||
![]() |
99f0e9c9f6 | ||
![]() |
d1225c17b2 | ||
![]() |
2adecae9bf | ||
![]() |
c7aa0d7b0f | ||
![]() |
a69ecf3e06 | ||
![]() |
9414e7a4b8 | ||
![]() |
d9473781d3 | ||
![]() |
4952199e68 | ||
![]() |
388460993d | ||
![]() |
f21c1f0400 | ||
![]() |
4db62bbf63 | ||
![]() |
42ec634cd3 | ||
![]() |
a07de4d1e9 | ||
![]() |
0361a88856 | ||
![]() |
a88f248b75 | ||
![]() |
82f06d0b46 | ||
![]() |
10e019a1dd | ||
![]() |
173e26c054 | ||
![]() |
24f8d26b2d | ||
![]() |
8ceb8ebbe7 | ||
![]() |
71c583d359 | ||
![]() |
9d4e8f4bda | ||
![]() |
65d2a88591 | ||
![]() |
a04d296b55 | ||
![]() |
79452053ee | ||
![]() |
7ad5fac9c0 | ||
![]() |
2456a42608 | ||
![]() |
3c396ba880 | ||
![]() |
65e4d1e068 | ||
![]() |
e643134f09 | ||
![]() |
19bc3c7b36 | ||
![]() |
a50e31ec3f | ||
![]() |
ff96389864 | ||
![]() |
efd5a8cc98 | ||
![]() |
d5c59109b1 | ||
![]() |
4a9fcdbc9b | ||
![]() |
6bea0b009a | ||
![]() |
ff86a15ddd | ||
![]() |
d204ce6f39 | ||
![]() |
6bc011f678 | ||
![]() |
acdf198b05 | ||
![]() |
5f2dfcf499 | ||
![]() |
aa69d1a3b9 | ||
![]() |
29bde68337 | ||
![]() |
e9ae2d4cd8 | ||
![]() |
0ba6188e4b | ||
![]() |
283a003aef | ||
![]() |
a50ab9c49b | ||
![]() |
b4e42b4180 | ||
![]() |
f7efd83e09 | ||
![]() |
f67381d0ac | ||
![]() |
af0c15be2c | ||
![]() |
4ce6f6b048 | ||
![]() |
828091128e | ||
![]() |
f8c0b9a80f | ||
![]() |
f4129dc4a0 | ||
![]() |
f6f27e604a | ||
![]() |
bc1dd0e9e8 | ||
![]() |
6b1dba2dc3 | ||
![]() |
e4a47927b9 | ||
![]() |
226c27f903 | ||
![]() |
5acaf9dbb8 | ||
![]() |
a856aba953 | ||
![]() |
ce351dd3aa | ||
![]() |
99125386f0 | ||
![]() |
44d6ac2f04 | ||
![]() |
b5ff430e2d |
83
README.md
@@ -2,9 +2,9 @@
|
||||
|
||||
This project is a new fork and is recruiting development help. If you are able to help out please contact us.
|
||||
|
||||
Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.
|
||||
Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.
|
||||
|
||||
Developer note: The software implements the [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/master/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) APIs.
|
||||
Developer note: The software implements the [Torznab](https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer) (with [nZEDb](https://github.com/nZEDb/nZEDb/blob/dev/docs/newznab_api_specification.txt) category numbering) and [TorrentPotato](https://github.com/RuudBurger/CouchPotatoServer/wiki/Couchpotato-torrent-provider) APIs.
|
||||
|
||||
|
||||
|
||||
@@ -14,55 +14,114 @@ Developer note: The software implements the [Torznab](https://github.com/Sonarr/
|
||||
|
||||
|
||||
#### Supported Private Trackers
|
||||
* Abnormal
|
||||
* AlphaRatio
|
||||
* AlphaReign
|
||||
* Andraste
|
||||
* AnimeBytes
|
||||
* AnimeTorrents
|
||||
* Apollo (XANAX)
|
||||
* Avistaz
|
||||
* BakaBT
|
||||
* bB
|
||||
* Best Friends
|
||||
* BeyondHD
|
||||
* Bit-City Reloaded
|
||||
* BIT-HDTV
|
||||
* BitMeTV
|
||||
* BlueTigers
|
||||
* BitSoup
|
||||
* Blu-bits
|
||||
* BTN
|
||||
* CinemaZ
|
||||
* DanishBits
|
||||
* Demonoid
|
||||
* EuTorrents
|
||||
* DigitalHive
|
||||
* EoT-Forum
|
||||
* Ethor.net (Thor's Land)
|
||||
* FileList
|
||||
* Freaks Tracking System
|
||||
* Freshon
|
||||
* FunFile
|
||||
* Fuzer
|
||||
* Ghost City
|
||||
* HD4Free
|
||||
* HD-Space
|
||||
* HD-Torrents
|
||||
* Hebits
|
||||
* New Real World
|
||||
* Hounddawgs
|
||||
* House-of-Torrents
|
||||
* ILoveTorrents
|
||||
* Immortalseed
|
||||
* IPTorrents
|
||||
* MoreThanTV
|
||||
* MyAnonamouse
|
||||
* myAmity
|
||||
* NCore
|
||||
* NextGen
|
||||
* PassThePopcorn
|
||||
* PirateTheNet
|
||||
* Pretome
|
||||
* PrivateHD
|
||||
* RevolutionTT
|
||||
* SceneAccess
|
||||
* SceneFZ
|
||||
* SceneTime
|
||||
* SDBits
|
||||
* Secret Cinema
|
||||
* Shareisland
|
||||
* Shazbat
|
||||
* SpeedCD
|
||||
* The Horror Charnel
|
||||
* The New Retro
|
||||
* The Shinning
|
||||
* TehConnection
|
||||
* Torrent Network
|
||||
* Torrent Sector Crew
|
||||
* TorrentBD
|
||||
* TorrentBytes
|
||||
* TorrentDay
|
||||
* TorrentHeaven
|
||||
* TorrentLeech
|
||||
* TorrentShack
|
||||
* Torrent-Syndikat
|
||||
* TransmitheNet
|
||||
* Tspate
|
||||
* TV Chaos UK
|
||||
* TV-Vault
|
||||
* UHDBits
|
||||
* World-In-HD
|
||||
* WorldOfP2P
|
||||
* x264
|
||||
* XSpeeds
|
||||
* Xthor
|
||||
|
||||
#### Installation on Windows
|
||||
|
||||
We recommend you install Jackett as a Windows service using the supplied installer. When installed as a service the tray icon acts as a way to open/start/stop Jackett. If you opted to not install it as a service then Jackett will run its web server from the tray tool.
|
||||
We recommend you install Jackett as a Windows service using the supplied installer. You may also download the zipped version if you would like to configure everything manually.
|
||||
|
||||
Jackett can also be run from the command line using JackettConsole.exe if you would like to see log messages (Ensure the server isn't already running from the tray/service).
|
||||
To get started with using the installer for Jackett, follow the steps below:
|
||||
|
||||
1. Download the latest version of the Windows installer, "Jackett.Installer.Windows.exe" from the [releases](https://github.com/Jackett/Jackett/releases/latest) page.
|
||||
2. When prompted if you would like this app to make changes to your computer, select "yes".
|
||||
3. If you would like to install Jackett as a Windows Service, make sure the "Install as Windows Service" checkbox is filled.
|
||||
4. Once the installation has finished, check the "Launch Jackett" box to get started.
|
||||
5. Navigate your web browser to: http://127.0.0.1:9117
|
||||
6. You're now ready to begin adding your trackers and using Jackett.
|
||||
|
||||
When installed as a service the tray icon acts as a way to open/start/stop Jackett. If you opted to not install it as a service then Jackett will run its web server from the tray tool.
|
||||
|
||||
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/OSX
|
||||
1. Install [Mono 4](http://www.mono-project.com/download/) or better
|
||||
* Follow the instructions on the mono website and install the `mono-devel` package.
|
||||
2. Install libcurl:
|
||||
* Debian/Ubunutu: apt-get install libcurl-dev
|
||||
* Redhat/Fedora: yum install libcurl-devel
|
||||
* Debian/Ubunutu: `apt-get install libcurl-dev`
|
||||
* Redhat/Fedora: `yum install libcurl-devel`
|
||||
* For other distros see the [Curl docs](http://curl.haxx.se/dlwiz/?type=devel).
|
||||
3. Download and extract the latest ```.tar.gz``` release from the [releases page](https://github.com/Jackett/Jackett/releases) and run Jackett using mono with the command "mono JackettConsole.exe".
|
||||
|
||||
3. Download and extract the latest `Jackett.Binaries.Mono.tar.gz` release from the [releases page](https://github.com/Jackett/Jackett/releases) and run Jackett using mono with the command `mono JackettConsole.exe`.
|
||||
|
||||
Detailed instructions for [Ubuntu 14.x](http://www.htpcguides.com/install-jackett-on-ubuntu-14-x-for-custom-torrents-in-sonarr/) and [Ubuntu 15.x](http://www.htpcguides.com/install-jackett-ubuntu-15-x-for-custom-torrents-in-sonarr/)
|
||||
|
||||
#### Installation on Synology
|
||||
@@ -86,9 +145,9 @@ You can get additional logging with the switches "-t -l". Please post logs if y
|
||||
Please supply as much information about the problem you are experiencing as possible. Your issue has a much greater chance of being resolved if logs are supplied so that we can see what is going on. Creating an issue with '### isn't working' doesn't help anyone to fix the problem.
|
||||
|
||||
### Contributing
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/zone117x/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project. We currently only support private trackers.
|
||||
All contributions are welcome just send a pull request. Jackett's framework allows our team (and any other volunteering dev) to implement new trackers in an hour or two. If you'd like support for a new tracker but are not a developer then feel free to leave a request on the [issues page](https://github.com/Jackett/Jackett/issues). It is recommended to use Visual studio 2015 when making code changes in this project. We currently only support private trackers.
|
||||
|
||||
|
||||
### Screenshots
|
||||
|
||||

|
||||

|
||||
|
@@ -7,7 +7,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
@@ -27,6 +27,9 @@ namespace Jackett.Console
|
||||
[Option('c', "UseClient", HelpText = "Override web client selection. [automatic(Default)/libcurl/safecurl/httpclient]")]
|
||||
public string Client { get; set; }
|
||||
|
||||
[Option('j', "ProxyConnection", HelpText = "use proxy - e.g. 127.0.0.1:8888")]
|
||||
public string ProxyConnection { get; set; }
|
||||
|
||||
[Option('s', "Start", HelpText = "Start the Jacket Windows service (Must be admin)")]
|
||||
public bool StartService { get; set; }
|
||||
|
||||
@@ -54,9 +57,12 @@ namespace Jackett.Console
|
||||
[Option('f', "SSLFix", HelpText = "[true/false] Linux Libcurl NSS Missing ECC Ciphers workaround (Use if you can't access some trackers) .")]
|
||||
public bool? SSLFix { get; set; }
|
||||
|
||||
[Option('n', "IgnoreSslErrors", HelpText = "[true/false] Linux Libcurl - Ignores invalid SSL certificates")]
|
||||
[Option('n', "IgnoreSslErrors", HelpText = "[true/false] Ignores invalid SSL certificates")]
|
||||
public bool? IgnoreSslErrors { get; set; }
|
||||
|
||||
[Option('d', "DataFolder", HelpText = "Specify the location of the data folder (Must be admin on Windows) eg. --DataFolder=\"D:\\Your Data\\Jackett\\\"")]
|
||||
public string DataFolder { get; set; }
|
||||
|
||||
[ParserState]
|
||||
public IParserState LastParserState { get; set; }
|
||||
}
|
||||
|
@@ -94,12 +94,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -1,220 +1,234 @@
|
||||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
using Jackett;
|
||||
using Jackett.Console;
|
||||
using Jackett.Indexers;
|
||||
using Jackett.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JackettConsole
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new ConsoleOptions();
|
||||
if (!Parser.Default.ParseArguments(args, options) || options.ShowHelp == true)
|
||||
{
|
||||
if (options.LastParserState != null && options.LastParserState.Errors.Count > 0)
|
||||
{
|
||||
var help = new HelpText();
|
||||
var errors = help.RenderParsingErrorsText(options, 2); // indent with two spaces
|
||||
Console.WriteLine("Jackett v" + Engine.ConfigService.GetVersion());
|
||||
Console.WriteLine("Switch error: " + errors);
|
||||
Console.WriteLine("See --help for further details on switches.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var text = HelpText.AutoBuild(options, (HelpText current) => HelpText.DefaultParsingErrorsHandler(options, current));
|
||||
text.Copyright = " ";
|
||||
text.Heading = "Jackett v" + Engine.ConfigService.GetVersion() + " options:";
|
||||
Console.WriteLine(text);
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (options.ListenPublic && options.ListenPrivate)
|
||||
{
|
||||
Console.WriteLine("You can only use listen private OR listen publicly.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
/* ====== Options ===== */
|
||||
|
||||
// SSL Fix
|
||||
Startup.DoSSLFix = options.SSLFix;
|
||||
|
||||
// Use curl
|
||||
if (options.Client != null)
|
||||
Startup.ClientOverride = options.Client.ToLowerInvariant();
|
||||
|
||||
// Logging
|
||||
if (options.Logging)
|
||||
Startup.LogRequests = true;
|
||||
|
||||
// Tracing
|
||||
if (options.Tracing)
|
||||
Startup.TracingEnabled = true;
|
||||
|
||||
// Log after the fact as using the logger will cause the options above to be used
|
||||
|
||||
if (options.Logging)
|
||||
Engine.Logger.Info("Logging enabled.");
|
||||
|
||||
if (options.Tracing)
|
||||
Engine.Logger.Info("Tracing enabled.");
|
||||
|
||||
if (options.SSLFix == true)
|
||||
Engine.Logger.Info("SSL ECC workaround enabled.");
|
||||
else if (options.SSLFix == false)
|
||||
Engine.Logger.Info("SSL ECC workaround has been disabled.");
|
||||
|
||||
// Ignore SSL errors on Curl
|
||||
Startup.IgnoreSslErrors = options.IgnoreSslErrors;
|
||||
if (options.IgnoreSslErrors == true)
|
||||
{
|
||||
Engine.Logger.Info("Curl will ignore SSL certificate errors.");
|
||||
}
|
||||
|
||||
/* ====== Actions ===== */
|
||||
|
||||
// Install service
|
||||
if (options.Install)
|
||||
{
|
||||
Engine.ServiceConfig.Install();
|
||||
return;
|
||||
}
|
||||
|
||||
// Uninstall service
|
||||
if (options.Uninstall)
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: false);
|
||||
Engine.ServiceConfig.Uninstall();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reserve urls
|
||||
if (options.ReserveUrls)
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Service
|
||||
if (options.StartService)
|
||||
{
|
||||
if (!Engine.ServiceConfig.ServiceRunning())
|
||||
{
|
||||
Engine.ServiceConfig.Start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop Service
|
||||
if (options.StopService)
|
||||
{
|
||||
if (Engine.ServiceConfig.ServiceRunning())
|
||||
{
|
||||
Engine.ServiceConfig.Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate settings
|
||||
if (options.MigrateSettings)
|
||||
{
|
||||
Engine.ConfigService.PerformMigration();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Show Version
|
||||
if (options.ShowVersion)
|
||||
{
|
||||
Console.WriteLine("Jackett v" + Engine.ConfigService.GetVersion());
|
||||
return;
|
||||
}
|
||||
|
||||
/* ====== Overrides ===== */
|
||||
|
||||
// Override listen public
|
||||
if (options.ListenPublic || options.ListenPrivate)
|
||||
{
|
||||
if (Engine.Server.Config.AllowExternal != options.ListenPublic)
|
||||
{
|
||||
Engine.Logger.Info("Overriding external access to " + options.ListenPublic);
|
||||
Engine.Server.Config.AllowExternal = options.ListenPublic;
|
||||
if (System.Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
{
|
||||
if (ServerUtil.IsUserAdministrator())
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Unable to switch to public listening without admin rights.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// Override port
|
||||
if (options.Port != 0)
|
||||
{
|
||||
if (Engine.Server.Config.Port != options.Port)
|
||||
{
|
||||
Engine.Logger.Info("Overriding port to " + options.Port);
|
||||
Engine.Server.Config.Port = options.Port;
|
||||
if (System.Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
{
|
||||
if (ServerUtil.IsUserAdministrator())
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Unable to switch ports when not running as administrator");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.Initalize();
|
||||
Engine.Server.Start();
|
||||
Engine.RunTime.Spin();
|
||||
Engine.Logger.Info("Server thread exit");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e, "Top level exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
using Jackett;
|
||||
using Jackett.Console;
|
||||
using Jackett.Indexers;
|
||||
using Jackett.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JackettConsole
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new ConsoleOptions();
|
||||
if (!Parser.Default.ParseArguments(args, options) || options.ShowHelp == true)
|
||||
{
|
||||
if (options.LastParserState != null && options.LastParserState.Errors.Count > 0)
|
||||
{
|
||||
var help = new HelpText();
|
||||
var errors = help.RenderParsingErrorsText(options, 2); // indent with two spaces
|
||||
Console.WriteLine("Jackett v" + Engine.ConfigService.GetVersion());
|
||||
Console.WriteLine("Switch error: " + errors);
|
||||
Console.WriteLine("See --help for further details on switches.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var text = HelpText.AutoBuild(options, (HelpText current) => HelpText.DefaultParsingErrorsHandler(options, current));
|
||||
text.Copyright = " ";
|
||||
text.Heading = "Jackett v" + Engine.ConfigService.GetVersion() + " options:";
|
||||
Console.WriteLine(text);
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (options.ListenPublic && options.ListenPrivate)
|
||||
{
|
||||
Console.WriteLine("You can only use listen private OR listen publicly.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
/* ====== Options ===== */
|
||||
|
||||
// SSL Fix
|
||||
Startup.DoSSLFix = options.SSLFix;
|
||||
|
||||
// Use curl
|
||||
if (options.Client != null)
|
||||
Startup.ClientOverride = options.Client.ToLowerInvariant();
|
||||
|
||||
// Use Proxy
|
||||
if (options.ProxyConnection != null)
|
||||
{
|
||||
Startup.ProxyConnection = options.ProxyConnection.ToLowerInvariant();
|
||||
Engine.Logger.Info("Proxy enabled. " + Startup.ProxyConnection);
|
||||
}
|
||||
// Logging
|
||||
if (options.Logging)
|
||||
Startup.LogRequests = true;
|
||||
|
||||
// Tracing
|
||||
if (options.Tracing)
|
||||
Startup.TracingEnabled = true;
|
||||
|
||||
// Log after the fact as using the logger will cause the options above to be used
|
||||
|
||||
if (options.Logging)
|
||||
Engine.Logger.Info("Logging enabled.");
|
||||
|
||||
if (options.Tracing)
|
||||
Engine.Logger.Info("Tracing enabled.");
|
||||
|
||||
if (options.SSLFix == true)
|
||||
Engine.Logger.Info("SSL ECC workaround enabled.");
|
||||
else if (options.SSLFix == false)
|
||||
Engine.Logger.Info("SSL ECC workaround has been disabled.");
|
||||
|
||||
// Ignore SSL errors on Curl
|
||||
Startup.IgnoreSslErrors = options.IgnoreSslErrors;
|
||||
if (options.IgnoreSslErrors == true)
|
||||
{
|
||||
Engine.Logger.Info("Jackett will ignore SSL certificate errors.");
|
||||
}
|
||||
|
||||
// Choose Data Folder
|
||||
if (!string.IsNullOrWhiteSpace(options.DataFolder))
|
||||
{
|
||||
Startup.CustomDataFolder = options.DataFolder.Replace("\"", string.Empty).Replace("'", string.Empty).Replace(@"\\", @"\");
|
||||
Engine.Logger.Info("Jackett Data will be stored in: " + Startup.CustomDataFolder);
|
||||
}
|
||||
|
||||
/* ====== Actions ===== */
|
||||
|
||||
// Install service
|
||||
if (options.Install)
|
||||
{
|
||||
Engine.ServiceConfig.Install();
|
||||
return;
|
||||
}
|
||||
|
||||
// Uninstall service
|
||||
if (options.Uninstall)
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: false);
|
||||
Engine.ServiceConfig.Uninstall();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reserve urls
|
||||
if (options.ReserveUrls)
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Service
|
||||
if (options.StartService)
|
||||
{
|
||||
if (!Engine.ServiceConfig.ServiceRunning())
|
||||
{
|
||||
Engine.ServiceConfig.Start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop Service
|
||||
if (options.StopService)
|
||||
{
|
||||
if (Engine.ServiceConfig.ServiceRunning())
|
||||
{
|
||||
Engine.ServiceConfig.Stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Migrate settings
|
||||
if (options.MigrateSettings)
|
||||
{
|
||||
Engine.ConfigService.PerformMigration();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Show Version
|
||||
if (options.ShowVersion)
|
||||
{
|
||||
Console.WriteLine("Jackett v" + Engine.ConfigService.GetVersion());
|
||||
return;
|
||||
}
|
||||
|
||||
/* ====== Overrides ===== */
|
||||
|
||||
// Override listen public
|
||||
if (options.ListenPublic || options.ListenPrivate)
|
||||
{
|
||||
if (Engine.Server.Config.AllowExternal != options.ListenPublic)
|
||||
{
|
||||
Engine.Logger.Info("Overriding external access to " + options.ListenPublic);
|
||||
Engine.Server.Config.AllowExternal = options.ListenPublic;
|
||||
if (System.Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
{
|
||||
if (ServerUtil.IsUserAdministrator())
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Unable to switch to public listening without admin rights.");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// Override port
|
||||
if (options.Port != 0)
|
||||
{
|
||||
if (Engine.Server.Config.Port != options.Port)
|
||||
{
|
||||
Engine.Logger.Info("Overriding port to " + options.Port);
|
||||
Engine.Server.Config.Port = options.Port;
|
||||
if (System.Environment.OSVersion.Platform != PlatformID.Unix)
|
||||
{
|
||||
if (ServerUtil.IsUserAdministrator())
|
||||
{
|
||||
Engine.Server.ReserveUrls(doInstall: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Unable to switch ports when not running as administrator");
|
||||
Environment.ExitCode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.SaveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Server.Initalize();
|
||||
Engine.Server.Start();
|
||||
Engine.RunTime.Spin();
|
||||
Engine.Logger.Info("Server thread exit");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e, "Top level exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -21,7 +21,7 @@
|
||||
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -75,12 +75,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -62,12 +62,12 @@
|
||||
<HintPath>..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.1.1\lib\net45\FluentAssertions.dll</HintPath>
|
||||
<Reference Include="FluentAssertions, Version=4.13.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.1.1\lib\net45\FluentAssertions.Core.dll</HintPath>
|
||||
<Reference Include="FluentAssertions.Core, Version=4.13.0.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\FluentAssertions.4.13.0\lib\net45\FluentAssertions.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
@@ -87,12 +87,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.Hosting.3.0.1\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.core, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
@@ -103,8 +103,8 @@
|
||||
<HintPath>..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.interfaces.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.0.5813.39031, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.0.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Reference Include="nunit.framework, Version=3.4.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.util, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
@@ -164,6 +164,7 @@
|
||||
<Compile Include="TestIIndexerManagerServiceHelper.cs" />
|
||||
<Compile Include="TestUtil.cs" />
|
||||
<Compile Include="TestWebClient.cs" />
|
||||
<Compile Include="Util\ParseUtilTests.cs" />
|
||||
<Compile Include="Util\ServerUtilTests.cs" />
|
||||
<Compile Include="Util\TvCategoryParserTests.cs" />
|
||||
</ItemGroup>
|
||||
@@ -182,6 +183,9 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="Indexers\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Util\Invalid-RSS.xml" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||
<ItemGroup>
|
||||
|
@@ -42,5 +42,15 @@ namespace JackettTest
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void InitCardigannIndexers(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SortIndexers()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
src/Jackett.Test/Util/Invalid-RSS.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>RSS Syndicator</title>
|
||||
<link>http://somewebsite.com</link>
|
||||
<description>
|
||||
<br />
|
||||
Enjoy!<br />
|
||||
<br />
|
||||
-<br />
|
||||
<br />
|
||||
group info<br />
|
||||
<br />
|
||||
Know Your Role and Shut Your Mouth!<br />
|
||||
<br />
|
||||
we are now looking for...<br />
|
||||
<br />
|
||||
</description>
|
||||
</channel>
|
||||
</rss>
|
46
src/Jackett.Test/Util/ParseUtilTests.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using FluentAssertions;
|
||||
using Jackett.Utils;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace JackettTest.Util
|
||||
{
|
||||
[TestFixture]
|
||||
public class ParseUtilTests
|
||||
{
|
||||
private static string InvalidRssXml
|
||||
{
|
||||
get
|
||||
{
|
||||
var type = typeof(ParseUtilTests);
|
||||
using (var resourceStream = type.Assembly.GetManifestResourceStream($"{type.Namespace}.Invalid-RSS.xml"))
|
||||
using (var sr = new StreamReader(resourceStream))
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Invalid_RSS_should_parse_after_removing_invalid_chars()
|
||||
{
|
||||
var invalidRss = InvalidRssXml;
|
||||
Action parseAction = () => XDocument.Parse(invalidRss);
|
||||
parseAction.ShouldThrow<Exception>().WithMessage("'\a', hexadecimal value 0x07, is an invalid character. Line 12, position 7.");
|
||||
|
||||
var validRSs = ParseUtil.RemoveInvalidXmlChars(invalidRss);
|
||||
var rssDoc = XDocument.Parse(validRSs);
|
||||
rssDoc.Root.Should().NotBeNull();
|
||||
var description = rssDoc.Root.XPathSelectElement("//description");
|
||||
description.Value.Should().Contain("Know Your Role and Shut Your Mouth!");
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<package id="Autofac.WebApi2.Owin" version="3.3.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="4.1.1" targetFramework="net45" />
|
||||
<package id="CsQuery" version="1.3.4" targetFramework="net45" />
|
||||
<package id="FluentAssertions" version="4.1.1" targetFramework="net45" />
|
||||
<package id="FluentAssertions" version="4.13.0" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
|
||||
@@ -18,9 +18,9 @@
|
||||
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="NUnit" version="3.4.1" targetFramework="net45" />
|
||||
<package id="NUnitTestAdapter" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
|
@@ -79,12 +79,12 @@
|
||||
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
</packages>
|
@@ -23,7 +23,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -39,12 +39,16 @@
|
||||
<ApplicationIcon>jackett.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Autofac.3.5.2\lib\net40\Autofac.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.3.7\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
@@ -1,149 +1,150 @@
|
||||
using CommandLine;
|
||||
using Jackett.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett.Updater
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new Program().Run(args);
|
||||
}
|
||||
|
||||
private void Run(string[] args)
|
||||
{
|
||||
Engine.Logger.Info("Jackett Updater v" + GetCurrentVersion());
|
||||
|
||||
try {
|
||||
var options = new UpdaterConsoleOptions();
|
||||
if (Parser.Default.ParseArguments(args, options))
|
||||
{
|
||||
ProcessUpdate(options);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Failed to process update arguments!: " + string.Join(" ", args));
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e, "Exception applying update!");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCurrentVersion()
|
||||
{
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
|
||||
return fvi.FileVersion;
|
||||
}
|
||||
|
||||
private void ProcessUpdate(UpdaterConsoleOptions options)
|
||||
{
|
||||
var updateLocation = GetUpdateLocation();
|
||||
if(!(updateLocation.EndsWith("\\") || updateLocation.EndsWith("/")))
|
||||
{
|
||||
updateLocation += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
var isWindows = System.Environment.OSVersion.Platform != PlatformID.Unix;
|
||||
var trayRunning = false;
|
||||
var trayProcesses = Process.GetProcessesByName("JackettTray");
|
||||
if (isWindows)
|
||||
{
|
||||
if (trayProcesses.Count() > 0)
|
||||
{
|
||||
foreach (var proc in trayProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
Engine.Logger.Info("Killing tray process " + proc.Id);
|
||||
proc.Kill();
|
||||
trayRunning = true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Logger.Info("Waiting for Jackett to close..");
|
||||
Thread.Sleep(2000);
|
||||
|
||||
var files = Directory.GetFiles(updateLocation, "*.*", SearchOption.AllDirectories);
|
||||
foreach(var file in files)
|
||||
{
|
||||
var fileName = Path.GetFileName(file).ToLowerInvariant();
|
||||
|
||||
if (fileName.EndsWith(".zip") ||
|
||||
fileName.EndsWith(".tar") ||
|
||||
fileName.EndsWith(".gz"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Engine.Logger.Info("Copying " + fileName);
|
||||
var dest = Path.Combine(options.Path, file.Substring(updateLocation.Length));
|
||||
File.Copy(file, dest, true);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (trayRunning)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
Arguments = options.Args,
|
||||
FileName = Path.Combine(options.Path, "JackettTray.exe"),
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
if(string.Equals(options.Type, "JackettService.exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var serviceHelper = new ServiceConfigService(null, null);
|
||||
if (serviceHelper.ServiceExists())
|
||||
{
|
||||
serviceHelper.Start();
|
||||
}
|
||||
} else
|
||||
{
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
Arguments = options.Args,
|
||||
FileName = Path.Combine(options.Path, "JackettConsole.exe"),
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
if (!isWindows)
|
||||
{
|
||||
startInfo.Arguments = startInfo.FileName + " " + startInfo.Arguments;
|
||||
startInfo.FileName = "mono";
|
||||
}
|
||||
|
||||
Engine.Logger.Info("Starting Jackett: " + startInfo.FileName + " " + startInfo.Arguments);
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetUpdateLocation()
|
||||
{
|
||||
var location = new Uri(Assembly.GetEntryAssembly().GetName().CodeBase);
|
||||
return new FileInfo(location.AbsolutePath).DirectoryName;
|
||||
}
|
||||
}
|
||||
}
|
||||
using CommandLine;
|
||||
using Jackett.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett.Updater
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new Program().Run(args);
|
||||
}
|
||||
|
||||
private void Run(string[] args)
|
||||
{
|
||||
Engine.SetupLogging(null, "updater.txt");
|
||||
Engine.Logger.Info("Jackett Updater v" + GetCurrentVersion());
|
||||
Engine.Logger.Info("Options " + string.Join(" ", args));
|
||||
try {
|
||||
var options = new UpdaterConsoleOptions();
|
||||
if (Parser.Default.ParseArguments(args, options))
|
||||
{
|
||||
ProcessUpdate(options);
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Logger.Error("Failed to process update arguments!: " + string.Join(" ", args));
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e, "Exception applying update!");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCurrentVersion()
|
||||
{
|
||||
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
|
||||
return fvi.FileVersion;
|
||||
}
|
||||
|
||||
private void ProcessUpdate(UpdaterConsoleOptions options)
|
||||
{
|
||||
var updateLocation = GetUpdateLocation();
|
||||
if(!(updateLocation.EndsWith("\\") || updateLocation.EndsWith("/")))
|
||||
{
|
||||
updateLocation += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
var isWindows = System.Environment.OSVersion.Platform != PlatformID.Unix;
|
||||
var trayRunning = false;
|
||||
var trayProcesses = Process.GetProcessesByName("JackettTray");
|
||||
if (isWindows)
|
||||
{
|
||||
if (trayProcesses.Count() > 0)
|
||||
{
|
||||
foreach (var proc in trayProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
Engine.Logger.Info("Killing tray process " + proc.Id);
|
||||
proc.Kill();
|
||||
trayRunning = true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Engine.Logger.Info("Waiting for Jackett to close..");
|
||||
Thread.Sleep(2000);
|
||||
Engine.Logger.Info("Finding files in: " + updateLocation);
|
||||
var files = Directory.GetFiles(updateLocation, "*.*", SearchOption.AllDirectories);
|
||||
foreach(var file in files)
|
||||
{
|
||||
var fileName = Path.GetFileName(file).ToLowerInvariant();
|
||||
|
||||
if (fileName.EndsWith(".zip") ||
|
||||
fileName.EndsWith(".tar") ||
|
||||
fileName.EndsWith(".gz"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Engine.Logger.Info("Copying " + fileName);
|
||||
var dest = Path.Combine(options.Path, file.Substring(updateLocation.Length));
|
||||
File.Copy(file, dest, true);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Engine.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (trayRunning)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
Arguments = options.Args,
|
||||
FileName = Path.Combine(options.Path, "JackettTray.exe"),
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
if(string.Equals(options.Type, "JackettService.exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var serviceHelper = new ServiceConfigService(null, null);
|
||||
if (serviceHelper.ServiceExists())
|
||||
{
|
||||
serviceHelper.Start();
|
||||
}
|
||||
} else
|
||||
{
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
Arguments = options.Args,
|
||||
FileName = Path.Combine(options.Path, "JackettConsole.exe"),
|
||||
UseShellExecute = true
|
||||
};
|
||||
|
||||
if (!isWindows)
|
||||
{
|
||||
startInfo.Arguments = startInfo.FileName + " " + startInfo.Arguments;
|
||||
startInfo.FileName = "mono";
|
||||
}
|
||||
|
||||
Engine.Logger.Info("Starting Jackett: " + startInfo.FileName + " " + startInfo.Arguments);
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetUpdateLocation()
|
||||
{
|
||||
var location = new Uri(Assembly.GetEntryAssembly().GetName().CodeBase);
|
||||
return new FileInfo(location.AbsolutePath).DirectoryName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Autofac" version="3.5.2" targetFramework="net45" />
|
||||
<package id="CommandLineParser" version="1.9.71" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
|
||||
<package id="NLog" version="4.2.3" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.7" targetFramework="net45" />
|
||||
</packages>
|
@@ -9,7 +9,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
|
@@ -20,101 +20,37 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 6px;
|
||||
box-shadow: 1px 1px 5px 2px #cdcdcd;
|
||||
width: 270px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 5px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#indexers {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
|
||||
#unconfigured-indexers{
|
||||
text-align: center;
|
||||
.indexer-table {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#unconfigured-indexers .card {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.unconfigured-indexer {
|
||||
height: 120px;
|
||||
.test-success {
|
||||
color: #449d44;
|
||||
}
|
||||
|
||||
.indexer {
|
||||
height: 252px;
|
||||
.test-error {
|
||||
color: #c9302c;
|
||||
}
|
||||
|
||||
.add-indexer {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.indexer-logo {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.indexer-logo > .hidden-name {
|
||||
position: absolute;
|
||||
color: rgba(255, 255, 255, 0);
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.indexer-logo > img {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #FFF;
|
||||
}
|
||||
|
||||
.indexer-name > h3 {
|
||||
margin-top: 13px;
|
||||
text-align: center;
|
||||
.test-inprogress {
|
||||
color: #286090;
|
||||
}
|
||||
|
||||
.indexer-buttons {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.indexer-buttons > .btn {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.indexer-buttons > .btn {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.indexer-button-test {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.indexer-add-content {
|
||||
color: gray;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.indexer-add-content > .glyphicon {
|
||||
font-size: 50px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.indexer-add-content > .light-text {
|
||||
margin-top: 11px;
|
||||
font-size: 18px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.indexer-host {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.indexer-host > input {
|
||||
font-size: 12px;
|
||||
padding: 2px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.setup-item-inputstring {
|
||||
@@ -287,3 +223,12 @@ pre {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.setup-item-displayinfo:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table td.fit{
|
||||
white-space: nowrap;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,17 @@
|
||||
$(document).ready(function () {
|
||||
var basePath = '';
|
||||
|
||||
$(document).ready(function () {
|
||||
$.ajaxSetup({ cache: false });
|
||||
window.jackettIsLocal = window.location.hostname === 'localhost' ||
|
||||
window.location.hostname === '127.0.0.1';
|
||||
|
||||
bindUIButtons();
|
||||
reloadIndexers();
|
||||
loadJackettSettings();
|
||||
|
||||
});
|
||||
|
||||
function getJackettConfig(callback) {
|
||||
var jqxhr = $.get("/admin/get_jackett_config", function (data) {
|
||||
var jqxhr = $.get("get_jackett_config", function (data) {
|
||||
|
||||
callback(data);
|
||||
}).fail(function () {
|
||||
@@ -22,6 +24,12 @@ function loadJackettSettings() {
|
||||
$("#api-key-input").val(data.config.api_key);
|
||||
$("#app-version").html(data.app_version);
|
||||
$("#jackett-port").val(data.config.port);
|
||||
$("#jackett-basepathoverride").val(data.config.basepathoverride);
|
||||
basePath = data.config.basepathoverride;
|
||||
if (basePath === null || basePath === undefined) {
|
||||
basePath = '';
|
||||
}
|
||||
|
||||
$("#jackett-savedir").val(data.config.blackholedir);
|
||||
$("#jackett-allowext").attr('checked', data.config.external);
|
||||
$("#jackett-allowupdate").attr('checked', data.config.updatedisabled);
|
||||
@@ -32,58 +40,157 @@ function loadJackettSettings() {
|
||||
if (password != null && password != '') {
|
||||
$("#logoutBtn").show();
|
||||
}
|
||||
|
||||
reloadIndexers();
|
||||
});
|
||||
}
|
||||
|
||||
function reloadIndexers() {
|
||||
$('#indexers').hide();
|
||||
$('#indexers > .indexer').remove();
|
||||
$('#unconfigured-indexers').empty();
|
||||
var jqxhr = $.get("/admin/get_indexers", function (data) {
|
||||
displayIndexers(data.items);
|
||||
var jqxhr = $.get("get_indexers", function (data) {
|
||||
var configuredIndexers = [];
|
||||
var unconfiguredIndexers = [];
|
||||
for (var i = 0; i < data.items.length; i++) {
|
||||
var item = data.items[i];
|
||||
item.torznab_host = resolveUrl(basePath + "/torznab/" + item.id);
|
||||
item.potato_host = resolveUrl(basePath + "/potato/" + item.id);
|
||||
|
||||
if (item.last_error)
|
||||
item.state = "error";
|
||||
else
|
||||
item.state = "success";
|
||||
|
||||
item.main_cats_list = [];
|
||||
for (var catID in item.caps) {
|
||||
var isMainCat = (catID % 1000) == 0;
|
||||
if (isMainCat)
|
||||
item.main_cats_list.push(item.caps[catID]);
|
||||
}
|
||||
item.mains_cats = item.main_cats_list.join(", ");
|
||||
|
||||
if (item.configured)
|
||||
configuredIndexers.push(item);
|
||||
else
|
||||
unconfiguredIndexers.push(item);
|
||||
}
|
||||
displayConfiguredIndexersList(configuredIndexers);
|
||||
displayUnconfiguredIndexersList(unconfiguredIndexers);
|
||||
}).fail(function () {
|
||||
doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
}
|
||||
|
||||
function displayIndexers(items) {
|
||||
var indexerTemplate = Handlebars.compile($("#configured-indexer").html());
|
||||
var unconfiguredIndexerTemplate = Handlebars.compile($("#unconfigured-indexer").html());
|
||||
function displayConfiguredIndexersList(indexers) {
|
||||
var indexersTemplate = Handlebars.compile($("#configured-indexer-table").html());
|
||||
var indexersTable = $(indexersTemplate({ indexers: indexers }));
|
||||
indexersTable.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 100,
|
||||
"lengthMenu": [[10, 20, 50, 100, 200, -1], [10, 20, 50, 100, 200, "All"]],
|
||||
"order": [[0, "desc"]],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": 0,
|
||||
"visible": true,
|
||||
"searchable": true
|
||||
},
|
||||
{
|
||||
"targets": 1,
|
||||
"visible": true,
|
||||
"searchable": false
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
$('#indexers').empty();
|
||||
$('#indexers').append(indexersTable);
|
||||
prepareTestButtons();
|
||||
$('#indexers').fadeIn();
|
||||
prepareSearchButtons();
|
||||
prepareSetupButtons();
|
||||
prepareDeleteButtons();
|
||||
prepareCopyButtons();
|
||||
}
|
||||
|
||||
function displayUnconfiguredIndexersList(indexers) {
|
||||
var indexersTemplate = Handlebars.compile($("#unconfigured-indexer-table").html());
|
||||
var indexersTable = $(indexersTemplate({ indexers: indexers }));
|
||||
indexersTable.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 100,
|
||||
"lengthMenu": [[10, 20, 50, 100, 200, -1], [10, 20, 50, 100, 200, "All"]],
|
||||
"order": [[0, "desc"]],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": 0,
|
||||
"visible": true,
|
||||
"searchable": true
|
||||
},
|
||||
{
|
||||
"targets": 1,
|
||||
"visible": true,
|
||||
"searchable": true
|
||||
},
|
||||
{
|
||||
"targets": 2,
|
||||
"visible": true,
|
||||
"searchable": true
|
||||
},
|
||||
{
|
||||
"targets": 3,
|
||||
"visible": true,
|
||||
"searchable": false
|
||||
}
|
||||
]
|
||||
});
|
||||
$('#unconfigured-indexers-template').empty();
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
item.torznab_host = resolveUrl("/torznab/" + item.id);
|
||||
item.potato_host = resolveUrl("/potato/" + item.id);
|
||||
if (item.configured)
|
||||
$('#indexers').append(indexerTemplate(item));
|
||||
else
|
||||
$('#unconfigured-indexers-template').append($(unconfiguredIndexerTemplate(item)));
|
||||
$('#unconfigured-indexers-template').append(indexersTable);
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
// create hidden text element, if it doesn't already exist
|
||||
var targetId = "_hiddenCopyText_";
|
||||
// must use a temporary form element for the selection and copy
|
||||
target = document.getElementById(targetId);
|
||||
if (!target) {
|
||||
var target = document.createElement("textarea");
|
||||
target.style.position = "absolute";
|
||||
target.style.left = "-9999px";
|
||||
target.style.top = "0";
|
||||
target.id = targetId;
|
||||
document.body.appendChild(target);
|
||||
}
|
||||
target.textContent = text;
|
||||
// select the content
|
||||
var currentFocus = document.activeElement;
|
||||
target.focus();
|
||||
target.setSelectionRange(0, target.value.length);
|
||||
|
||||
// copy the selection
|
||||
var succeed;
|
||||
try {
|
||||
succeed = document.execCommand("copy");
|
||||
} catch (e) {
|
||||
succeed = false;
|
||||
}
|
||||
// restore original focus
|
||||
if (currentFocus && typeof currentFocus.focus === "function") {
|
||||
currentFocus.focus();
|
||||
}
|
||||
|
||||
var addIndexerButton = $($('#add-indexer').html());
|
||||
addIndexerButton.appendTo($('#indexers'));
|
||||
target.textContent = "";
|
||||
|
||||
addIndexerButton.click(function () {
|
||||
$("#modals").empty();
|
||||
var dialog = $($("#select-indexer").html());
|
||||
dialog.find('#unconfigured-indexers').html($('#unconfigured-indexers-template').html());
|
||||
$("#modals").append(dialog);
|
||||
dialog.modal("show");
|
||||
$('.indexer-setup').each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
$btn.click(function () {
|
||||
$('#select-indexer-modal').modal('hide').on('hidden.bs.modal', function (e) {
|
||||
displayIndexerSetup(id);
|
||||
});
|
||||
});
|
||||
return succeed;
|
||||
}
|
||||
|
||||
function prepareCopyButtons() {
|
||||
$(".indexer-button-copy").each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var title = $btn[0].title;
|
||||
$btn.click(function () {
|
||||
copyToClipboard(title);
|
||||
});
|
||||
});
|
||||
|
||||
$('#indexers').fadeIn();
|
||||
prepareSetupButtons();
|
||||
prepareTestButtons();
|
||||
prepareDeleteButtons();
|
||||
}
|
||||
|
||||
function prepareDeleteButtons() {
|
||||
@@ -91,7 +198,7 @@ function prepareDeleteButtons() {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
$btn.click(function () {
|
||||
var jqxhr = $.post("/admin/delete_indexer", JSON.stringify({ indexer: id }), function (data) {
|
||||
var jqxhr = $.post("delete_indexer", JSON.stringify({ indexer: id }), function (data) {
|
||||
if (data.result == "error") {
|
||||
doNotify("Delete error for " + id + "\n" + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
}
|
||||
@@ -107,45 +214,89 @@ function prepareDeleteButtons() {
|
||||
});
|
||||
}
|
||||
|
||||
function prepareSearchButtons() {
|
||||
$('.indexer-button-search').each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
$btn.click(function() {
|
||||
showSearch(id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function prepareSetupButtons() {
|
||||
$('.indexer-setup').each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
var link = $btn.data("link");
|
||||
$btn.click(function () {
|
||||
displayIndexerSetup(id);
|
||||
displayIndexerSetup(id, link);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateTestState(id, state, message)
|
||||
{
|
||||
var btn = $(".indexer-button-test[data-id=" + id + "]");
|
||||
if (message) {
|
||||
btn.tooltip("hide");
|
||||
btn.data('bs.tooltip', false).tooltip({ title: message });
|
||||
}
|
||||
var icon = btn.find("span");
|
||||
icon.removeClass("glyphicon-ok test-success glyphicon-alert test-error glyphicon-refresh spinner test-inprogres");
|
||||
|
||||
if (state == "success") {
|
||||
icon.addClass("glyphicon-ok test-success");
|
||||
} else if (state == "error") {
|
||||
icon.addClass("glyphicon-alert test-error");
|
||||
} else if (state == "inprogres") {
|
||||
icon.addClass("glyphicon-refresh test-inprogres spinner");
|
||||
}
|
||||
}
|
||||
|
||||
function testIndexer(id, notifyResult) {
|
||||
updateTestState(id, "inprogres", null);
|
||||
|
||||
if (notifyResult)
|
||||
doNotify("Test started for " + id, "info", "glyphicon glyphicon-transfer");
|
||||
var jqxhr = $.post("test_indexer", JSON.stringify({ indexer: id }), function (data) {
|
||||
if (data.result == "error") {
|
||||
updateTestState(id, "error", data.error);
|
||||
if (notifyResult)
|
||||
doNotify("Test failed for " + id + ": \n" + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
}
|
||||
else {
|
||||
updateTestState(id, "success", "Test successful");
|
||||
if (notifyResult)
|
||||
doNotify("Test successful for " + id, "success", "glyphicon glyphicon-ok");
|
||||
}
|
||||
}).fail(function () {
|
||||
doNotify("Error testing indexer, request to Jackett server error", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
}
|
||||
|
||||
function prepareTestButtons() {
|
||||
$(".indexer-button-test").each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
var state = $btn.data("state");
|
||||
$btn.tooltip();
|
||||
updateTestState(id, state, null);
|
||||
$btn.click(function () {
|
||||
doNotify("Test started for " + id, "info", "glyphicon glyphicon-transfer");
|
||||
var jqxhr = $.post("/admin/test_indexer", JSON.stringify({ indexer: id }), function (data) {
|
||||
if (data.result == "error") {
|
||||
doNotify("Test failed for " + id + ": \n" + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
}
|
||||
else {
|
||||
doNotify("Test successful for " + id, "success", "glyphicon glyphicon-ok");
|
||||
}
|
||||
}).fail(function () {
|
||||
doNotify("Error testing indexer, request to Jackett server error", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
testIndexer(id, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function displayIndexerSetup(id) {
|
||||
function displayIndexerSetup(id, link) {
|
||||
|
||||
var jqxhr = $.post("/admin/get_config_form", JSON.stringify({ indexer: id }), function (data) {
|
||||
var jqxhr = $.post("get_config_form", JSON.stringify({ indexer: id }), function (data) {
|
||||
if (data.result == "error") {
|
||||
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
return;
|
||||
}
|
||||
|
||||
populateSetupForm(id, data.name, data.config, data.caps);
|
||||
populateSetupForm(id, data.name, data.config, data.caps, link);
|
||||
|
||||
}).fail(function () {
|
||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
@@ -188,17 +339,43 @@ function populateConfigItems(configForm, config) {
|
||||
var template = setupItemTemplate(item);
|
||||
$formItemContainer.append(template);
|
||||
if (item.type === 'recaptcha') {
|
||||
grecaptcha.render($('.jackettrecaptcha')[0], {
|
||||
'sitekey': item.sitekey
|
||||
});
|
||||
var jackettrecaptcha = $('.jackettrecaptcha');
|
||||
jackettrecaptcha.data("version", item.version);
|
||||
switch (item.version) {
|
||||
case "1":
|
||||
// The v1 reCAPTCHA code uses document.write() calls to write the CAPTCHA to the location where the script was loaded.
|
||||
// As it's loaded async this doesn't work.
|
||||
// We use an iframe to work around this problem.
|
||||
var html = '<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k='+encodeURIComponent(item.sitekey)+'"></script>';
|
||||
var frame = document.createElement('iframe');
|
||||
frame.id = "jackettrecaptchaiframe";
|
||||
frame.style.height = "145px";
|
||||
frame.style.weight = "326px";
|
||||
frame.style.border = "none";
|
||||
frame.onload = function () {
|
||||
// auto resize iframe to content
|
||||
frame.style.height = frame.contentWindow.document.body.scrollHeight + 'px';
|
||||
frame.style.width = frame.contentWindow.document.body.scrollWidth + 'px';
|
||||
}
|
||||
jackettrecaptcha.append(frame);
|
||||
frame.contentDocument.open();
|
||||
frame.contentDocument.write(html);
|
||||
frame.contentDocument.close();
|
||||
break;
|
||||
case "2":
|
||||
grecaptcha.render(jackettrecaptcha[0], {
|
||||
'sitekey': item.sitekey
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newConfigModal(title, config, caps) {
|
||||
function newConfigModal(title, config, caps, link) {
|
||||
var configTemplate = Handlebars.compile($("#jackett-config-setup-modal").html());
|
||||
var configForm = $(configTemplate({ title: title, caps: caps }));
|
||||
var configForm = $(configTemplate({ title: title, caps: caps, link:link }));
|
||||
$("#modals").append(configForm);
|
||||
populateConfigItems(configForm, config);
|
||||
return configForm;
|
||||
@@ -223,7 +400,18 @@ function getConfigModalJson(configForm) {
|
||||
break;
|
||||
case "recaptcha":
|
||||
if (window.jackettIsLocal) {
|
||||
itemEntry.value = $('.g-recaptcha-response').val();
|
||||
var version = $el.find('.jackettrecaptcha').data("version");
|
||||
switch (version) {
|
||||
case "1":
|
||||
var frameDoc = $("#jackettrecaptchaiframe")[0].contentDocument;
|
||||
itemEntry.version = version;
|
||||
itemEntry.challenge = $("#recaptcha_challenge_field", frameDoc).val()
|
||||
itemEntry.value = $("#recaptcha_response_field", frameDoc).val()
|
||||
break;
|
||||
case "2":
|
||||
itemEntry.value = $('.g-recaptcha-response').val();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
itemEntry.cookie = $el.find(".setup-item-recaptcha input").val();
|
||||
}
|
||||
@@ -234,8 +422,8 @@ function getConfigModalJson(configForm) {
|
||||
return configJson;
|
||||
}
|
||||
|
||||
function populateSetupForm(indexerId, name, config, caps) {
|
||||
var configForm = newConfigModal(name, config, caps);
|
||||
function populateSetupForm(indexerId, name, config, caps, link) {
|
||||
var configForm = newConfigModal(name, config, caps, link);
|
||||
var $goButton = configForm.find(".setup-indexer-go");
|
||||
$goButton.click(function () {
|
||||
var data = { indexer: indexerId, name: name };
|
||||
@@ -245,7 +433,7 @@ function populateSetupForm(indexerId, name, config, caps) {
|
||||
$goButton.prop('disabled', true);
|
||||
$goButton.html($('#spinner').html());
|
||||
|
||||
var jqxhr = $.post("/admin/configure_indexer", JSON.stringify(data), function (data) {
|
||||
var jqxhr = $.post("configure_indexer", JSON.stringify(data), function (data) {
|
||||
if (data.result == "error") {
|
||||
if (data.config) {
|
||||
populateConfigItems(configForm, data.config);
|
||||
@@ -296,6 +484,182 @@ function clearNotifications() {
|
||||
$('[data-notify="container"]').remove();
|
||||
}
|
||||
|
||||
function updateReleasesRow(row)
|
||||
{
|
||||
var labels = $(row).find("span.release-labels");
|
||||
var DownloadVolumeFactor = parseFloat($(row).find("td.DownloadVolumeFactor").html());
|
||||
var UploadVolumeFactor = parseFloat($(row).find("td.UploadVolumeFactor").html());
|
||||
|
||||
labels.empty();
|
||||
|
||||
if (!isNaN(DownloadVolumeFactor)) {
|
||||
if (DownloadVolumeFactor == 0) {
|
||||
labels.append('\n<span class="label label-success">FREELEECH</span>');
|
||||
} else if (DownloadVolumeFactor < 1) {
|
||||
labels.append('\n<span class="label label-primary">' + DownloadVolumeFactor * 100 + '%DL</span>');
|
||||
} else if (DownloadVolumeFactor > 1) {
|
||||
labels.append('\n<span class="label label-danger">' + DownloadVolumeFactor * 100 + '%DL</span>');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(UploadVolumeFactor)) {
|
||||
if (UploadVolumeFactor == 0) {
|
||||
labels.append('\n<span class="label label-warning">NO UPLOAD</span>');
|
||||
} else if (UploadVolumeFactor != 1) {
|
||||
labels.append('\n<span class="label label-info">' + UploadVolumeFactor * 100 + '%UL</span>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showSearch(selectedIndexer) {
|
||||
$('#select-indexer-modal').remove();
|
||||
var jqxhr = $.get("get_indexers", function (data) {
|
||||
var scope = {
|
||||
items: data.items
|
||||
};
|
||||
|
||||
var indexers = [];
|
||||
indexers.push({
|
||||
id: '', name: '-- All --'
|
||||
});
|
||||
for (var i = 0; i < data.items.length; i++) {
|
||||
if (data.items[i].configured === true) {
|
||||
indexers.push(data.items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-search").html());
|
||||
var releaseDialog = $(releaseTemplate({
|
||||
indexers: indexers
|
||||
}));
|
||||
if (selectedIndexer)
|
||||
releaseDialog.find("#searchTracker").val(selectedIndexer);
|
||||
$("#modals").append(releaseDialog);
|
||||
releaseDialog.modal("show");
|
||||
|
||||
releaseDialog.on('shown.bs.modal', function () {
|
||||
releaseDialog.find('#searchquery').focus();
|
||||
});
|
||||
|
||||
var setCategories = function (tracker, items) {
|
||||
var cats = {};
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].configured === true && (items[i].id === tracker || tracker === '')) {
|
||||
indexers["'" + items[i].id + "'"] = items[i].name;
|
||||
for (var prop in items[i].caps) {
|
||||
cats[prop] = items[i].caps[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
var select = $('#searchCategory');
|
||||
select.html("<option value=''>-- All --</option>");
|
||||
$.each(cats, function (value, key) {
|
||||
select.append($("<option></option>")
|
||||
.attr("value", value).text(key + ' (' + value + ')'));
|
||||
});
|
||||
};
|
||||
|
||||
setCategories('', data.items);
|
||||
$('#searchTracker').change(jQuery.proxy(function () {
|
||||
var trackerId = $('#searchTracker').val();
|
||||
setCategories(trackerId, this.items);
|
||||
}, scope));
|
||||
|
||||
document.getElementById("searchquery")
|
||||
.addEventListener("keyup", function (event) {
|
||||
event.preventDefault();
|
||||
if (event.keyCode == 13) {
|
||||
document.getElementById("jackett-search-perform").click();
|
||||
}
|
||||
});
|
||||
|
||||
$('#jackett-search-perform').click(function () {
|
||||
if ($('#jackett-search-perform').text().trim() !== 'Search trackers') {
|
||||
// We are searchin already
|
||||
return;
|
||||
}
|
||||
var queryObj = {
|
||||
Query: releaseDialog.find('#searchquery').val(),
|
||||
Category: releaseDialog.find('#searchCategory').val(),
|
||||
Tracker: releaseDialog.find('#searchTracker').val().replace("'", "").replace("'", ""),
|
||||
};
|
||||
$('#searchResults').empty();
|
||||
|
||||
$('#jackett-search-perform').html($('#spinner').html());
|
||||
var jqxhr = $.post("search", queryObj, function (data) {
|
||||
$('#jackett-search-perform').html('Search trackers');
|
||||
var resultsTemplate = Handlebars.compile($("#jackett-search-results").html());
|
||||
var results = $('#searchResults');
|
||||
results.html($(resultsTemplate(data)));
|
||||
results.find('tr.jackett-search-results-row').each(function () { updateReleasesRow(this); });
|
||||
|
||||
results.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 20,
|
||||
"lengthMenu": [[10, 20, 50, -1], [10, 20, 50, "All"]],
|
||||
"order": [[0, "desc"]],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": 0,
|
||||
"visible": false,
|
||||
"searchable": false,
|
||||
"type": 'date'
|
||||
},
|
||||
{
|
||||
"targets": 1,
|
||||
"visible": true,
|
||||
"searchable": false,
|
||||
"iDataSort": 0
|
||||
},
|
||||
{
|
||||
"targets": 4,
|
||||
"visible": false,
|
||||
"searchable": false,
|
||||
"type": 'num'
|
||||
},
|
||||
{
|
||||
"targets": 5,
|
||||
"visible": true,
|
||||
"searchable": false,
|
||||
"iDataSort": 4
|
||||
}
|
||||
],
|
||||
initComplete: function () {
|
||||
var count = 0;
|
||||
this.api().columns().every(function () {
|
||||
count++;
|
||||
if (count === 3 || count === 8) {
|
||||
var column = this;
|
||||
var select = $('<select><option value=""></option></select>')
|
||||
.appendTo($(column.footer()).empty())
|
||||
.on('change', function () {
|
||||
var val = $.fn.dataTable.util.escapeRegex(
|
||||
$(this).val()
|
||||
);
|
||||
|
||||
column
|
||||
.search(val ? '^' + val + '$' : '', true, false)
|
||||
.draw();
|
||||
});
|
||||
|
||||
column.data().unique().sort().each(function (d, j) {
|
||||
select.append('<option value="' + d + '">' + d + '</option>')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}).fail(function () {
|
||||
$('#jackett-search-perform').html('Search trackers');
|
||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
});
|
||||
|
||||
}).fail(function () {
|
||||
doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
}
|
||||
|
||||
function bindUIButtons() {
|
||||
$('body').on('click', '.downloadlink', function (e, b) {
|
||||
@@ -318,11 +682,38 @@ function bindUIButtons() {
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#jackett-add-indexer').click(function () {
|
||||
$("#modals").empty();
|
||||
var dialog = $($("#select-indexer").html());
|
||||
dialog.find('#unconfigured-indexers').html($('#unconfigured-indexers-template').html());
|
||||
$("#modals").append(dialog);
|
||||
dialog.modal("show");
|
||||
$('.indexer-setup').each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
var link = $btn.data("link");
|
||||
$btn.click(function () {
|
||||
$('#select-indexer-modal').modal('hide').on('hidden.bs.modal', function (e) {
|
||||
displayIndexerSetup(id, link);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#jackett-test-all").click(function () {
|
||||
$(".indexer-button-test").each(function (i, btn) {
|
||||
var $btn = $(btn);
|
||||
var id = $btn.data("id");
|
||||
testIndexer(id, false);
|
||||
});
|
||||
});
|
||||
|
||||
$("#jackett-show-releases").click(function () {
|
||||
var jqxhr = $.get("/admin/GetCache", function (data) {
|
||||
var jqxhr = $.get("GetCache", function (data) {
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-releases").html());
|
||||
var item = { releases: data, Title: 'Releases' };
|
||||
var releaseDialog = $(releaseTemplate(item));
|
||||
releaseDialog.find('tr.jackett-releases-row').each(function () { updateReleasesRow(this); });
|
||||
releaseDialog.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 20,
|
||||
@@ -370,7 +761,7 @@ function bindUIButtons() {
|
||||
var count = 0;
|
||||
this.api().columns().every(function () {
|
||||
count++;
|
||||
if (count === 5 || count === 9) {
|
||||
if (count === 5 || count === 10) {
|
||||
var column = this;
|
||||
var select = $('<select><option value=""></option></select>')
|
||||
.appendTo($(column.footer()).empty())
|
||||
@@ -400,139 +791,11 @@ function bindUIButtons() {
|
||||
});
|
||||
|
||||
$("#jackett-show-search").click(function () {
|
||||
$('#select-indexer-modal').remove();
|
||||
var jqxhr = $.get("/admin/get_indexers", function (data) {
|
||||
var scope = {
|
||||
items: data.items
|
||||
};
|
||||
|
||||
var indexers = [];
|
||||
indexers.push({ id: '', name: '-- All --' });
|
||||
for (var i = 0; i < data.items.length; i++) {
|
||||
if (data.items[i].configured === true) {
|
||||
indexers.push(data.items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-search").html());
|
||||
var releaseDialog = $(releaseTemplate({ indexers: indexers }));
|
||||
$("#modals").append(releaseDialog);
|
||||
releaseDialog.modal("show");
|
||||
|
||||
var setCategories = function (tracker, items) {
|
||||
var cats = {};
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].configured === true && (items[i].id === tracker || tracker === '')) {
|
||||
indexers["'" + items[i].id + "'"] = items[i].name;
|
||||
for (var prop in items[i].caps) {
|
||||
cats[prop] = items[i].caps[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
var select = $('#searchCategory');
|
||||
select.html("<option value=''>-- All --</option>");
|
||||
$.each(cats, function (value, key) {
|
||||
select.append($("<option></option>")
|
||||
.attr("value", value).text(key + ' (' + value + ')'));
|
||||
});
|
||||
};
|
||||
|
||||
setCategories('', data.items);
|
||||
$('#searchTracker').change(jQuery.proxy(function () {
|
||||
var trackerId = $('#searchTracker').val();
|
||||
setCategories(trackerId, this.items);
|
||||
}, scope));
|
||||
|
||||
|
||||
$('#jackett-search-perform').click(function () {
|
||||
if ($('#jackett-search-perform').text().trim() !== 'Search trackers') {
|
||||
// We are searchin already
|
||||
return;
|
||||
}
|
||||
var queryObj = {
|
||||
Query: releaseDialog.find('#searchquery').val(),
|
||||
Category: releaseDialog.find('#searchCategory').val(),
|
||||
Tracker: releaseDialog.find('#searchTracker').val().replace("'", "").replace("'", ""),
|
||||
};
|
||||
$('#searchResults').empty();
|
||||
|
||||
$('#jackett-search-perform').html($('#spinner').html());
|
||||
var jqxhr = $.post("/admin/search", queryObj, function (data) {
|
||||
$('#jackett-search-perform').html('Search trackers');
|
||||
var resultsTemplate = Handlebars.compile($("#jackett-search-results").html());
|
||||
var results = $('#searchResults');
|
||||
results.html($(resultsTemplate(data)));
|
||||
|
||||
results.find('table').DataTable(
|
||||
{
|
||||
"pageLength": 20,
|
||||
"lengthMenu": [[10, 20, 50, -1], [10, 20, 50, "All"]],
|
||||
"order": [[0, "desc"]],
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": 0,
|
||||
"visible": false,
|
||||
"searchable": false,
|
||||
"type": 'date'
|
||||
},
|
||||
{
|
||||
"targets": 1,
|
||||
"visible": true,
|
||||
"searchable": false,
|
||||
"iDataSort": 0
|
||||
},
|
||||
{
|
||||
"targets": 4,
|
||||
"visible": false,
|
||||
"searchable": false,
|
||||
"type": 'num'
|
||||
},
|
||||
{
|
||||
"targets": 5,
|
||||
"visible": true,
|
||||
"searchable": false,
|
||||
"iDataSort": 4
|
||||
}
|
||||
],
|
||||
initComplete: function () {
|
||||
var count = 0;
|
||||
this.api().columns().every(function () {
|
||||
count++;
|
||||
if (count === 3 || count === 7) {
|
||||
var column = this;
|
||||
var select = $('<select><option value=""></option></select>')
|
||||
.appendTo($(column.footer()).empty())
|
||||
.on('change', function () {
|
||||
var val = $.fn.dataTable.util.escapeRegex(
|
||||
$(this).val()
|
||||
);
|
||||
|
||||
column
|
||||
.search(val ? '^' + val + '$' : '', true, false)
|
||||
.draw();
|
||||
});
|
||||
|
||||
column.data().unique().sort().each(function (d, j) {
|
||||
select.append('<option value="' + d + '">' + d + '</option>')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}).fail(function () {
|
||||
$('#jackett-search-perform').html('Search trackers');
|
||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
});
|
||||
|
||||
}).fail(function () {
|
||||
doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
showSearch(null);
|
||||
});
|
||||
|
||||
$("#view-jackett-logs").click(function () {
|
||||
var jqxhr = $.get("/admin/GetLogs", function (data) {
|
||||
var jqxhr = $.get("GetLogs", function (data) {
|
||||
var releaseTemplate = Handlebars.compile($("#jackett-logs").html());
|
||||
var item = { logs: data };
|
||||
var releaseDialog = $(releaseTemplate(item));
|
||||
@@ -546,6 +809,7 @@ function bindUIButtons() {
|
||||
|
||||
$("#change-jackett-port").click(function () {
|
||||
var jackett_port = $("#jackett-port").val();
|
||||
var jackett_basepathoverride = $("#jackett-basepathoverride").val();
|
||||
var jackett_external = $("#jackett-allowext").is(':checked');
|
||||
var jackett_update = $("#jackett-allowupdate").is(':checked');
|
||||
var jackett_prerelease = $("#jackett-prerelease").is(':checked');
|
||||
@@ -556,21 +820,17 @@ function bindUIButtons() {
|
||||
updatedisabled: jackett_update,
|
||||
prerelease: jackett_prerelease,
|
||||
blackholedir: $("#jackett-savedir").val(),
|
||||
logging: jackett_logging
|
||||
logging: jackett_logging,
|
||||
basepathoverride: jackett_basepathoverride
|
||||
};
|
||||
var jqxhr = $.post("/admin/set_config", JSON.stringify(jsonObject), function (data) {
|
||||
var jqxhr = $.post("set_config", JSON.stringify(jsonObject), function (data) {
|
||||
if (data.result == "error") {
|
||||
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
return;
|
||||
} else {
|
||||
doNotify("Redirecting you to complete configuration update..", "success", "glyphicon glyphicon-ok");
|
||||
window.setTimeout(function () {
|
||||
url = window.location.href;
|
||||
if (data.external) {
|
||||
window.location.href = url.substr(0, url.lastIndexOf(":") + 1) + data.port;
|
||||
} else {
|
||||
window.location.href = 'http://127.0.0.1:' + data.port;
|
||||
}
|
||||
window.location.reload(true);
|
||||
}, 3000);
|
||||
|
||||
}
|
||||
@@ -580,7 +840,7 @@ function bindUIButtons() {
|
||||
});
|
||||
|
||||
$("#trigger-updater").click(function () {
|
||||
var jqxhr = $.get("/admin/trigger_update", function (data) {
|
||||
var jqxhr = $.get("trigger_update", function (data) {
|
||||
if (data.result == "error") {
|
||||
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
return;
|
||||
@@ -596,7 +856,7 @@ function bindUIButtons() {
|
||||
var password = $("#jackett-adminpwd").val();
|
||||
var jsonObject = { password: password };
|
||||
|
||||
var jqxhr = $.post("/admin/set_admin_password", JSON.stringify(jsonObject), function (data) {
|
||||
var jqxhr = $.post("set_admin_password", JSON.stringify(jsonObject), function (data) {
|
||||
|
||||
if (data.result == "error") {
|
||||
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||
@@ -613,4 +873,4 @@ function bindUIButtons() {
|
||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -3,28 +3,28 @@
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
||||
<script src="/libs/filesize.min.js"></script>
|
||||
<script src="/libs/jquery.min.js"></script>
|
||||
<script src="/libs/jquery.dataTables.min.js"></script>
|
||||
<script src="/libs/handlebars.min.js"></script>
|
||||
<script src="/libs/moment.min.js"></script>
|
||||
<script src="/libs/handlebarsmoment.js"></script>
|
||||
<script src="/bootstrap/bootstrap.min.js"></script>
|
||||
<script src="/libs/bootstrap-notify.js"></script>
|
||||
<link rel='shortcut icon' type='image/x-icon' href='../favicon.ico' />
|
||||
<script src="../libs/filesize.min.js"></script>
|
||||
<script src="../libs/jquery.min.js"></script>
|
||||
<script src="../libs/jquery.dataTables.min.js"></script>
|
||||
<script src="../libs/handlebars.min.js"></script>
|
||||
<script src="../libs/moment.min.js"></script>
|
||||
<script src="../libs/handlebarsmoment.js"></script>
|
||||
<script src="../bootstrap/bootstrap.min.js"></script>
|
||||
<script src="../libs/bootstrap-notify.js"></script>
|
||||
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
|
||||
|
||||
<link href="/bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/animate.css" rel="stylesheet">
|
||||
<link href="/custom.css" rel="stylesheet">
|
||||
<link href="/css/jquery.dataTables.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/css/font-awesome.min.css">
|
||||
<link href="../bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../animate.css" rel="stylesheet">
|
||||
<link href="../custom.css" rel="stylesheet">
|
||||
<link href="../css/jquery.dataTables.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../css/font-awesome.min.css">
|
||||
<title>Jackett</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<img id="logo" src="/jacket_medium.png" alt="Logo" /><span id="header-title">Jackett</span>
|
||||
<img id="logo" src="../jacket_medium.png" alt="Logo" /><span id="header-title">Jackett</span>
|
||||
|
||||
<div class="pull-right jackett-apikey">
|
||||
<span class="input-header">API Key: </span>
|
||||
@@ -32,11 +32,17 @@
|
||||
</div>
|
||||
<hr />
|
||||
<div class="pull-right">
|
||||
<button id="jackett-add-indexer" class="btn btn-success btn-sm">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add indexer
|
||||
</button>
|
||||
<button id="jackett-show-search" class="btn btn-success btn-sm">
|
||||
<i class="fa fa-search"></i> Manual Search <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
<span class="glyphicon glyphicon-search" aria-hidden="true"></span> Manual Search
|
||||
</button>
|
||||
<button id="jackett-show-releases" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-database"></i> View cached releases <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
<i class="fa fa-database"></i> View cached releases
|
||||
</button>
|
||||
<button id="jackett-test-all" class="btn btn-warning btn-sm">
|
||||
<span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span> Test All
|
||||
</button>
|
||||
</div>
|
||||
<h3>Configured Indexers</h3>
|
||||
@@ -48,7 +54,7 @@
|
||||
<ol>
|
||||
<li>Go to <b>Settings > Indexers > Add > Torznab > Custom</b>.</li>
|
||||
<li>For <b>URL</b> enter the <b>Torznab Host</b> of one of the indexers.</li>
|
||||
<li>For the <b>API key</b> using the key below.</li>
|
||||
<li>For the <b>API key</b> using the key above.</li>
|
||||
</ol>
|
||||
<h4>Adding a Jackett indexer in CouchPotato</h4>
|
||||
<ol>
|
||||
@@ -84,10 +90,13 @@
|
||||
Logout
|
||||
</a>
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<span class="input-header">Base Path Override: </span>
|
||||
<input id="jackett-basepathoverride" class="form-control input-right" type="text" value="" placeholder="/jackett/">
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<span class="input-header">Server port: </span>
|
||||
<input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117">
|
||||
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<span class="input-header">Manual download blackhole directory: </span>
|
||||
@@ -167,52 +176,86 @@
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="configured-indexer" type="text/x-handlebars-template">
|
||||
<div class="configured-indexer indexer card">
|
||||
<div class="indexer-logo">
|
||||
<!-- Make section browser searchable -->
|
||||
<span class="hidden-name">{{name}}</span>
|
||||
<img alt="{{name}}" title="{{name}}" src="/logos/{{id}}.png" />
|
||||
</div>
|
||||
<div class="indexer-buttons">
|
||||
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||
</button>
|
||||
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
||||
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
||||
</a>
|
||||
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
|
||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="indexer-host">
|
||||
<b>Torznab Host:</b>
|
||||
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
|
||||
<b>CouchPotato Host:</b>
|
||||
{{#if potatoenabled}}
|
||||
|
||||
<input class="form-control" type="text" value="{{potato_host}}" placeholder="Torznab Host" readonly="">
|
||||
{{else}}
|
||||
<input class="form-control" type="text" value="Not available" placeholder="Torznab Host" readonly="">
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<script id="configured-indexer-table" type="text/x-handlebars-template">
|
||||
<table class="indexer-table configured-indexer-table dataTable compact cell-border hover stripe table table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Indexer</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each indexers}}
|
||||
<tr class="configured-indexer-row">
|
||||
<td><a target="_blank" href="{{site_link}}" title="{{description}}">{{name}}</a></td>
|
||||
<td class="fit">
|
||||
<div class="indexer-buttons">
|
||||
<button title="{{torznab_host}}" type="button" class="indexer-button-copy btn btn-xs btn-info">Copy Torznab Feed</button>
|
||||
<button title="{{potato_host}}" type="button" class="indexer-button-copy btn btn-xs btn-info{{#unless potatoenabled}} disabled{{/unless}}">Copy Potato Feed</button>
|
||||
|
||||
<button title="Search" class="btn btn-success btn-xs indexer-button-search" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button title="Configure" class="btn btn-primary btn-xs indexer-setup" data-id="{{id}}" data-link="{{site_link}}">
|
||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button title="Delete" class="btn btn-danger btn-xs indexer-button-delete" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button title="{{last_error}}" class="btn btn-warning btn-xs indexer-button-test" data-toggle="tooltip" data-id="{{id}}" data-state="{{state}}">
|
||||
Test
|
||||
<span class="glyphicon" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</script>
|
||||
<script id="unconfigured-indexer" type="text/x-handlebars-template">
|
||||
<div class="unconfigured-indexer card">
|
||||
<div class="indexer-logo">
|
||||
<!-- Make section browser searchable -->
|
||||
<span class="hidden-name">{{name}}</span>
|
||||
<img alt="{{name}}" title="{{name}}" src="/logos/{{id}}.png" />
|
||||
</div>
|
||||
<div class="indexer-buttons">
|
||||
<a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>
|
||||
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script id="unconfigured-indexer-table" type="text/x-handlebars-template">
|
||||
<table class="indexer-table configured-indexer-table dataTable compact cell-border hover stripe table table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Indexer</th>
|
||||
<th>Categories</th>
|
||||
<th>Language</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each indexers}}
|
||||
<tr class="unconfigured-indexer-row">
|
||||
<td><a target="_blank" href="{{site_link}}" title="{{description}}">{{name}}</a></td>
|
||||
<td>{{mains_cats}}</td>
|
||||
<td>{{language}}</td>
|
||||
<td class="fit">
|
||||
<div class="indexer-buttons">
|
||||
<button title="Configure" class="btn btn-success btn-xs indexer-setup" data-id="{{id}}" data-link="{{site_link}}">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script id="jackett-releases" type="text/x-handlebars-template">
|
||||
@@ -236,26 +279,34 @@
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Size</th>
|
||||
<th>Files</th>
|
||||
<th>Category</th>
|
||||
<th>Grabs</th>
|
||||
<th>Seeds</th>
|
||||
<th>Leechers</th>
|
||||
<th>DL Factor</th>
|
||||
<th>UL Factor</th>
|
||||
<th>Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each releases}}
|
||||
<tr>
|
||||
<tr class="jackett-releases-row">
|
||||
<td>{{PublishDate}}</td>
|
||||
<td>{{FirstSeen}}</td>
|
||||
<td>{{jacketTimespan PublishDate}}</td>
|
||||
<td>{{jacketTimespan FirstSeen}}</td>
|
||||
<td>{{Tracker}}</td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a></td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a> <span class="release-labels"></span></td>
|
||||
<td>{{Size}}</td>
|
||||
<td>{{jacketSize Size}}</td>
|
||||
<td>{{Files}}</td>
|
||||
<td>{{CategoryDesc}}</td>
|
||||
<td>{{Grabs}}</td>
|
||||
<td>{{Seeders}}</td>
|
||||
<td>{{Peers}}</td>
|
||||
<td class="DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
|
||||
<td class="UploadVolumeFactor">{{UploadVolumeFactor}}</td>
|
||||
<td class="downloadcolumn">
|
||||
<a class="downloadlink" title="Download locally" href="{{Link}}"><i class="fa fa-download"></i></a>
|
||||
{{#if BlackholeLink}}
|
||||
@@ -277,6 +328,12 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -334,24 +391,32 @@
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Size</th>
|
||||
<th>Files</th>
|
||||
<th>Category</th>
|
||||
<th>Grabs</th>
|
||||
<th>Seeds</th>
|
||||
<th>Leechers</th>
|
||||
<th>DL Factor</th>
|
||||
<th>UL Factor</th>
|
||||
<th>Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each Results}}
|
||||
<tr>
|
||||
<tr class="jackett-search-results-row">
|
||||
<td>{{PublishDate}}</td>
|
||||
<td>{{jacketTimespan PublishDate}}</td>
|
||||
<td>{{Tracker}}</td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a></td>
|
||||
<td><a href="{{Comments}}">{{Title}}</a> <span class="release-labels"></span></td>
|
||||
<td>{{Size}}</td>
|
||||
<td>{{jacketSize Size}}</td>
|
||||
<td>{{Files}}</td>
|
||||
<td>{{CategoryDesc}}</td>
|
||||
<td>{{Grabs}}</td>
|
||||
<td>{{Seeders}}</td>
|
||||
<td>{{Peers}}</td>
|
||||
<td class="DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
|
||||
<td class="UploadVolumeFactor">{{UploadVolumeFactor}}</td>
|
||||
<td class="downloadcolumn">
|
||||
<a class="downloadlink" title="Download locally" href="{{Link}}"><i class="fa fa-download"></i></a>
|
||||
{{#if BlackholeLink}}
|
||||
@@ -371,6 +436,12 @@
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -437,7 +508,7 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">{{title}}</h4>
|
||||
<h4 class="modal-title">{{title}} - <a target="_blank" href="{{link}}">{{link}}</a></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="config-setup-form"></form>
|
||||
@@ -469,18 +540,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script id="add-indexer" type="text/x-handlebars-template">
|
||||
<button class="indexer card add-indexer" data-toggle="modal">
|
||||
<div class="indexer-add-content">
|
||||
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
<div class="light-text">Add</div>
|
||||
</div>
|
||||
</button>
|
||||
</script>
|
||||
|
||||
<script id="spinner" type="text/x-handlebars-template">
|
||||
<span class="spinner glyphicon glyphicon-refresh"></span>
|
||||
</script>
|
||||
|
||||
<script src="/custom.js"></script>
|
||||
<script src="../custom.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -4,30 +4,30 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
||||
<link rel='shortcut icon' type='image/x-icon' href='../favicon.ico' />
|
||||
|
||||
<script src="/libs/jquery.min.js"></script>
|
||||
<script src="/libs/jquery.dataTables.min.js"></script>
|
||||
<script src="/libs/handlebars.min.js"></script>
|
||||
<script src="/libs/moment.min.js"></script>
|
||||
<script src="/libs/handlebarsmoment.js"></script>
|
||||
<script src="/bootstrap/bootstrap.min.js"></script>
|
||||
<script src="/libs/bootstrap-notify.js"></script>
|
||||
<script src="../libs/jquery.min.js"></script>
|
||||
<script src="../libs/jquery.dataTables.min.js"></script>
|
||||
<script src="../libs/handlebars.min.js"></script>
|
||||
<script src="../libs/moment.min.js"></script>
|
||||
<script src="../libs/handlebarsmoment.js"></script>
|
||||
<script src="../bootstrap/bootstrap.min.js"></script>
|
||||
<script src="../libs/bootstrap-notify.js"></script>
|
||||
|
||||
<link href="/bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/animate.css" rel="stylesheet">
|
||||
<link href="/custom.css" rel="stylesheet">
|
||||
<link href="../bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../animate.css" rel="stylesheet">
|
||||
<link href="../custom.css" rel="stylesheet">
|
||||
|
||||
<title>Jackett</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<img id="logo" src="/jacket_medium.png" /><span id="header-title">Jackett</span>
|
||||
<img id="logo" src="../jacket_medium.png" /><span id="header-title">Jackett</span>
|
||||
|
||||
<hr />
|
||||
<h1>Login</h1>
|
||||
<form action="/Admin/Dashboard" method="post">
|
||||
<form action="Dashboard" method="post">
|
||||
<div class="input-area">
|
||||
<span class="input-header">Admin password</span>
|
||||
<input id="password" name="password" class="form-control input-right" type="password">
|
||||
@@ -38,4 +38,4 @@
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 20 KiB |
@@ -80,7 +80,7 @@ namespace Jackett.Controllers
|
||||
var ctx = Request.GetOwinContext();
|
||||
var authManager = ctx.Authentication;
|
||||
authManager.SignOut("ApplicationCookie");
|
||||
return Redirect("/Admin/Dashboard");
|
||||
return Redirect("Admin/Dashboard");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@@ -232,6 +232,8 @@ namespace Jackett.Controllers
|
||||
item["description"] = indexer.DisplayDescription;
|
||||
item["configured"] = indexer.IsConfigured;
|
||||
item["site_link"] = indexer.SiteLink;
|
||||
item["language"] = indexer.Language;
|
||||
item["last_error"] = indexer.LastError;
|
||||
item["potatoenabled"] = indexer.TorznabCaps.Categories.Select(c => c.ID).Any(i => PotatoController.MOVIE_CATS.Contains(i));
|
||||
|
||||
var caps = new JObject();
|
||||
@@ -256,19 +258,24 @@ namespace Jackett.Controllers
|
||||
public async Task<IHttpActionResult> Test()
|
||||
{
|
||||
JToken jsonReply = new JObject();
|
||||
IIndexer indexer = null;
|
||||
try
|
||||
{
|
||||
var postData = await ReadPostDataJson();
|
||||
string indexerString = (string)postData["indexer"];
|
||||
indexer = indexerService.GetIndexer(indexerString);
|
||||
await indexerService.TestIndexer(indexerString);
|
||||
jsonReply["name"] = indexerService.GetIndexer(indexerString).DisplayName;
|
||||
jsonReply["name"] = indexer.DisplayName;
|
||||
jsonReply["result"] = "success";
|
||||
indexer.LastError = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "Exception in test_indexer");
|
||||
jsonReply["result"] = "error";
|
||||
jsonReply["error"] = ex.Message;
|
||||
if (indexer != null)
|
||||
indexer.LastError = ex.Message;
|
||||
}
|
||||
return Json(jsonReply);
|
||||
}
|
||||
@@ -318,6 +325,7 @@ namespace Jackett.Controllers
|
||||
cfg["prerelease"] = serverService.Config.UpdatePrerelease;
|
||||
cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword) ? string.Empty : serverService.Config.AdminPassword.Substring(0, 10);
|
||||
cfg["logging"] = Startup.TracingEnabled;
|
||||
cfg["basepathoverride"] = serverService.Config.BasePathOverride;
|
||||
|
||||
|
||||
jsonReply["config"] = cfg;
|
||||
@@ -349,9 +357,12 @@ namespace Jackett.Controllers
|
||||
bool updateDisabled = (bool)postData["updatedisabled"];
|
||||
bool preRelease = (bool)postData["prerelease"];
|
||||
bool logging = (bool)postData["logging"];
|
||||
string basePathOverride = (string)postData["basepathoverride"];
|
||||
|
||||
Engine.Server.Config.UpdateDisabled = updateDisabled;
|
||||
Engine.Server.Config.UpdatePrerelease = preRelease;
|
||||
Engine.Server.Config.BasePathOverride = basePathOverride;
|
||||
Startup.BasePath = Engine.Server.BasePath();
|
||||
Engine.Server.SaveConfig();
|
||||
|
||||
Engine.SetLogLevel(logging ? LogLevel.Debug : LogLevel.Info);
|
||||
@@ -446,7 +457,7 @@ namespace Jackett.Controllers
|
||||
|
||||
private void ConfigureCacheResults(List<TrackerCacheResult> results)
|
||||
{
|
||||
var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port);
|
||||
var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath());
|
||||
foreach (var result in results)
|
||||
{
|
||||
var link = result.Link;
|
||||
|
@@ -99,7 +99,9 @@ namespace Jackett.Controllers
|
||||
{
|
||||
ApiKey = request.passkey,
|
||||
Categories = MOVIE_CATS,
|
||||
SearchTerm = request.search
|
||||
SearchTerm = request.search,
|
||||
ImdbID = request.imdbid,
|
||||
QueryType = "TorrentPotato"
|
||||
};
|
||||
|
||||
IEnumerable<ReleaseInfo> releases = new List<ReleaseInfo>();
|
||||
@@ -117,7 +119,7 @@ namespace Jackett.Controllers
|
||||
}
|
||||
|
||||
releases = indexer.FilterResults(torznabQuery, releases);
|
||||
var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port);
|
||||
var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath());
|
||||
var potatoResponse = new TorrentPotatoResponse();
|
||||
|
||||
releases = TorznabUtil.FilterResultsToTitle(releases, torznabQuery.SanitizedSearchTerm, year);
|
||||
|
@@ -100,7 +100,7 @@ namespace Jackett.Controllers
|
||||
|
||||
logger.Info(logBuilder.ToString());
|
||||
|
||||
var serverUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port);
|
||||
var serverUrl = string.Format("{0}://{1}:{2}{3}", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port, serverService.BasePath());
|
||||
var resultPage = new ResultPage(new ChannelInfo
|
||||
{
|
||||
Title = indexer.DisplayName,
|
||||
|
@@ -85,6 +85,7 @@ namespace Jackett
|
||||
|
||||
using (var easy = new CurlEasy())
|
||||
{
|
||||
|
||||
easy.Url = curlRequest.Url;
|
||||
easy.BufferSize = 64 * 1024;
|
||||
easy.UserAgent = BrowserUtil.ChromeUserAgent;
|
||||
@@ -139,7 +140,12 @@ namespace Jackett
|
||||
{
|
||||
easy.SetOpt(CurlOption.SslVerifyhost, false);
|
||||
easy.SetOpt(CurlOption.SslVerifyPeer, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (Startup.ProxyConnection != null)
|
||||
{
|
||||
easy.SetOpt(CurlOption.Proxy, Startup.ProxyConnection);
|
||||
}
|
||||
|
||||
easy.Perform();
|
||||
|
||||
@@ -155,10 +161,19 @@ namespace Jackett
|
||||
|
||||
var headerBytes = Combine(headerBuffers.ToArray());
|
||||
var headerString = Encoding.UTF8.GetString(headerBytes);
|
||||
if (Startup.ProxyConnection != null)
|
||||
{
|
||||
var firstcrlf = headerString.IndexOf("\r\n\r\n");
|
||||
var secondcrlf = headerString.IndexOf("\r\n\r\n", firstcrlf + 1);
|
||||
if (secondcrlf > 0)
|
||||
{
|
||||
headerString = headerString.Substring(firstcrlf + 4, secondcrlf - (firstcrlf));
|
||||
}
|
||||
}
|
||||
var headerParts = headerString.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var headers = new List<string[]>();
|
||||
var headerCount = 0;
|
||||
HttpStatusCode status = HttpStatusCode.InternalServerError;
|
||||
HttpStatusCode status = HttpStatusCode.NotImplemented;
|
||||
var cookieBuilder = new StringBuilder();
|
||||
var cookies = new List<Tuple<string, string>>();
|
||||
foreach (var headerPart in headerParts)
|
||||
@@ -201,6 +216,31 @@ namespace Jackett
|
||||
cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2);
|
||||
}
|
||||
|
||||
// add some debug output to track down the problem causing people getting InternalServerError results
|
||||
if (status == HttpStatusCode.NotImplemented || status == HttpStatusCode.InternalServerError)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnErrorMessage("got NotImplemented/InternalServerError");
|
||||
OnErrorMessage("request.Method: " + curlRequest.Method);
|
||||
OnErrorMessage("request.Url: " + curlRequest.Url);
|
||||
OnErrorMessage("request.Cookies: " + curlRequest.Cookies);
|
||||
OnErrorMessage("request.Referer: " + curlRequest.Referer);
|
||||
OnErrorMessage("request.RawPOSTDdata: " + curlRequest.RawPOSTDdata);
|
||||
OnErrorMessage("cookies: "+ cookieBuilder.ToString().Trim());
|
||||
OnErrorMessage("headerString:\n" + headerString);
|
||||
|
||||
foreach (var headerPart in headerParts)
|
||||
{
|
||||
OnErrorMessage("headerParts: "+headerPart);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnErrorMessage(string.Format("CurlHelper: error while handling NotImplemented/InternalServerError:\n{0}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
var contentBytes = Combine(contentBuffers.ToArray());
|
||||
var curlResponse = new CurlResponse(headers, contentBytes, status, cookieBuilder.ToString().Trim());
|
||||
return curlResponse;
|
||||
|
82
src/Jackett/Definitions/alphareign.yml
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
site: alphareign
|
||||
name: AlphaReign
|
||||
description: "A DHT only tracker"
|
||||
language: en-us
|
||||
|
||||
links:
|
||||
- https://alphareign.se
|
||||
|
||||
caps:
|
||||
categories:
|
||||
Movies: Movies
|
||||
Movies/HD: Movies/HD
|
||||
Movies/SD: Movies/SD
|
||||
TV: TV
|
||||
TV/HD: TV/HD
|
||||
TV/SD: TV/SD
|
||||
Other: Other
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /login
|
||||
method: form
|
||||
form: form[action^="/login"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- path: /login
|
||||
selector: div.auth-form > div > div.alert-danger
|
||||
|
||||
test:
|
||||
path: settings
|
||||
selector: a[href="/logout"]
|
||||
|
||||
ratio:
|
||||
text: "∞"
|
||||
|
||||
search:
|
||||
path: /
|
||||
method: get
|
||||
inputs:
|
||||
query: "{{ .Keywords }}"
|
||||
rows:
|
||||
selector: .result
|
||||
fields:
|
||||
title:
|
||||
attribute: data-name
|
||||
details:
|
||||
selector: a[href^="/torrent"]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="magnet:"]
|
||||
attribute: href
|
||||
date:
|
||||
attribute: data-added
|
||||
size:
|
||||
attribute: data-size
|
||||
seeders:
|
||||
attribute: data-seeders
|
||||
leechers:
|
||||
attribute: data-leechers
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
category:
|
||||
case:
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"HD\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"720p\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"1080p\")": "Movies/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\") + span[class=\"label label-info\"]:contains(\"SD\")": "Movies/SD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"movie\")": "Movies"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\") + span[class=\"label label-info\"]:contains(\"HD\")": "TV/HD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\") + span[class=\"label label-info\"]:contains(\"SD\")": "TV/SD"
|
||||
"span[class=\"label label-primary\"]:contains(\"video\") + span[class=\"label label-success\"]:contains(\"show\")": "TV"
|
||||
"*": "Other"
|
92
src/Jackett/Definitions/apollo.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
site: apollo
|
||||
name: Apollo
|
||||
description: "A music tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://apollo.rip
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Audio # Music
|
||||
2: PC # Applications
|
||||
3: Books # E-Books
|
||||
4: Audio/Audiobook # Audiobooks
|
||||
5: Movies # E-Learning Videos
|
||||
6: TV # Comedy
|
||||
7: Books/Comics # Comics
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
keeplogged: 1
|
||||
login: "Log in"
|
||||
error:
|
||||
- selector: form#loginform > span.warning
|
||||
test:
|
||||
path: torrents.php
|
||||
|
||||
ratio:
|
||||
path: torrents.php
|
||||
selector: li#stats_ratio > span
|
||||
|
||||
search:
|
||||
path: torrents.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}filter_cat[{{.}}]=1&{{end}}"
|
||||
searchstr: "{{ .Query.Keywords }}"
|
||||
order_by: time
|
||||
order_way: desc
|
||||
action: basic
|
||||
searchsubmit: 1
|
||||
rows:
|
||||
selector: table#torrent_table > tbody > tr.torrent
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="torrents.php?action=download&id="]
|
||||
attribute: href
|
||||
description:
|
||||
selector: div.group_info
|
||||
remove: span
|
||||
title:
|
||||
selector: div.group_info
|
||||
remove: span, div.tags
|
||||
category:
|
||||
selector: td.cats_col
|
||||
case:
|
||||
div.cats_music: 1
|
||||
div.cats_applications: 2
|
||||
div.cats_ebooks: 3
|
||||
div.cats_audiobooks: 4
|
||||
div.cats_elearningvideos: 5
|
||||
div.cats_comedy: 6
|
||||
div.cats_comics: 7
|
||||
comments:
|
||||
selector: a[href^="torrents.php?id="]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(3)
|
||||
date:
|
||||
selector: td:nth-child(4)
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
":root div.alertbar:contains(\"freeleech\")": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
146
src/Jackett/Definitions/blubits.yml
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
site: blubits
|
||||
name: Blu-bits
|
||||
description: "A HD tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://blu-bits.com/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Movies
|
||||
14: Movies/BluRay # Full Blu-ray
|
||||
54: Movies/HD # HD-DVD
|
||||
16: Movies/HD # Remux
|
||||
55: Movies/HD # 2160p
|
||||
15: Movies/HD # 1080p
|
||||
19: Movies/HD # 1080i
|
||||
18: Movies/HD # 720p
|
||||
|
||||
# Documentaries
|
||||
21: Movies/BluRay # Full Blu-ray
|
||||
39: Movies/HD # Remux
|
||||
56: Movies/HD # 2160p
|
||||
23: Movies/HD # 1080p
|
||||
24: Movies/HD # 1080i
|
||||
25: Movies/HD # 720p
|
||||
|
||||
# TV Series
|
||||
27: TV/HD # Full Blu-ray
|
||||
40: TV/HD # Remux
|
||||
28: TV/HD # 1080p
|
||||
29: TV/HD # 1080i
|
||||
30: TV/HD # 720p
|
||||
|
||||
# HDTV
|
||||
35: TV/HD # 1080i
|
||||
36: TV/HD # 720p
|
||||
|
||||
# XXX
|
||||
59: XXX # Full Blu-ray
|
||||
46: XXX # 1080p
|
||||
51: XXX # 720p
|
||||
|
||||
# Music
|
||||
53: Audio/Video # Full Blu-ray
|
||||
57: Audio/Video # Remux
|
||||
45: Audio/Video # 1080p
|
||||
58: Audio/Video # 720p
|
||||
38: Audio/Lossless # Flac
|
||||
|
||||
41: TV/Sport # Sports
|
||||
42: TV/Anime # Anime
|
||||
44: PC # Windows Apps
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: table.lista > tbody > tr > td.lista > span[style="color:#FF0000;"]
|
||||
test:
|
||||
path: index.php
|
||||
selector: ul#navlist
|
||||
|
||||
ratio:
|
||||
path: index.php
|
||||
selector: "ul#navlist > li:contains(\"Ratio: \")"
|
||||
filters:
|
||||
- name: split
|
||||
args: ["\u00a0", 1]
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
inputs:
|
||||
search: "{{ .Query.Keywords }}"
|
||||
page: torrents
|
||||
options: 0
|
||||
active: 0
|
||||
rows:
|
||||
selector: div.b-content > table.lista > tbody > tr:has(a[href^="index.php?page=torrents&category="])
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["View details: ", ""]
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
comments:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
size:
|
||||
selector: p
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", ""]
|
||||
- name: regexp
|
||||
args: "\\|\\s+Size:\\s+([\\w\\d\\.,]+ \\w\\w)\\s+\\|"
|
||||
date:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "<center>Added:(.*?)</center>"
|
||||
grabs:
|
||||
selector: a[href^="index.php?page=torrent_history&id="]
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: a[title="Click here to view peers details"]:nth-child(1)
|
||||
leechers:
|
||||
selector: a[title="Click here to view peers details"]:nth-child(2)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[alt="gold"]: "0"
|
||||
img[alt="silver"]: "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[alt="2x Upload Multiplier"]: "2"
|
||||
img[alt="3x Upload Multiplier"]: "3"
|
||||
img[alt="4x Upload Multiplier"]: "4"
|
||||
img[alt="5x Upload Multiplier"]: "5"
|
||||
img[alt="6x Upload Multiplier"]: "6"
|
||||
img[alt="7x Upload Multiplier"]: "7"
|
||||
img[alt="8x Upload Multiplier"]: "8"
|
||||
img[alt="9x Upload Multiplier"]: "9"
|
||||
img[alt="10x Upload Multiplier"]: "10"
|
||||
"*": "1"
|
176
src/Jackett/Definitions/eotforum.yml
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
site: eotforum
|
||||
name: EoT-Forum
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- http://eot-forum.net
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Filme
|
||||
14: Movies/SD # SD XviD
|
||||
15: Movies/SD # SD x264
|
||||
16: Movies/HD # HD
|
||||
68: Movies/HD # UHD
|
||||
17: Movies/3D # 3D
|
||||
18: Movies/DVD # DVD-R
|
||||
19: Movies # Pack
|
||||
20: Movies # International
|
||||
21: XXX # XXX
|
||||
|
||||
# Serien/TV
|
||||
23: TV/SD # SD XviD
|
||||
24: TV/SD # SD x264
|
||||
25: TV/HD # HD
|
||||
26: TV/SD # DVD-R
|
||||
27: TV # Pack
|
||||
28: TV # International
|
||||
29: TV/Sport # Sport
|
||||
|
||||
# Dokus
|
||||
31: TV/Documentary # SD XviD
|
||||
32: TV/Documentary # SD x264
|
||||
33: TV/Documentary # HD
|
||||
34: TV/Documentary # 3D
|
||||
35: TV/Documentary # Pack
|
||||
67: TV/Documentary # DVD-R
|
||||
36: TV/Documentary # International
|
||||
|
||||
# Audio
|
||||
38: Audio # Charts
|
||||
39: Audio/MP3 # MP3
|
||||
40: Audio/Lossless # Flac
|
||||
41: Audio # Pack
|
||||
42: Audio/Video # MusikVideo
|
||||
43: Audio/Audiobook # Hörbücher
|
||||
|
||||
# Spiele
|
||||
45: PC/Games # Windows
|
||||
46: PC/Mac # MacOS
|
||||
47: Console/PS4 # Sony PS
|
||||
48: Console/Xbox # Microsoft XBox
|
||||
49: Console/NDS # Nintendo
|
||||
50: PC/Games # Linux
|
||||
51: Console # Andere
|
||||
|
||||
# Software
|
||||
53: PC # Windows
|
||||
54: PC/Mac # MacOS
|
||||
55: PC # Linux
|
||||
56: PC/Phone-Android # Android
|
||||
57: PC/Phone-IOS # Apple IOS
|
||||
58: PC/Phone-Other # Andere
|
||||
|
||||
# Sonstiges
|
||||
60: Books # EBooks
|
||||
61: Other # Bilder
|
||||
62: TV/Anime # Anime
|
||||
63: Other # MISC
|
||||
64: XXX # XXX-Bilder/EBooks/Audio
|
||||
|
||||
# EOT-Specials
|
||||
66: Other # Special
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
rememberme: "forever"
|
||||
submit: "Login"
|
||||
error:
|
||||
- selector: td.lista[align="center"][colspan="2"] > span
|
||||
test:
|
||||
path: index.php
|
||||
selector: img[alt="Ratio"]
|
||||
|
||||
ratio:
|
||||
path: index.php
|
||||
selector: img[alt="Ratio"] + font
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
inputs:
|
||||
page: "torrents"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
options: "0"
|
||||
active: "0"
|
||||
gold: "0"
|
||||
rows:
|
||||
selector: table.lista > tbody > tr:has(a[href^="index.php?page=torrent-details&id="])
|
||||
dateheaders:
|
||||
selector: ":has(td.header > b)"
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Torrents vom ", ""]
|
||||
- name: replace
|
||||
args: ["Januar", "January"]
|
||||
- name: replace
|
||||
args: ["Februar", "February"]
|
||||
- name: replace
|
||||
args: ["March", "März"]
|
||||
- name: replace
|
||||
args: ["Mai", "May"]
|
||||
- name: replace
|
||||
args: ["Juni", "June"]
|
||||
- name: replace
|
||||
args: ["Juli", "July"]
|
||||
- name: replace
|
||||
args: ["Oktober", "October"]
|
||||
- name: replace
|
||||
args: ["Dezember", "December"]
|
||||
- name: dateparse
|
||||
args: "02.January.2006"
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: title
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Details anzeigen: ", ""]
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
comments:
|
||||
selector: a[href*="#comments"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(3)
|
||||
grabs:
|
||||
selector: td:nth-child(5) > font:nth-child(3)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
seeders:
|
||||
selector: td:nth-child(5) > font:nth-child(1)
|
||||
leechers:
|
||||
selector: td:nth-child(5) > font:nth-child(2)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[alt="gold"]: "0"
|
||||
img[alt="silver"]: "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[alt="2x Upload Multiplier"]: "2"
|
||||
img[alt="3x Upload Multiplier"]: "3"
|
||||
img[alt="4x Upload Multiplier"]: "4"
|
||||
img[alt="5x Upload Multiplier"]: "5"
|
||||
img[alt="6x Upload Multiplier"]: "6"
|
||||
img[alt="7x Upload Multiplier"]: "7"
|
||||
img[alt="8x Upload Multiplier"]: "8"
|
||||
img[alt="9x Upload Multiplier"]: "9"
|
||||
img[alt="10x Upload Multiplier"]: "10"
|
||||
"*": "1"
|
113
src/Jackett/Definitions/ethor.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
site: ethor
|
||||
name: Ethor.net (Thor's Land)
|
||||
description: "A French gerneral tracker"
|
||||
language: fr-fr
|
||||
links:
|
||||
- https://ethor.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
22: PC # Applications/Divers
|
||||
1: PC # Applications/PC ISO
|
||||
44: PC # Applications/Portable
|
||||
47: Movies/BluRay # Films/Bluray
|
||||
20: Movies/DVD # Films/DVDr
|
||||
42: Movies/HD # Films/HD Rip
|
||||
19: Movies/SD # Films/SD Rip
|
||||
5: Movies/SD # Films/VCD
|
||||
4: PC/Games # Jeux/PC
|
||||
41: Console # Jeux/Portable
|
||||
34: Console/PS4 # Jeux/PS2-PS3
|
||||
38: Console/Wii # Jeux/Wii-GC
|
||||
40: Console/Xbox # Jeux/Xbox360
|
||||
6: Audio # Musique
|
||||
37: Audio/Video # Musique/Video
|
||||
48: TV/HD # Série-Télé/Bluray
|
||||
45: TV/SD # Série-Télé/DVDr
|
||||
43: TV/HD # Série-Télé/HD Rip
|
||||
7: TV/SD # Série-Télé/SD Rip
|
||||
23: Books # E-Books
|
||||
46: Other # Évé. sportif
|
||||
36: Other # Kidz
|
||||
25: Other # Misc
|
||||
9: XXX # XXX
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: cookie
|
||||
type: text
|
||||
label: Cookie
|
||||
|
||||
login:
|
||||
method: cookie
|
||||
inputs:
|
||||
cookie: "{{ .Config.cookie }}"
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
ratio:
|
||||
path: browse.php
|
||||
selector: span#ratioRatio
|
||||
|
||||
search:
|
||||
path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
advcat: "0"
|
||||
incldead: "1"
|
||||
stype: "b"
|
||||
dp: "0"
|
||||
isUserClick: "0"
|
||||
rows:
|
||||
selector: p + table > tbody > tr:has(a[href^="/details.php"])
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["/details.php", "/download.php"]
|
||||
title:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
category:
|
||||
selector: a[href^="/browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href^="/details.php"]:has(b)
|
||||
attribute: href
|
||||
files:
|
||||
selector: a[href*="#filelist"]
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
grabs:
|
||||
selector: td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: append
|
||||
args: " -05:00" # timezone offset
|
||||
- name: dateparse
|
||||
args: "2006-01-0215:04:05 -07:00"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"img[title^=\"Freeleech: \"]": "0"
|
||||
"img[title^=\"Half Freeleech: \"]": "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
101
src/Jackett/Definitions/freakstrackingsystem.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
site: freakstrackingsystem
|
||||
name: Freaks Tracking System
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://fts.to
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Movies
|
||||
2: TV
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /index.php?form=UserLogin
|
||||
method: form
|
||||
inputs:
|
||||
loginUsername: "{{ .Config.username }}"
|
||||
loginPassword: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: p.innerError
|
||||
test:
|
||||
path: index.php?page=FTSAttachmentList
|
||||
selector: "span:contains(\"Freaks-Punkte:\") > span"
|
||||
|
||||
ratio:
|
||||
path: index.php?page=FTSAttachmentList
|
||||
selector: "span:contains(\"Freaks-Punkte:\") > span"
|
||||
|
||||
search:
|
||||
path: index.php
|
||||
inputs:
|
||||
page: "FTSAttachmentList"
|
||||
sortField: "uploadTime"
|
||||
sortOrder: "DESC"
|
||||
filename: "{{ .Query.Keywords }}"
|
||||
rows:
|
||||
selector: table.tableList > tbody > tr[class^="container-"]:has(td:nth-child(5) > a, td:nth-child(6) > a) # has makes sure that we can build a download link
|
||||
fields:
|
||||
download:
|
||||
selector: td:nth-child(5) > a, td:nth-child(6) > a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["page=FTSPeerList&torrentID=", "page=Attachment&attachmentID="]
|
||||
title:
|
||||
selector: td:nth-child(4) > a
|
||||
category:
|
||||
text: 2
|
||||
details:
|
||||
selector: td:nth-child(4) > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(8)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
grabs:
|
||||
selector: td:nth-child(7)
|
||||
seeders:
|
||||
selector: td:nth-child(5)
|
||||
leechers:
|
||||
selector: td:nth-child(6)
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Heute", "Today"]
|
||||
- name: replace
|
||||
args: ["Gestern", "Yesterday"]
|
||||
- name: replace
|
||||
args: ["Januar", "January"]
|
||||
- name: replace
|
||||
args: ["Februar", "February"]
|
||||
- name: replace
|
||||
args: ["März", "March"]
|
||||
- name: replace
|
||||
args: ["Mai", "May"]
|
||||
- name: replace
|
||||
args: ["Juni", "June"]
|
||||
- name: replace
|
||||
args: ["Juli", "July"]
|
||||
- name: replace
|
||||
args: ["Oktober", "October"]
|
||||
- name: replace
|
||||
args: ["Dezember", "December"]
|
||||
- name: dateparse
|
||||
args: "2. January 2006, 15:04"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
101
src/Jackett/Definitions/hdme.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
site: hdme
|
||||
name: HDME
|
||||
language: en-us
|
||||
links:
|
||||
- https://hdme.eu
|
||||
|
||||
caps:
|
||||
categories:
|
||||
24: TV/Anime # Anime
|
||||
25: PC/0day # Appz
|
||||
47: Movies/HD # AVCHD
|
||||
26: Movies/BluRay # Bluray
|
||||
54: Movies/HD # dbREMUX
|
||||
41: Movies/HD # Documentaries
|
||||
50: Movies/HD # FourGHD
|
||||
44: Movies/HD # HDME
|
||||
28: Audio/Lossless # HQ Music
|
||||
48: Movies/HD # iCandy
|
||||
45: Movies/HD # INtL
|
||||
29: Other # Misc
|
||||
49: PC/Phone-Other # Mobile
|
||||
30: Movies/HD # Movie 1080i
|
||||
31: Movies/HD # Movie 1080p
|
||||
32: Movies/HD # Movie 720p
|
||||
33: Audio/Video # Music Videos
|
||||
34: TV # Packs
|
||||
53: Movies/HD # Remux
|
||||
56: Movies/HD # RUXi
|
||||
55: Movies/HD # SiNiSteR
|
||||
36: TV/Sport # Sports
|
||||
37: TV/HD # TV Series 1080i
|
||||
38: TV/HD # TV Series 1080p
|
||||
39: TV/HD # TV Series 720p
|
||||
57: Movies # UHD 2160p
|
||||
40: XXX # XXX
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: td.embedded
|
||||
message:
|
||||
selector: td.text
|
||||
test:
|
||||
path: /my.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: span.smallfont > font
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Ratio:(.+?)Uploaded"
|
||||
- name: replace
|
||||
args: [",", ""]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
incldead: "1"
|
||||
blah: "0"
|
||||
rows:
|
||||
selector: table[width="100%"] > tbody > tr:has(td.bottom[background="_images/bg_torrent.jpg"])
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-child(2) a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title:
|
||||
selector: td:nth-child(3) > a
|
||||
attribute: title
|
||||
comments:
|
||||
selector: td:nth-child(3) > a
|
||||
attribute: href
|
||||
download:
|
||||
selector: td:nth-child(11) > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
remove: br
|
||||
date:
|
||||
selector: td:nth-child(3)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Added: (.+?)\n"
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
117
src/Jackett/Definitions/newretro.yml
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
site: newretro
|
||||
name: The New Retro
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- http://new-retro.ddns.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
101: TV/Anime # Filme - Animie
|
||||
102: Movies/BluRay # Filme - Bluray
|
||||
131: Movies/Other # Filme - Bollywood
|
||||
103: Movies/DVD # Filme - DVD
|
||||
104: Movies/DVD # Filme - DVD-R
|
||||
132: Movies/DVD # Filme - HD2DVD
|
||||
130: Movies # Filme - Klassiker
|
||||
105: Movies # Filme - x264
|
||||
106: Movies # Filme - XviD / DivX
|
||||
69: XXX # XXX
|
||||
124: Audio # Musik - Alben
|
||||
122: Audio/Audiobook # Musik - Hörbuch
|
||||
123: Audio # Musik - Mixe
|
||||
133: Audio/MP3 # Musik - MP3
|
||||
125: Audio/Video # Musik - Video
|
||||
113: PC # Programme - Linux
|
||||
114: PC/Mac # Programme - Mac
|
||||
115: PC # Programme - Windows
|
||||
117: TV # Allgemein - Serien
|
||||
116: TV/Documentary # Serien - Dokus
|
||||
118: TV/Sport # Serien - Sport
|
||||
119: Other # Bilder
|
||||
120: Books # Ebook
|
||||
127: Other # Für Unsere kleinsten
|
||||
121: Other # Handy Stuff
|
||||
129: Other # Sonstiges
|
||||
109: Other # Spiele - Handy
|
||||
112: Console # Spiele - Konsolen
|
||||
111: PC/Games # Spiele - Mac / Linux
|
||||
110: PC/Games # Spiele - PC
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: table.tableinborder:contains("Anmeldung Gescheitert!") > tbody > tr > td.tablea
|
||||
test:
|
||||
path: /usercp.php
|
||||
|
||||
ratio:
|
||||
path: /usercp.php
|
||||
selector: div#lmtd table > tbody > tr:contains("Ratio:") > td:nth-child(2)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
showsearch: "1"
|
||||
orderby: "added"
|
||||
sort: "desc"
|
||||
incldead: "1"
|
||||
|
||||
rows:
|
||||
selector: table.tableinborder[summary] > tbody > tr
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?torrent="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="details.php?id="]:has(b)
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(4) > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(1)
|
||||
grabs:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(3) > b
|
||||
files:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(1) > b:nth-child(2)
|
||||
seeders:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(1)
|
||||
leechers:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(2) > b:nth-child(3)
|
||||
date:
|
||||
selector: td.tablea > table > tbody > tr:nth-child(2) > td:nth-child(5)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", " "]
|
||||
- name: dateparse
|
||||
args: "02.01.2006 15:04:05"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"font[color=\"red\"]:contains(\"Only Upload\")": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
92
src/Jackett/Definitions/sdbits.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
site: sdbits
|
||||
name: SDBits
|
||||
description: "SDBits is a small tracker that focuses on SD movies and tv."
|
||||
language: en-us
|
||||
links:
|
||||
- http://sdbits.org
|
||||
|
||||
caps:
|
||||
categories:
|
||||
6: Audio # Audio
|
||||
3: TV/Documentary # Documentary
|
||||
1: Movies # Movies
|
||||
4: Audio # Music
|
||||
5: TV/Sport # Sports
|
||||
2: TV # TV
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takeloginn3.php
|
||||
method: post
|
||||
inputs:
|
||||
uname: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("failed")+table)
|
||||
test:
|
||||
path: /browse.php
|
||||
selector: span.smallfont:has(a[href="logout.php"])
|
||||
|
||||
ratio:
|
||||
path: /browse.php
|
||||
selector: span.smallfont:has(a[href="logout.php"])
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Ratio:[ \u00a0](.*?)\u00a0"
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
method: post
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
descriptions: "0"
|
||||
rows:
|
||||
selector: table#torrent-list > tbody > tr[id]
|
||||
fields:
|
||||
title:
|
||||
selector: td:nth-child(3) > b > a
|
||||
category:
|
||||
selector: a[href^="?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
details:
|
||||
selector: td:nth-child(3) > b > a
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
grabs:
|
||||
selector: td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
filters:
|
||||
- name: append
|
||||
args: " ago"
|
||||
imdb:
|
||||
selector: a[href^="http://www.imdb.com/"]
|
||||
attribute: href
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"a[style=\"color:#000099\"][href^=\"details.php?\"]": "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
127
src/Jackett/Definitions/secretcinema.yml
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
site: secretcinema
|
||||
name: Secret Cinema
|
||||
description: "A tracker for rare movies."
|
||||
language: en-us
|
||||
encoding: "UTF-8"
|
||||
links:
|
||||
- http://www.secret-cinema.net
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: TV/Anime # Animation
|
||||
2: Movies # Arthouse
|
||||
3: Movies # Asian
|
||||
19: Audio/Audiobook # Audiobooks
|
||||
29: Movies # Badfilm
|
||||
18: Books # Books
|
||||
4: Movies # Classics
|
||||
5: Movies # Comedy
|
||||
20: Books/Comics # Comix
|
||||
6: Movies # Cult
|
||||
7: TV/Documentary # Documentary
|
||||
8: Movies # Fantasy & SF
|
||||
9: Movies # Horror
|
||||
22: Movies # Noir
|
||||
17: Audio # OST
|
||||
10: Other # Other
|
||||
15: TV # Other TV
|
||||
16: Audio # Radio
|
||||
11: Movies # Silent
|
||||
12: TV # Talent Show!
|
||||
14: Movies # TV Movies
|
||||
13: TV # TV Series
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
returnto: "index.php"
|
||||
test:
|
||||
path: /browse.php
|
||||
selector: div.Userstats
|
||||
|
||||
ratio:
|
||||
path: /browse.php
|
||||
selector: div.Userstats
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "\n\u00a0(.*) \u00a0"
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}cat{{.}}=on&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
tpp: "100"
|
||||
dirty: "1"
|
||||
rows:
|
||||
selector: table[id="large"] > tbody > tr:has(div.browsing)
|
||||
dateheaders:
|
||||
selector: ":has(td.colhead[title] > b)"
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "Mon 02 Jan"
|
||||
fields:
|
||||
title:
|
||||
selector: a[href^="viewtopic.php?id="]
|
||||
description:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(2)
|
||||
category:
|
||||
selector: a[href^="browse.php?cat"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["browse.php?cat", ""]
|
||||
- name: replace
|
||||
args: ["=on", ""]
|
||||
details:
|
||||
selector: a[href^="viewtopic.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php/"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(4)
|
||||
files:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(3)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
grabs:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(5)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
seeders:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(6)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
leechers:
|
||||
selector: table > tbody > tr:nth-child(2) > td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
date:
|
||||
selector: td:nth-child(1) > div > table > tbody > tr:nth-child(2) > td:nth-child(1)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["'", ""]
|
||||
- name: replace
|
||||
args: ["\xA0", ""]
|
||||
- name: dateparse
|
||||
args: "02 Jan 0615:04"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
150
src/Jackett/Definitions/shareisland.yml
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
site: shareisland
|
||||
name: Shareisland
|
||||
description: "A general italian tracker"
|
||||
language: it-it
|
||||
links:
|
||||
- http://shareisland.org
|
||||
|
||||
caps:
|
||||
categories:
|
||||
32: Other # Vip
|
||||
45: XXX # Vip XXX
|
||||
33: Other # ex Vip
|
||||
1: Movies # Movies
|
||||
49: Movies/HD # H-264
|
||||
51: Movies/HD # H-265
|
||||
41: Movies/Other # Cartoons
|
||||
14: Movies/SD # DivX
|
||||
16: Movies/Other # Cine News
|
||||
39: Movies/HD # 720p
|
||||
40: Movies/HD # 1080p
|
||||
46: Movies/BluRay # Blu Ray Disk
|
||||
31: Movies/HD # BDRip
|
||||
17: Movies/DVD # DVD
|
||||
43: Movies/SD # DVDRip
|
||||
6: PC # Applications
|
||||
18: PC/0day # PC Applications
|
||||
19: PC/Mac # Macintosh Applications
|
||||
44: PC/Phone-Android # Android applications
|
||||
7: Audio # Music
|
||||
20: Audio/Video # Video
|
||||
21: Audio/MP3 # Mp3
|
||||
2: Console # Games
|
||||
3: Console/PS4 # Sony PS
|
||||
4: Console/Wii # Wii
|
||||
26: Console/Xbox # XboX
|
||||
27: PC/Games # PC
|
||||
28: Console/NDS # Nintendo
|
||||
34: Books # Edicola
|
||||
52: Books # Quotidiani
|
||||
53: Books # Libreria
|
||||
35: TV # SerieTV
|
||||
55: TV/HD # Serie Tv HD
|
||||
36: Other # Rip By ShareIsland
|
||||
47: Other # Disclaimer
|
||||
48: Other # P2P network
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /ajax/login.php
|
||||
method: post
|
||||
form: form
|
||||
inputs:
|
||||
loginbox_membername: "{{ .Config.username }}"
|
||||
loginbox_password: "{{ .Config.password }}"
|
||||
action: "login"
|
||||
loginbox_remember: "true"
|
||||
error:
|
||||
- selector: div.error
|
||||
test:
|
||||
path: /?p=home&pid=1
|
||||
selector: div#member_info_bar
|
||||
|
||||
ratio:
|
||||
path: /?p=home&pid=1
|
||||
selector: img[title="Ratio"] + span
|
||||
|
||||
search:
|
||||
path: /
|
||||
inputs:
|
||||
p: "torrents"
|
||||
pid: "32"
|
||||
$raw: "{{range .Categories}}cid[]:{{.}}&{{end}}"
|
||||
keywords: "{{ .Query.Keywords }}"
|
||||
search_type: "name"
|
||||
searchin: "title"
|
||||
|
||||
rows:
|
||||
selector: table#torrents_table_classic > tbody > tr:not([id="torrents_table_classic_head"])
|
||||
fields:
|
||||
title:
|
||||
selector: td.torrent_name > a
|
||||
category:
|
||||
selector: div.category_image > a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cid
|
||||
comments:
|
||||
selector: td.torrent_name > a
|
||||
attribute: href
|
||||
download:
|
||||
selector: a:has(img[title="Download Torrent"])
|
||||
attribute: href
|
||||
size:
|
||||
selector: td.size
|
||||
seeders:
|
||||
selector: td.seeders
|
||||
leechers:
|
||||
selector: td.leechers
|
||||
grabs:
|
||||
selector: td.completed
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"img[title=\"FREE!\"]": "0"
|
||||
"img[title=\"Download Multiplier: 0.5\"]": "0.5"
|
||||
"img[title=\"Moltiplicatore Download: 0.5\"]": "0.5"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"img[title=\"Upload Multiplier: 3\"]": "3"
|
||||
"img[title=\"Upload Multiplier: 2\"]": "2"
|
||||
"img[title=\"Upload Multiplier: 1.5\"]": "1.5"
|
||||
"img[title=\"Moltiplicatore Upload: 3\"]": "3"
|
||||
"img[title=\"Moltiplicatore Upload: 2\"]": "2"
|
||||
"img[title=\"Moltiplicatore Upload: 1.5\"]": "1.5"
|
||||
"*": "1"
|
||||
date:
|
||||
selector: td.torrent_name
|
||||
remove: a, span, div, br
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Uploaded ", ""]
|
||||
- name: replace
|
||||
args: [" by", ""]
|
||||
- name: replace
|
||||
args: [" at", ""]
|
||||
- name: replace
|
||||
args: ["Oggi", "Today"]
|
||||
- name: replace
|
||||
args: ["Ieri", "Yesterday"]
|
||||
- name: replace
|
||||
args: ["lunedì", "Monday"]
|
||||
- name: replace
|
||||
args: ["martedì", "Tuesday"]
|
||||
- name: replace
|
||||
args: ["Mercoledì", "Wednesday"]
|
||||
- name: replace
|
||||
args: ["Giovedì", "Thursday"]
|
||||
- name: replace
|
||||
args: ["Venerdì", "Friday"]
|
||||
- name: replace
|
||||
args: ["Sabato", "Saturday"]
|
||||
- name: replace
|
||||
args: ["Domenica", "Sunday"]
|
||||
- name: replace
|
||||
args: [" alle", ""]
|
101
src/Jackett/Definitions/thehorrorcharnel.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
site: thehorrorcharnel
|
||||
name: The Horror Charnel
|
||||
description: "A Horror, Cult, Sleaze, Sci-Fi & more tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://horrorcharnel.org
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Movies
|
||||
2: TV
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
|
||||
login:
|
||||
path: loginto.php
|
||||
method: form
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
use_sslvalue=: "on"
|
||||
perm_ssl: "1"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: "div#base_content > table.mainouter > tbody > tr > td.outer > table.main > tbody > tr > td:has(h2)"
|
||||
test:
|
||||
path: usercp.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: td.navi_top:contains("Deine Ratio:")
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Deine Ratio: ", ""]
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
#$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: 1
|
||||
|
||||
rows:
|
||||
selector: p + table > tbody > tr:has(a[href^="details.php?id="])
|
||||
after: 1
|
||||
fields:
|
||||
title:
|
||||
selector: a[onmouseover][href^="details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: <b>(.*?)</b>
|
||||
description:
|
||||
selector: td[colspan="13"]
|
||||
remove: a
|
||||
category:
|
||||
text: 1
|
||||
comments:
|
||||
selector: a[onmouseover][href^="details.php"]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(6)
|
||||
size:
|
||||
selector: td:nth-child(9)
|
||||
seeders:
|
||||
selector: td:nth-child(11)
|
||||
leechers:
|
||||
selector: td:nth-child(12)
|
||||
date:
|
||||
selector: td:nth-child(8)
|
||||
grabs:
|
||||
selector: td:nth-child(10)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="free.gif"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
151
src/Jackett/Definitions/theshinning.yml
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
site: theshinning
|
||||
name: The Shinning
|
||||
description: "A German gerneral tracker"
|
||||
language: de-de
|
||||
encoding: windows-1252
|
||||
links:
|
||||
- https://theshinning.org
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Filme
|
||||
28: Movies/SD # HORROR
|
||||
25: Movies/DVD # SD|DVD
|
||||
99: Movies/SD # SD|SPORT
|
||||
24: Movies/SD # SD|x264
|
||||
22: Movies/SD # SD|XVID
|
||||
26: XXX # SD|XxX
|
||||
|
||||
# High Definition
|
||||
29: Movies/BluRay # BLURAY
|
||||
19: Movies/HD # HD|1080p
|
||||
21: Movies/3D # HD|3D
|
||||
107: Movies/HD # HD|4K
|
||||
20: Movies/HD # HD|720p
|
||||
101: Movies/HD # HD|REMUX
|
||||
100: Movies/HD # HD|SPORT
|
||||
27: XXX # HD|XxX
|
||||
102: Movies/HD # SMALL|HD
|
||||
|
||||
# Musik
|
||||
13: Audio # CHARTS
|
||||
31: Audio/Lossless # FLAC
|
||||
97: Audio/Audiobook # HOERSPIELE
|
||||
30: Audio/MP3 # MP3
|
||||
106: Audio # SAMPLER
|
||||
|
||||
# Releaser
|
||||
18: Movies # CR3WHD
|
||||
16: Movies # ONKEL JENS
|
||||
17: Movies # xTM|XviD
|
||||
|
||||
# Serie
|
||||
11: TV/SD # DVD|SERIEN
|
||||
9: TV/HD # HD|SERIEN
|
||||
32: TV # MIXED|SERIEN
|
||||
10: TV # PACK|SERIEN
|
||||
8: TV/SD # SD|SERIEN
|
||||
105: TV # SMALL|SERIE
|
||||
12: TV # US|SERIEN
|
||||
|
||||
# Sonstige
|
||||
98: PC # APPS
|
||||
90: TV/Documentary # DOKUS
|
||||
91: Books # EBOOKS
|
||||
14: Movies # KINOHITS
|
||||
15: Other # REQUEST
|
||||
23: Other # SONSTIGES
|
||||
104: XXX/Imageset # XXX|IMAGE
|
||||
|
||||
# Spiele
|
||||
96: Console/NDS # GAMES|NDS
|
||||
92: PC/Games # GAMES|PC
|
||||
94: Console/PS4 # GAMES|PS
|
||||
95: Console/Wii # GAMES|WII
|
||||
93: Console/Xbox # GAMES|XBOX
|
||||
|
||||
# Wrestling
|
||||
3: TV/Sport # HD|RAW
|
||||
1: TV/Sport # HD|SMACKD
|
||||
6: TV/Sport # NXT
|
||||
7: TV/Sport # PPV
|
||||
4: TV/Sport # SD|RAW
|
||||
2: TV/Sport # SD|SMACKD
|
||||
5: TV/Sport # TNA
|
||||
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /login.php
|
||||
method: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: div.stderr_info_wrap
|
||||
test:
|
||||
path: /browse.php
|
||||
selector: img[title="Ratio"] + i
|
||||
|
||||
ratio:
|
||||
path: /browse.php
|
||||
selector: img[title="Ratio"] + i
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
showsearch: "1"
|
||||
incldead: "1"
|
||||
orderby: "added"
|
||||
sort: "desc"
|
||||
rows:
|
||||
selector: table.main > tbody > tr:contains("Alle Torrents") + tr > td > table.tableinborder > tbody > tr
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download-ssl.php?torrent="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: div.title_wrap
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
details:
|
||||
selector: div.title_wrap > a
|
||||
attribute: href
|
||||
size:
|
||||
selector: div.bro_right_ad > b
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
grabs:
|
||||
selector: div.bro_right_ae > b
|
||||
seeders:
|
||||
selector: div.bro_box1_aa > b
|
||||
leechers:
|
||||
selector: div.bro_box_aaa > b
|
||||
date:
|
||||
selector: div.bro_box_date > span
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", " "]
|
||||
- name: dateparse
|
||||
args: "02.01.2006 15:04:05"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
span[title="OnlyUp"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
150
src/Jackett/Definitions/torrentbd.yml
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
site: torrentbd
|
||||
name: TorrentBD
|
||||
description: "A general tracker"
|
||||
language: en-us
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.torrentbd.com/torrent
|
||||
|
||||
caps:
|
||||
categories:
|
||||
28: TV/Anime # Anime - All
|
||||
65: PC/Phone-Android # Apps - Android
|
||||
20: PC # Apps - Linux
|
||||
19: PC/Mac # Apps - Mac
|
||||
18: PC # Apps - PC
|
||||
21: Audio # Bangla - Audio
|
||||
7: Movies # Bangla - Movies | Natok
|
||||
49: TV # Cartoons - All
|
||||
9: TV/Documentary # Documentaries - All
|
||||
73: Books/Comics # E-Books - Comics
|
||||
77: Books # E-Books - Manga
|
||||
60: PC/Games # Games - Cracks | Patches
|
||||
17: Console # Games - Other
|
||||
10: PC # Games - PC
|
||||
11: Console/PS3 # Games - PS2
|
||||
43: Console/PS3 # Games - PS3
|
||||
12: Console/PSP # Games - PSP
|
||||
52: PC/Games # Games - Updates | DLC
|
||||
13: Console/Xbox # Games - Xbox
|
||||
14: Console/Xbox360 # Games - Xbox360
|
||||
23: Movies/BluRay # Hindi - Blu-ray
|
||||
51: Movies/SD # Hindi - CAM | TS | TC
|
||||
27: Movies/DVD # Hindi - DVDRip
|
||||
58: Movies/SD # Hindi - DVDScr | Pre-DVD
|
||||
68: Movies/SD # Hindi - HD-Rip
|
||||
59: Movies # Hindi - Web-Rip
|
||||
47: Movies/HD # Movies - 1080p BluRay
|
||||
67: Movies/3D # Movies - 3D
|
||||
42: Movies/BluRay # Movies - 720p BluRay
|
||||
57: Movies # Movies - Animation
|
||||
4: Movies/SD # Movies - CAM | TS | TC
|
||||
69: Movies # Movies - Dual Audio
|
||||
1: Movies/SD # Movies - DVDRip
|
||||
56: Movies/SD # Movies - DVDSCR | R5 | R6
|
||||
46: Movies/SD # Movies - HD-Rip
|
||||
76: Movies/BluRay # Movies - Lossless BluRay
|
||||
2: Movies # Movies - Packs
|
||||
24: Movies/SD # Movies - SD BluRay
|
||||
34: Movies # Movies - Tamil
|
||||
3: Movies # Movies - Unrated
|
||||
72: Movies # Movies - WEB Rip
|
||||
55: Movies # Movies - WEB-DL
|
||||
22: Audio # Music - Audio
|
||||
64: Audio # Music - Concerts | Live Shows
|
||||
71: Audio/Lossless # Music - FLAC
|
||||
50: Audio # Music - OST
|
||||
26: Audio # Music - Radio
|
||||
25: Audio # Music - Video
|
||||
78: Audio # Music - Video 4K
|
||||
36: Books # Other - E-Books
|
||||
37: Other # Other - Images
|
||||
38: PC/Phone-Other # Other - Mobile Phone
|
||||
40: Other # Other - Other | Misc
|
||||
39: Other # Other - Tutorial
|
||||
44: Other # Religious - Islam
|
||||
48: TV/Sport # Sports - All
|
||||
70: TV/Sport # Sports - Football
|
||||
6: TV/Sport # Sports - Pro Wrestling
|
||||
33: Other # Trailers - Movie | Games
|
||||
66: TV # TV - Awards | Ceremonies
|
||||
5: TV/SD # TV - Episodes
|
||||
61: TV/HD # TV - Episodes - 720p | 1080p
|
||||
41: TV/SD # TV - Packs
|
||||
62: TV/HD # TV - Packs - HD | BRRip
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: account-login.php
|
||||
method: form
|
||||
form: form[action="account-login.php"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: div.myFrame:has(div.myF-caption > font.error)
|
||||
test:
|
||||
path: torrents-search.php
|
||||
selector: div.myB-content:contains("Ratio:")
|
||||
|
||||
ratio:
|
||||
path: torrents-search.php
|
||||
selector: div.myB-content:contains("Ratio:")
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Ratio: (.*?)$"
|
||||
|
||||
search:
|
||||
path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
rows:
|
||||
selector: tr.t-row
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: span.torrname
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href^="torrents.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
details:
|
||||
selector: a[onmouseover][href^="torrents-details.php?id="]
|
||||
attribute: href
|
||||
details:
|
||||
selector: a[href*="#comments"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
grabs:
|
||||
selector: td:nth-child(9)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
date:
|
||||
selector: a[onmouseover][href^="torrents-details.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "Date Added: </b>(\\d+-\\d+-\\d+)<br />"
|
||||
- name: dateparse
|
||||
args: "02-01-2006"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[title^="FreeLeech "]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
175
src/Jackett/Definitions/torrentsectorcrew.yml
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
site: torrentsectorcrew
|
||||
name: Torrent Sector Crew
|
||||
description: "A German general tracker"
|
||||
language: de-de
|
||||
links:
|
||||
- https://tsctracker.net/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
# Apps
|
||||
65: PC/Phone-Android # Android
|
||||
83: PC/Phone-IOS # iOS
|
||||
107: PC/0day # Linux
|
||||
48: PC/Mac # MAC
|
||||
109: PC # Sonstige
|
||||
22: PC/0day # Win
|
||||
|
||||
# Audio
|
||||
24: Audio/Audiobook # aBooks
|
||||
104: Audio # Disco's
|
||||
38: Audio/Audiobook # Hörspiel
|
||||
6: Audio # Musik
|
||||
82: Audio # Tracks
|
||||
29: Audio/Video # Videos
|
||||
|
||||
# Doku
|
||||
113: TV/Documentary # 3D
|
||||
76: TV/Documentary # HD
|
||||
78: TV/Documentary # Packs
|
||||
75: TV/Documentary # SD
|
||||
114: TV/Documentary # Sonstige
|
||||
77: TV/Documentary # Untouched
|
||||
|
||||
# Filme
|
||||
54: Movies/HD # 1080p
|
||||
5: Movies/3D # 3D
|
||||
55: Movies/HD # 720p
|
||||
111: Movies # Anime
|
||||
43: Movies/BluRay # BluRay
|
||||
20: Movies/DVD # DVDR
|
||||
120: Movies/Foreign # Int.
|
||||
119: Movies # Remux
|
||||
121: Movies/HD # UHD
|
||||
36: Movies/HD # x264
|
||||
19: Movies/SD # XviD
|
||||
|
||||
# Serien
|
||||
112: TV/Anime # Anime
|
||||
69: TV/HD # HD
|
||||
72: TV/Foreign # Int.
|
||||
68: TV # Packs
|
||||
66: TV/SD # SD
|
||||
108: TV # TV-Shows
|
||||
|
||||
# Sonstige
|
||||
117: Other # Diverses
|
||||
28: Books # eBooks
|
||||
42: TV/Sport # Sport
|
||||
103: Other # Tutorials
|
||||
9: Other # Wallpaper
|
||||
64: XXX # XXX
|
||||
|
||||
# Spiele
|
||||
115: PC/Mac # MAC
|
||||
37: Console/NDS # Nintendo
|
||||
4: PC/Games # PC
|
||||
58: Console/PS4 # PSX
|
||||
116: Other # Sonstige
|
||||
50: Console/Xbox # XBOX
|
||||
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
settings:
|
||||
- name: pin
|
||||
type: text
|
||||
label: Pin
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
|
||||
login:
|
||||
path: landing.php
|
||||
method: form
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
pin: "{{ .Config.pin }}"
|
||||
error:
|
||||
- selector: "#login_box_desc"
|
||||
test:
|
||||
path: my.php
|
||||
|
||||
ratio:
|
||||
path: /my.php
|
||||
selector: td.navi_top:contains("Deine Ratio:")
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Deine Ratio: ", ""]
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
|
||||
search:
|
||||
path: browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
orderby: "added"
|
||||
sort: desc
|
||||
rows:
|
||||
selector: "h2 +p + br + table.tablebrowse > tbody > tr[style=\"height: 45px;\"], tr:contains(\"Weiter\") > td > table.tablebrowse > tbody > tr[style=\"height: 45px;\"]"
|
||||
fields:
|
||||
title:
|
||||
selector: a[title][href^="details.php"]
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href*="cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href*="&tocomm="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download_ssl.php"]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(3)
|
||||
grabs:
|
||||
selector: td:nth-child(9)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["-mal", ""]
|
||||
size:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
date:
|
||||
selector: td:nth-child(5)
|
||||
remove: br
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "02.01.200615:04:05"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"font:contains(\"[OnlyUpload]\")": "0"
|
||||
"font:contains(\"[-40 Download]\")": "0.6"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
143
src/Jackett/Definitions/tspate.yml
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
site: tspate
|
||||
name: Tspate
|
||||
description: "A French gerneral tracker"
|
||||
language: fr-fr
|
||||
links:
|
||||
- https://www.tspate.me/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
50: TV/Anime # Anime: Tout
|
||||
40: PC # Apps: Windows
|
||||
41: PC/Mac # Apps: Mac
|
||||
42: PC/Phone-Android # Apps: Android
|
||||
43: PC/Phone-IOS # Apps: Ipod/Iphone
|
||||
44: PC # Apps: Autres
|
||||
45: PC # Apps: Linux
|
||||
27: Books # E-Books: Livres
|
||||
28: Books # E-Books: Audio
|
||||
29: Books/Comics # E-Books: Comics
|
||||
30: Books # E-Books: Mangas
|
||||
32: Books # E-Books: Bds
|
||||
31: Books # E-Books: Presse
|
||||
63: Books # E-Books: ePUB
|
||||
1: Movies/SD # Films: DVDRip
|
||||
2: Movies/SD # Films: DVDRip VOSTFR
|
||||
3: Movies/DVD # Films: DVD-R
|
||||
4: Movies/HD # Films: 720p
|
||||
5: Movies/HD # Films: 1080p
|
||||
6: Movies/BluRay # Films: BluRay x264 et x265
|
||||
7: Movies/3D # Films: BluRay-3D
|
||||
11: Movies/HD # Films: BDRip
|
||||
9: Movies/DVD # Films: R5
|
||||
10: Movies/SD # Films: DVDSCR
|
||||
56: Movies/HD # Films: mHD
|
||||
12: Movies/HD # Films: BRRip
|
||||
13: Movies/HD # Films: WEBRip
|
||||
58: Movies/HD # Films: WEBRip 720p
|
||||
59: Movies/HD # Films: WEBRip 1080p
|
||||
8: Movies/SD # Films: Cam / TS
|
||||
62: Movies/HD # Films: Uhd4k
|
||||
35: Console/Xbox # Jeux: XBOX
|
||||
36: Console/Wii # Jeux: WII
|
||||
37: Console/PSP # Jeux: PSP
|
||||
38: Console/NDS # Jeux: DS
|
||||
39: PC/Phone-Android # Jeux: Android
|
||||
34: Console/PS3 # Jeux: PS3
|
||||
55: PC/Mac # Jeux: Mac
|
||||
33: PC/Games # Jeux: PC
|
||||
46: Audio/MP3 # Musiques: MP3
|
||||
47: Audio/Lossless # Musiques: FLAC
|
||||
48: Audio # Musiques: WMA
|
||||
49: Audio # Musiques: Autres
|
||||
14: TV/SD # Series: HDTV
|
||||
15: TV # Series: TV VOSTFR
|
||||
16: TV # Series: TV VOSTA
|
||||
17: TV # Series: TV PACK
|
||||
18: TV/HD # Series: HDTV 1080p
|
||||
57: TV/HD # Series: TVHD VOSTFR
|
||||
20: TV # Series: TV VO
|
||||
19: TV/HD # Series: HDTV 720p
|
||||
64: TV/SD # Series: WEBRip
|
||||
60: TV/HD # Series: WEBRip 720p
|
||||
61: TV/HD # Series: WEBRip 1080p
|
||||
21: TV/SD # Series: DVD-R TV
|
||||
26: TV # Television: Television
|
||||
24: TV # Television: Spectacles
|
||||
23: TV/Sport # Television: Sports
|
||||
25: TV/Anime # Television: Animes
|
||||
22: TV/Documentary # Television: Documentaires
|
||||
51: XXX # XXX: Tous
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: account-login.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
remember: "yes"
|
||||
returnto: "/"
|
||||
error:
|
||||
- selector: div#nfobar > p#msgError
|
||||
test:
|
||||
path: torrents-search.php
|
||||
|
||||
ratio:
|
||||
path: torrents-search.php
|
||||
selector: div#infobar0 > ul > li:nth-child(1) > font:last-child
|
||||
|
||||
search:
|
||||
path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
|
||||
rows:
|
||||
selector: table.ttable_headinner > tbody > tr.t-row
|
||||
after: 1
|
||||
fields:
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
title:
|
||||
selector: a[href^="torrents-details.php?id="]
|
||||
attribute: title
|
||||
category:
|
||||
selector: a[href^="torrents.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href^="comments.php"]
|
||||
attribute: href
|
||||
size:
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: td:nth-child(8)
|
||||
seeders:
|
||||
selector: td:nth-child(9)
|
||||
leechers:
|
||||
selector: td:nth-child(10)
|
||||
date:
|
||||
selector: small > i
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["Date: ", ""]
|
||||
- name: replace
|
||||
args: ["le ", ""]
|
||||
- name: dateparse
|
||||
args: "15:04:05 02-01-2006"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="images/free.gif"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
129
src/Jackett/Definitions/uhdbits.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
site: uhdbits
|
||||
name: UHDBits
|
||||
description: "A vietnamese general tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://uhdbits.org/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Movies
|
||||
2: Audio
|
||||
3: TV
|
||||
4: Audio
|
||||
5: PC
|
||||
6: Other
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /login.php
|
||||
form: form.auth_form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
keeplogged: "1"
|
||||
error:
|
||||
- selector: .auth_form > .warning
|
||||
message:
|
||||
selector: ".auth_form > .warning"
|
||||
test:
|
||||
path: /top10.php
|
||||
|
||||
ratio:
|
||||
path: /top10.php
|
||||
selector: span.r99
|
||||
|
||||
search:
|
||||
path: /torrents.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}filter_cat[{{.}}]=1&{{end}}"
|
||||
searchstr: "{{ .Query.Keywords }}"
|
||||
tags_type: "1"
|
||||
order_by: "time"
|
||||
order_way: "desc"
|
||||
action: "advanced"
|
||||
searchsubmit: "1"
|
||||
|
||||
rows:
|
||||
selector: table#torrent_table > tbody > tr.torrent
|
||||
fields:
|
||||
download:
|
||||
selector: a[title="Download"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\t", " "]
|
||||
title:
|
||||
selector: div.group_info
|
||||
remove: span, div.tags
|
||||
filters:
|
||||
- name: replace
|
||||
args: [" / Free", ""]
|
||||
- name: replace
|
||||
args: [" / ViE", ""]
|
||||
- name: replace
|
||||
args: [" / User", ""]
|
||||
- name: replace
|
||||
args: [" / Exclusive!", ""]
|
||||
- name: replace
|
||||
args: [" / ↓25%", ""]
|
||||
- name: replace
|
||||
args: [" / ↓50%", ""]
|
||||
- name: replace
|
||||
args: [" / ↓75%", ""]
|
||||
- name: replace
|
||||
args: [" / 2x 50%", ""]
|
||||
- name: replace
|
||||
args: [" / 2x Free", ""]
|
||||
- name: replace
|
||||
args: [" / 2x", ""]
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"strong.blink_me:contains(\"Free\")": "0"
|
||||
"strong.blink_me:contains(\"50\")": "0.5"
|
||||
"strong.blink_me:contains(\"25\")": "0.75"
|
||||
"strong.blink_me:contains(\"75\")": "0.25"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"strong.blink_me:contains(\"2x\")": "2"
|
||||
"*": "1"
|
||||
category:
|
||||
selector: a[href^="torrents.php?filter_cat"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "\\[(\\d+?)\\]"
|
||||
comments:
|
||||
selector: a.torrent_name
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\t", " "]
|
||||
details:
|
||||
selector: a.torrent_name
|
||||
attribute: href
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\t", " "]
|
||||
- name: regexp
|
||||
args: (.*)#torrent\d+$
|
||||
size:
|
||||
selector: td:nth-child(5)
|
||||
grabs:
|
||||
selector: td:nth-child(6)
|
||||
files:
|
||||
selector: td:nth-child(3)
|
||||
seeders:
|
||||
selector: td:nth-child(7)
|
||||
leechers:
|
||||
selector: td:nth-child(8)
|
||||
date:
|
||||
selector: td:nth-child(4)
|
||||
filters:
|
||||
- name: append
|
||||
args: " ago"
|
141
src/Jackett/Definitions/worldofp2p.yml
Normal file
@@ -0,0 +1,141 @@
|
||||
---
|
||||
site: worldofp2p
|
||||
name: WorldOfP2P
|
||||
description: "A general tracker"
|
||||
language: en-us
|
||||
links:
|
||||
- https://worldofp2p.net
|
||||
|
||||
caps:
|
||||
categories:
|
||||
44: TV/Anime # Anime
|
||||
22: PC # Applications
|
||||
43: Audio/Audiobook # Audio Books
|
||||
27: Books # Ebook
|
||||
4: PC/Games # Games
|
||||
40: Other/Misc # Miscellaneous
|
||||
19: Movies # Movies
|
||||
6: Audio # Music
|
||||
31: PC/Phone-Other # Portable
|
||||
7: TV # TV
|
||||
9: TV/Anime # Anime
|
||||
15: PC/0day # Apps-Linux
|
||||
16: PC/Mac # Apps-Macintosh
|
||||
1: PC/0day # Apps-Misc
|
||||
17: PC/Phone-Other # Apps-Mobile
|
||||
49: Audio # Audio Tracks
|
||||
51: Audio/Audiobook # AudioBook
|
||||
50: Books # Ebooks
|
||||
23: Console # Games-Mixed
|
||||
32: Other # Games-Packs
|
||||
2: PC/Games # Games-PC
|
||||
12: PC/Games # Games-PC Rips
|
||||
20: Console/Other # Games-PS1
|
||||
8: Console/Other # Games-PS2
|
||||
21: Console/PS3 # Games-PS3
|
||||
22: Console/PS4 # Games-PS4
|
||||
7: Console/PSP # Games-PSP
|
||||
14: Console/Wii # Games-Wii
|
||||
44: Console/Xbox360 # Games-Xbox 360
|
||||
45: Console/Xbox # Games-Xbox One
|
||||
43: Console/Xbox # Gamex-Xbox
|
||||
30: Movies/HD # Movies-1080p
|
||||
24: Movies/3D # Movies-3D
|
||||
53: Movies/SD # Movies-480p
|
||||
52: Movies/SD # Movies-576p
|
||||
25: Movies/HD # Movies-720p
|
||||
11: Movies/BluRay # Movies-Bluray
|
||||
26: Movies/HD # Movies-BRRip
|
||||
27: Movies/SD # Movies-Camera
|
||||
10: Movies/DVD # Movies-DVDR
|
||||
28: Movies/Other # Movies-Oldies
|
||||
31: Movies/Other # Movies-Packs
|
||||
33: Movies/Other # Movies-Sport
|
||||
29: Movies/WEBDL # Movies-Web/DL
|
||||
3: Movies/SD # Movies-XviD
|
||||
13: Audio/Lossless # Music-Flac
|
||||
4: Audio/MP3 # Music-MP3
|
||||
18: Audio # Music-Packs
|
||||
19: Audio/Video # Music-Videos
|
||||
38: TV/HD # TV-Bluray
|
||||
35: TV/SD # TV-DVDR
|
||||
36: TV/SD # TV-DVDRip
|
||||
37: TV/HD # TV-HD-x264
|
||||
41: TV # TV-Packs
|
||||
39: TV/SD # TV-SD-x264
|
||||
42: TV/WEB-DL # TV-Web/DL
|
||||
5: TV/SD # TV-XviD
|
||||
46: XXX # xXx-HD
|
||||
47: XXX/Imageset # xXx-Images
|
||||
48: XXX/Packs # xXx-Packs
|
||||
6: XXX/XviD # xXx-XviD
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
|
||||
login:
|
||||
path: /takelogin.php
|
||||
method: post
|
||||
form: form
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
login: "Login"
|
||||
error:
|
||||
- selector: td.stdmsg2
|
||||
test:
|
||||
path: /usercp.php?action=default
|
||||
|
||||
ratio:
|
||||
path: /usercp.php?action=default
|
||||
selector: div#sts_tex1 > span.colorblue > span
|
||||
|
||||
search:
|
||||
path: /browse.php
|
||||
inputs:
|
||||
$raw: "{{range .Categories}}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Query.Keywords }}"
|
||||
incldead: "1"
|
||||
searchin: "title"
|
||||
|
||||
rows:
|
||||
selector: div#browse > form > div > table.browsewidth100 > tbody > tr:has(a[href^="download.php?torrent="])
|
||||
fields:
|
||||
title:
|
||||
selector: a[href^="details.php?id="]
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
comments:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?torrent="]
|
||||
attribute: href
|
||||
files:
|
||||
selector: td:nth-child(5)
|
||||
size:
|
||||
selector: td:nth-child(8)
|
||||
seeders:
|
||||
selector: td:nth-child(10)
|
||||
leechers:
|
||||
selector: td:nth-child(11)
|
||||
date:
|
||||
selector: td:nth-child(7)
|
||||
grabs:
|
||||
selector: a[href^="snatches.php?id="]
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "(\\d+)"
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
a.info:contains("Free"): "0"
|
||||
img[src*="/free.png"]: "0"
|
||||
"*": "1"
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
"*": "1"
|
@@ -1,190 +1,191 @@
|
||||
using Autofac;
|
||||
using Jackett.Services;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.LayoutRenderers;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett
|
||||
{
|
||||
public class Engine
|
||||
{
|
||||
private static IContainer container = null;
|
||||
|
||||
static Engine()
|
||||
{
|
||||
BuildContainer();
|
||||
|
||||
}
|
||||
|
||||
public static void BuildContainer()
|
||||
{
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterModule<JackettModule>();
|
||||
container = builder.Build();
|
||||
|
||||
// Register the container in itself to allow for late resolves
|
||||
var secondaryBuilder = new ContainerBuilder();
|
||||
secondaryBuilder.RegisterInstance<IContainer>(container).SingleInstance();
|
||||
SetupLogging(secondaryBuilder);
|
||||
secondaryBuilder.Update(container);
|
||||
|
||||
}
|
||||
|
||||
public static IContainer GetContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
public static bool IsWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
}
|
||||
}
|
||||
|
||||
public static IConfigurationService ConfigService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IConfigurationService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IProcessService ProcessService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IProcessService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServiceConfigService ServiceConfig
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IServiceConfigService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static ITrayLockService LockService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<ITrayLockService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServerService Server
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IServerService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IRunTimeService RunTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IRunTimeService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static Logger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<Logger>();
|
||||
}
|
||||
}
|
||||
|
||||
public static ISecuityService SecurityService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<ISecuityService>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void SetupLogging(ContainerBuilder builder)
|
||||
{
|
||||
var logLevel = Startup.TracingEnabled ? LogLevel.Debug : LogLevel.Info;
|
||||
// Add custom date time format renderer as the default is too long
|
||||
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("simpledatetime", typeof(SimpleDateTimeRenderer));
|
||||
|
||||
var logConfig = new LoggingConfiguration();
|
||||
var logFile = new FileTarget();
|
||||
logConfig.AddTarget("file", logFile);
|
||||
logFile.Layout = "${longdate} ${level} ${message} ${exception:format=ToString}";
|
||||
logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), "log.txt");
|
||||
logFile.ArchiveFileName = "log.{#####}.txt";
|
||||
logFile.ArchiveAboveSize = 500000;
|
||||
logFile.MaxArchiveFiles = 5;
|
||||
logFile.KeepFileOpen = false;
|
||||
logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence;
|
||||
var logFileRule = new LoggingRule("*", logLevel, logFile);
|
||||
logConfig.LoggingRules.Add(logFileRule);
|
||||
|
||||
var logConsole = new ColoredConsoleTarget();
|
||||
logConfig.AddTarget("console", logConsole);
|
||||
|
||||
logConsole.Layout = "${simpledatetime} ${level} ${message} ${exception:format=ToString}";
|
||||
var logConsoleRule = new LoggingRule("*", logLevel, logConsole);
|
||||
logConfig.LoggingRules.Add(logConsoleRule);
|
||||
|
||||
var logService = new LogCacheService();
|
||||
logConfig.AddTarget("service", logService);
|
||||
var serviceRule = new LoggingRule("*", logLevel, logService);
|
||||
logConfig.LoggingRules.Add(serviceRule);
|
||||
|
||||
LogManager.Configuration = logConfig;
|
||||
builder.RegisterInstance<Logger>(LogManager.GetCurrentClassLogger()).SingleInstance();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void SetLogLevel(LogLevel level)
|
||||
{
|
||||
|
||||
foreach (var rule in LogManager.Configuration.LoggingRules)
|
||||
{
|
||||
if (level == LogLevel.Debug)
|
||||
{
|
||||
if (!rule.Levels.Contains(LogLevel.Debug))
|
||||
{
|
||||
rule.EnableLoggingForLevel(LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rule.Levels.Contains(LogLevel.Debug))
|
||||
{
|
||||
rule.DisableLoggingForLevel(LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[LayoutRenderer("simpledatetime")]
|
||||
public class SimpleDateTimeRenderer : LayoutRenderer
|
||||
{
|
||||
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
|
||||
{
|
||||
builder.Append(DateTime.Now.ToString("MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
}
|
||||
using Autofac;
|
||||
using Jackett.Services;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.LayoutRenderers;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett
|
||||
{
|
||||
public class Engine
|
||||
{
|
||||
private static IContainer container = null;
|
||||
|
||||
static Engine()
|
||||
{
|
||||
BuildContainer();
|
||||
|
||||
}
|
||||
|
||||
public static void BuildContainer()
|
||||
{
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterModule<JackettModule>();
|
||||
container = builder.Build();
|
||||
|
||||
// Register the container in itself to allow for late resolves
|
||||
var secondaryBuilder = new ContainerBuilder();
|
||||
secondaryBuilder.RegisterInstance<IContainer>(container).SingleInstance();
|
||||
SetupLogging(secondaryBuilder);
|
||||
secondaryBuilder.Update(container);
|
||||
|
||||
}
|
||||
|
||||
public static IContainer GetContainer()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
public static bool IsWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT;
|
||||
}
|
||||
}
|
||||
|
||||
public static IConfigurationService ConfigService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IConfigurationService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IProcessService ProcessService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IProcessService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServiceConfigService ServiceConfig
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IServiceConfigService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static ITrayLockService LockService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<ITrayLockService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IServerService Server
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IServerService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static IRunTimeService RunTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<IRunTimeService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static Logger Logger
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<Logger>();
|
||||
}
|
||||
}
|
||||
|
||||
public static ISecuityService SecurityService
|
||||
{
|
||||
get
|
||||
{
|
||||
return container.Resolve<ISecuityService>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void SetupLogging(ContainerBuilder builder = null, string logfile = "log.txt")
|
||||
{
|
||||
var logLevel = Startup.TracingEnabled ? LogLevel.Debug : LogLevel.Info;
|
||||
// Add custom date time format renderer as the default is too long
|
||||
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("simpledatetime", typeof(SimpleDateTimeRenderer));
|
||||
|
||||
var logConfig = new LoggingConfiguration();
|
||||
var logFile = new FileTarget();
|
||||
logConfig.AddTarget("file", logFile);
|
||||
logFile.Layout = "${longdate} ${level} ${message} ${exception:format=ToString}";
|
||||
logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), logfile);
|
||||
logFile.ArchiveFileName = "log.{#####}.txt";
|
||||
logFile.ArchiveAboveSize = 500000;
|
||||
logFile.MaxArchiveFiles = 5;
|
||||
logFile.KeepFileOpen = false;
|
||||
logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence;
|
||||
var logFileRule = new LoggingRule("*", logLevel, logFile);
|
||||
logConfig.LoggingRules.Add(logFileRule);
|
||||
|
||||
var logConsole = new ColoredConsoleTarget();
|
||||
logConfig.AddTarget("console", logConsole);
|
||||
|
||||
logConsole.Layout = "${simpledatetime} ${level} ${message} ${exception:format=ToString}";
|
||||
var logConsoleRule = new LoggingRule("*", logLevel, logConsole);
|
||||
logConfig.LoggingRules.Add(logConsoleRule);
|
||||
|
||||
var logService = new LogCacheService();
|
||||
logConfig.AddTarget("service", logService);
|
||||
var serviceRule = new LoggingRule("*", logLevel, logService);
|
||||
logConfig.LoggingRules.Add(serviceRule);
|
||||
|
||||
LogManager.Configuration = logConfig;
|
||||
if (builder != null)
|
||||
{
|
||||
builder.RegisterInstance<Logger>(LogManager.GetCurrentClassLogger()).SingleInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLogLevel(LogLevel level)
|
||||
{
|
||||
|
||||
foreach (var rule in LogManager.Configuration.LoggingRules)
|
||||
{
|
||||
if (level == LogLevel.Debug)
|
||||
{
|
||||
if (!rule.Levels.Contains(LogLevel.Debug))
|
||||
{
|
||||
rule.EnableLoggingForLevel(LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rule.Levels.Contains(LogLevel.Debug))
|
||||
{
|
||||
rule.DisableLoggingForLevel(LogLevel.Debug);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogManager.ReconfigExistingLoggers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[LayoutRenderer("simpledatetime")]
|
||||
public class SimpleDateTimeRenderer : LayoutRenderer
|
||||
{
|
||||
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
|
||||
{
|
||||
builder.Append(DateTime.Now.ToString("MM-dd HH:mm:ss"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
870
src/Jackett/Indexers/Abnormal.cs
Normal file
@@ -0,0 +1,870 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provider for Abnormal Private French Tracker
|
||||
/// </summary>
|
||||
public class Abnormal : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php"; } }
|
||||
private string TorrentCommentUrl { get { return TorrentDescriptionUrl; } }
|
||||
private string TorrentDescriptionUrl { get { return SiteLink + "torrents.php?id="; } }
|
||||
private string TorrentDownloadUrl { get { return SiteLink + "torrents.php?action=download&id={id}&authkey={auth_key}&torrent_pass={torrent_pass}"; } }
|
||||
private bool Latency { get { return ConfigData.Latency.Value; } }
|
||||
private bool DevMode { get { return ConfigData.DevMode.Value; } }
|
||||
private bool CacheMode { get { return ConfigData.HardDriveCache.Value; } }
|
||||
private string directory { get { return System.IO.Path.GetTempPath() + "Jackett\\" + MethodBase.GetCurrentMethod().DeclaringType.Name + "\\"; } }
|
||||
|
||||
private Dictionary<string, string> emulatedBrowserHeaders = new Dictionary<string, string>();
|
||||
private CQ fDom = null;
|
||||
|
||||
private ConfigurationDataAbnormal ConfigData
|
||||
{
|
||||
get { return (ConfigurationDataAbnormal)configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Abnormal(IIndexerManagerService i, IWebClient w, Logger l, IProtectionService ps)
|
||||
: base(
|
||||
name: "Abnormal",
|
||||
description: "General French Private Tracker",
|
||||
link: "https://abnormal.ws/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
downloadBase: "https://abnormal.ws/torrents.php?action=download&id=",
|
||||
configData: new ConfigurationDataAbnormal())
|
||||
{
|
||||
// Clean capabilities
|
||||
TorznabCaps.Categories.Clear();
|
||||
|
||||
// Movies
|
||||
AddCategoryMapping("MOVIE|DVDR", TorznabCatType.MoviesDVD); // DVDR
|
||||
AddCategoryMapping("MOVIE|DVDRIP", TorznabCatType.MoviesSD); // DVDRIP
|
||||
AddCategoryMapping("MOVIE|BDRIP", TorznabCatType.MoviesSD); // BDRIP
|
||||
AddCategoryMapping("MOVIE|VOSTFR", TorznabCatType.MoviesOther); // VOSTFR
|
||||
AddCategoryMapping("MOVIE|HD|720p", TorznabCatType.MoviesHD); // HD 720P
|
||||
AddCategoryMapping("MOVIE|HD|1080p", TorznabCatType.MoviesHD); // HD 1080P
|
||||
AddCategoryMapping("MOVIE|REMUXBR", TorznabCatType.MoviesBluRay); // REMUX BLURAY
|
||||
AddCategoryMapping("MOVIE|FULLBR", TorznabCatType.MoviesBluRay); // FULL BLURAY
|
||||
|
||||
// Series
|
||||
AddCategoryMapping("TV|SD|VOSTFR", TorznabCatType.TV); // SD VOSTFR
|
||||
AddCategoryMapping("TV|HD|VOSTFR", TorznabCatType.TVHD); // HD VOSTFR
|
||||
AddCategoryMapping("TV|SD|VF", TorznabCatType.TVSD); // SD VF
|
||||
AddCategoryMapping("TV|HD|VF", TorznabCatType.TVHD); // HD VF
|
||||
AddCategoryMapping("TV|PACK|FR", TorznabCatType.TVOTHER); // PACK FR
|
||||
AddCategoryMapping("TV|PACK|VOSTFR", TorznabCatType.TVOTHER); // PACK VOSTFR
|
||||
AddCategoryMapping("TV|EMISSIONS", TorznabCatType.TVOTHER); // EMISSIONS
|
||||
|
||||
// Anime
|
||||
AddCategoryMapping("ANIME", TorznabCatType.TVAnime); // ANIME
|
||||
|
||||
// Documentaries
|
||||
AddCategoryMapping("DOCS", TorznabCatType.TVDocumentary); // DOCS
|
||||
|
||||
// Music
|
||||
AddCategoryMapping("MUSIC|FLAC", TorznabCatType.AudioLossless); // FLAC
|
||||
AddCategoryMapping("MUSIC|MP3", TorznabCatType.AudioMP3); // MP3
|
||||
AddCategoryMapping("MUSIC|CONCERT", TorznabCatType.AudioVideo); // CONCERT
|
||||
|
||||
// Other
|
||||
AddCategoryMapping("PC|APP", TorznabCatType.PC); // PC
|
||||
AddCategoryMapping("PC|GAMES", TorznabCatType.PCGames); // GAMES
|
||||
AddCategoryMapping("EBOOKS", TorznabCatType.BooksEbook); // EBOOKS
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure our WiHD Provider
|
||||
/// </summary>
|
||||
/// <param name="configJson">Our params in Json</param>
|
||||
/// <returns>Configuration state</returns>
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
// Retrieve config values set by Jackett's user
|
||||
ConfigData.LoadValuesFromJson(configJson);
|
||||
|
||||
// Check & Validate Config
|
||||
validateConfig();
|
||||
|
||||
// Setting our data for a better emulated browser (maximum security)
|
||||
// TODO: Encoded Content not supported by Jackett at this time
|
||||
// emulatedBrowserHeaders.Add("Accept-Encoding", "gzip, deflate");
|
||||
|
||||
// If we want to simulate a browser
|
||||
if (ConfigData.Browser.Value) {
|
||||
|
||||
// Clean headers
|
||||
emulatedBrowserHeaders.Clear();
|
||||
|
||||
// Inject headers
|
||||
emulatedBrowserHeaders.Add("Accept", ConfigData.HeaderAccept.Value);
|
||||
emulatedBrowserHeaders.Add("Accept-Language", ConfigData.HeaderAcceptLang.Value);
|
||||
emulatedBrowserHeaders.Add("DNT", Convert.ToInt32(ConfigData.HeaderDNT.Value).ToString());
|
||||
emulatedBrowserHeaders.Add("Upgrade-Insecure-Requests", Convert.ToInt32(ConfigData.HeaderUpgradeInsecure.Value).ToString());
|
||||
emulatedBrowserHeaders.Add("User-Agent", ConfigData.HeaderUserAgent.Value);
|
||||
}
|
||||
|
||||
|
||||
// Getting login form to retrieve CSRF token
|
||||
var myRequest = new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl
|
||||
};
|
||||
|
||||
// Add our headers to request
|
||||
myRequest.Headers = emulatedBrowserHeaders;
|
||||
|
||||
// Building login form data
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", ConfigData.Username.Value },
|
||||
{ "password", ConfigData.Password.Value },
|
||||
{ "keeplogged", "1" },
|
||||
{ "login", "Connexion" }
|
||||
};
|
||||
|
||||
// Do the login
|
||||
var request = new Utils.Clients.WebRequest(){
|
||||
PostData = pairs,
|
||||
Referer = LoginUrl,
|
||||
Type = RequestType.POST,
|
||||
Url = LoginUrl,
|
||||
Headers = emulatedBrowserHeaders
|
||||
};
|
||||
|
||||
// Perform loggin
|
||||
latencyNow();
|
||||
output("\nPerform loggin.. with " + LoginUrl);
|
||||
var response = await webclient.GetString(request);
|
||||
|
||||
// Test if we are logged in
|
||||
await ConfigureIfOK(response.Cookies, response.Cookies.Contains("session="), () =>
|
||||
{
|
||||
// Parse error page
|
||||
CQ dom = response.Content;
|
||||
string message = dom[".warning"].Text().Split('.').Reverse().Skip(1).First();
|
||||
|
||||
// Try left
|
||||
string left = dom[".info"].Text().Trim();
|
||||
|
||||
// Oops, unable to login
|
||||
output("-> Login failed: \"" + message + "\" and " + left + " tries left before being banned for 6 hours !", "error");
|
||||
throw new ExceptionWithConfigData("Login failed: " + message, configData);
|
||||
});
|
||||
|
||||
output("-> Login Success");
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute our search query
|
||||
/// </summary>
|
||||
/// <param name="query">Query</param>
|
||||
/// <returns>Releases</returns>
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var torrentRowList = new List<CQ>();
|
||||
var searchTerm = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
int nbResults = 0;
|
||||
int pageLinkCount = 0;
|
||||
|
||||
// Check cache first so we don't query the server (if search term used or not in dev mode)
|
||||
if(!DevMode && !string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
lock (cache)
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
|
||||
// Search in cache
|
||||
var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault();
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Build our query
|
||||
var request = buildQuery(searchTerm, query, searchUrl);
|
||||
|
||||
// Getting results & Store content
|
||||
WebClientStringResult results = await queryExec(request);
|
||||
fDom = results.Content;
|
||||
|
||||
try
|
||||
{
|
||||
// Find torrent rows
|
||||
var firstPageRows = findTorrentRows();
|
||||
|
||||
// Add them to torrents list
|
||||
torrentRowList.AddRange(firstPageRows.Select(fRow => fRow.Cq()));
|
||||
|
||||
// Check if there are pagination links at bottom
|
||||
Boolean pagination = (fDom[".linkbox > a"].Length != 0);
|
||||
|
||||
// If pagination available
|
||||
if (pagination) {
|
||||
// Calculate numbers of pages available for this search query (Based on number results and number of torrents on first page)
|
||||
pageLinkCount = ParseUtil.CoerceInt(Regex.Match(fDom[".linkbox > a"].Last().Attr("href").ToString(), @"\d+").Value);
|
||||
|
||||
// Calculate average number of results (based on torrents rows lenght on first page)
|
||||
nbResults = firstPageRows.Count() * pageLinkCount;
|
||||
}
|
||||
else {
|
||||
// Check if we have a minimum of one result
|
||||
if (firstPageRows.Length >= 1)
|
||||
{
|
||||
// Retrieve total count on our alone page
|
||||
nbResults = firstPageRows.Count();
|
||||
pageLinkCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
output("\nNo result found for your query, please try another search term ...\n", "info");
|
||||
// No result found for this query
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
|
||||
output("\nThere are " + firstPageRows.Length + " results on the first page !");
|
||||
|
||||
// If we have a term used for search and pagination result superior to one
|
||||
if (!string.IsNullOrWhiteSpace(query.GetQueryString()) && pageLinkCount > 1)
|
||||
{
|
||||
// Starting with page #2
|
||||
for (int i = 2; i <= Math.Min(Int32.Parse(ConfigData.Pages.Value), pageLinkCount); i++)
|
||||
{
|
||||
output("\nProcessing page #" + i);
|
||||
|
||||
// Request our page
|
||||
latencyNow();
|
||||
|
||||
// Build our query
|
||||
var pageRequest = buildQuery(searchTerm, query, searchUrl, i);
|
||||
|
||||
// Getting results & Store content
|
||||
WebClientStringResult pageResults = await queryExec(pageRequest);
|
||||
|
||||
// Assign response
|
||||
fDom = pageResults.Content;
|
||||
|
||||
// Process page results
|
||||
var additionalPageRows = findTorrentRows();
|
||||
|
||||
// Add them to torrents list
|
||||
torrentRowList.AddRange(additionalPageRows.Select(fRow => fRow.Cq()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No search term, maybe testing... so registring autkey and torrentpass for future uses
|
||||
string infosData = firstPageRows.First().Find("td:eq(3) > a").Attr("href");
|
||||
IList<string> infosList = infosData.Split('&').Select(s => s.Trim()).Where(s => s != String.Empty).ToList();
|
||||
IList<string> infosTracker = infosList.Select(s => s.Split(new[] { '=' }, 2)[1].Trim()).ToList();
|
||||
|
||||
output("\nStoring Authkey for future uses...");
|
||||
ConfigData.AuthKey.Value = infosTracker[2];
|
||||
|
||||
output("\nStoring TorrentPass for future uses...");
|
||||
ConfigData.TorrentPass.Value = infosTracker[3];
|
||||
|
||||
}
|
||||
|
||||
// Loop on results
|
||||
foreach (CQ tRow in torrentRowList)
|
||||
{
|
||||
output("\n=>> Torrent #" + (releases.Count + 1));
|
||||
|
||||
// ID
|
||||
int id = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(1) > a").Attr("href").ToString(), @"\d+").Value);
|
||||
output("ID: " + id);
|
||||
|
||||
// Release Name
|
||||
string name = tRow.Find("td:eq(1) > a").Text().ToString();
|
||||
output("Release: " + name);
|
||||
|
||||
// Category
|
||||
string categoryID = tRow.Find("td:eq(0) > a").Attr("href").Replace("torrents.php?cat[]=", String.Empty);
|
||||
output("Category: " + MapTrackerCatToNewznab(categoryID) + " (" + categoryID + ")");
|
||||
|
||||
// Seeders
|
||||
int seeders = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(6)").Text(), @"\d+").Value);
|
||||
output("Seeders: " + seeders);
|
||||
|
||||
// Leechers
|
||||
int leechers = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(7)").Text(), @"\d+").Value);
|
||||
output("Leechers: " + leechers);
|
||||
|
||||
// Completed
|
||||
int completed = ParseUtil.CoerceInt(Regex.Match(tRow.Find("td:eq(5)").Text(), @"\d+").Value);
|
||||
output("Completed: " + completed);
|
||||
|
||||
// Size
|
||||
string sizeStr = tRow.Find("td:eq(4)").Text().Replace("Go", "gb").Replace("Mo", "mb").Replace("Ko", "kb");
|
||||
long size = ReleaseInfo.GetBytes(sizeStr);
|
||||
output("Size: " + sizeStr + " (" + size + " bytes)");
|
||||
|
||||
// Publish DateToString
|
||||
IList<string> clockList = tRow.Find("td:eq(2) > span").Text().Replace("Il y a", "").Split(',').Select(s => s.Trim()).Where(s => s != String.Empty).ToList();
|
||||
var date = agoToDate(clockList);
|
||||
output("Released on: " + date.ToLocalTime());
|
||||
|
||||
// Torrent Details URL
|
||||
Uri detailsLink = new Uri(TorrentDescriptionUrl + id);
|
||||
output("Details: " + detailsLink.AbsoluteUri);
|
||||
|
||||
// Torrent Comments URL
|
||||
Uri commentsLink = new Uri(TorrentCommentUrl + id);
|
||||
output("Comments Link: " + commentsLink.AbsoluteUri);
|
||||
|
||||
// Torrent Download URL
|
||||
Uri downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{auth_key}", ConfigData.AuthKey.Value).Replace("{torrent_pass}", ConfigData.TorrentPass.Value));
|
||||
output("Download Link: " + downloadLink.AbsoluteUri);
|
||||
|
||||
// Building release infos
|
||||
var release = new ReleaseInfo();
|
||||
release.Category = MapTrackerCatToNewznab(categoryID.ToString());
|
||||
release.Title = name;
|
||||
release.Seeders = seeders;
|
||||
release.Peers = seeders + leechers;
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
release.PublishDate = date;
|
||||
release.Size = size;
|
||||
release.Guid = detailsLink;
|
||||
release.Comments = commentsLink;
|
||||
release.Link = downloadLink;
|
||||
releases.Add(release);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError("Error, unable to parse result \n" + ex.StackTrace, ex);
|
||||
}
|
||||
|
||||
// Return found releases
|
||||
return releases;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build query to process
|
||||
/// </summary>
|
||||
/// <param name="term">Term to search</param>
|
||||
/// <param name="query">Torznab Query for categories mapping</param>
|
||||
/// <param name="url">Search url for provider</param>
|
||||
/// <param name="page">Page number to request</param>
|
||||
/// <returns>URL to query for parsing and processing results</returns>
|
||||
private string buildQuery(string term, TorznabQuery query, string url, int page = 0)
|
||||
{
|
||||
var parameters = new NameValueCollection();
|
||||
List<string> categoriesList = MapTorznabCapsToTrackers(query);
|
||||
string categories = null;
|
||||
|
||||
// Check if we are processing a new page
|
||||
if (page > 0)
|
||||
{
|
||||
// Adding page number to query
|
||||
parameters.Add("page", page.ToString());
|
||||
}
|
||||
|
||||
// Loop on Categories needed
|
||||
foreach (string category in categoriesList)
|
||||
{
|
||||
// If last, build !
|
||||
if (categoriesList.Last() == category)
|
||||
{
|
||||
// Adding previous categories to URL with latest category
|
||||
parameters.Add(Uri.EscapeDataString("cat[]"), HttpUtility.UrlEncode(category) + categories);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build categories parameter
|
||||
categories += "&" + Uri.EscapeDataString("cat[]") + "=" + HttpUtility.UrlEncode(category);
|
||||
}
|
||||
}
|
||||
|
||||
// If search term provided
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
// Add search term
|
||||
parameters.Add("search", HttpUtility.UrlEncode(term));
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Add("search", HttpUtility.UrlEncode("%"));
|
||||
// Showing all torrents (just for output function)
|
||||
term = "all";
|
||||
}
|
||||
|
||||
// Building our query -- Cannot use GetQueryString due to UrlEncode (generating wrong cat[] param)
|
||||
url += "?" + string.Join("&", parameters.AllKeys.Select(a => a + "=" + parameters[a]));
|
||||
|
||||
output("\nBuilded query for \"" + term + "\"... " + url);
|
||||
|
||||
// Return our search url
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch Method for Querying
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryExec(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Switch in we are in DEV mode with Hard Drive Cache or not
|
||||
if (DevMode && CacheMode)
|
||||
{
|
||||
// Check Cache before querying and load previous results if available
|
||||
results = await queryCache(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Querying tracker directly
|
||||
results = await queryTracker(request);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Torrents Page from Cache by Query Provided
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryCache(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Create Directory if not exist
|
||||
System.IO.Directory.CreateDirectory(directory);
|
||||
|
||||
// Clean Storage Provider Directory from outdated cached queries
|
||||
cleanCacheStorage();
|
||||
|
||||
// Create fingerprint for request
|
||||
string file = directory + request.GetHashCode() + ".json";
|
||||
|
||||
// Checking modes states
|
||||
if (System.IO.File.Exists(file))
|
||||
{
|
||||
// File exist... loading it right now !
|
||||
output("Loading results from hard drive cache ..." + request.GetHashCode() + ".json");
|
||||
results = JsonConvert.DeserializeObject<WebClientStringResult>(System.IO.File.ReadAllText(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No cached file found, querying tracker directly
|
||||
results = await queryTracker(request);
|
||||
|
||||
// Cached file didn't exist for our query, writing it right now !
|
||||
output("Writing results to hard drive cache ..." + request.GetHashCode() + ".json");
|
||||
System.IO.File.WriteAllText(file, JsonConvert.SerializeObject(results));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Torrents Page from Tracker by Query Provided
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebClientStringResult> queryTracker(string request)
|
||||
{
|
||||
WebClientStringResult results = null;
|
||||
|
||||
// Cache mode not enabled or cached file didn't exist for our query
|
||||
output("\nQuerying tracker for results....");
|
||||
|
||||
// Request our first page
|
||||
latencyNow();
|
||||
results = await RequestStringWithCookiesAndRetry(request, null, null, emulatedBrowserHeaders);
|
||||
|
||||
// Return results from tracker
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Hard Drive Cache Storage
|
||||
/// </summary>
|
||||
/// <param name="force">Force Provider Folder deletion</param>
|
||||
private void cleanCacheStorage(Boolean force = false)
|
||||
{
|
||||
// Check cleaning method
|
||||
if(force)
|
||||
{
|
||||
// Deleting Provider Storage folder and all files recursively
|
||||
output("\nDeleting Provider Storage folder and all files recursively ...");
|
||||
|
||||
// Check if directory exist
|
||||
if(System.IO.Directory.Exists(directory))
|
||||
{
|
||||
// Delete storage directory of provider
|
||||
System.IO.Directory.Delete(directory, true);
|
||||
output("-> Storage folder deleted successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No directory, so nothing to do
|
||||
output("-> No Storage folder found for this provider !");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
// Check if there is file older than ... and delete them
|
||||
output("\nCleaning Provider Storage folder... in progress.");
|
||||
System.IO.Directory.GetFiles(directory)
|
||||
.Select(f => new System.IO.FileInfo(f))
|
||||
.Where(f => f.LastAccessTime < DateTime.Now.AddMilliseconds(-Convert.ToInt32(ConfigData.HardDriveCacheKeepTime.Value)))
|
||||
.ToList()
|
||||
.ForEach(f => {
|
||||
output("Deleting cached file << " + f.Name + " >> ... done.");
|
||||
f.Delete();
|
||||
i++;
|
||||
});
|
||||
|
||||
// Inform on what was cleaned during process
|
||||
if(i > 0) {
|
||||
output("-> Deleted " + i + " cached files during cleaning.");
|
||||
}
|
||||
else {
|
||||
output("-> Nothing deleted during cleaning.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random fake latency to avoid detection on tracker side
|
||||
/// </summary>
|
||||
private void latencyNow()
|
||||
{
|
||||
// Need latency ?
|
||||
if(Latency)
|
||||
{
|
||||
// Generate a random value in our range
|
||||
var random = new Random(DateTime.Now.Millisecond);
|
||||
int waiting = random.Next(Convert.ToInt32(ConfigData.LatencyStart.Value), Convert.ToInt32(ConfigData.LatencyEnd.Value));
|
||||
output("\nLatency Faker => Sleeping for " + waiting + " ms...");
|
||||
|
||||
// Sleep now...
|
||||
System.Threading.Thread.Sleep(waiting);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find torrent rows in search pages
|
||||
/// </summary>
|
||||
/// <returns>JQuery Object</returns>
|
||||
private CQ findTorrentRows()
|
||||
{
|
||||
// Return all occurencis of torrents found
|
||||
return fDom[".torrent_table > tbody > tr"].Not(".colhead");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Ago date to DateTime
|
||||
/// </summary>
|
||||
/// <param name="clockList"></param>
|
||||
/// <returns>A DateTime</returns>
|
||||
private DateTime agoToDate(IList<string> clockList)
|
||||
{
|
||||
DateTime release = DateTime.Now;
|
||||
foreach (var ago in clockList)
|
||||
{
|
||||
// Check for years
|
||||
if (ago.Contains("années") || ago.Contains("année"))
|
||||
{
|
||||
// Number of years to remove
|
||||
int years = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddYears(-years);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for months
|
||||
else if (ago.Contains("mois"))
|
||||
{
|
||||
// Number of months to remove
|
||||
int months = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddMonths(-months);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for weeks
|
||||
else if (ago.Contains("semaines") || ago.Contains("semaine"))
|
||||
{
|
||||
// Number of weeks to remove
|
||||
int weeks = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddDays(-(7 * weeks));
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for days
|
||||
else if (ago.Contains("jours") || ago.Contains("jour"))
|
||||
{
|
||||
// Number of days to remove
|
||||
int days = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddDays(-days);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for hours
|
||||
else if (ago.Contains("heures") || ago.Contains("heure"))
|
||||
{
|
||||
// Number of hours to remove
|
||||
int hours = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddHours(-hours);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for minutes
|
||||
else if (ago.Contains("mins") || ago.Contains("min"))
|
||||
{
|
||||
// Number of minutes to remove
|
||||
int minutes = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddMinutes(-minutes);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Check for seconds
|
||||
else if (ago.Contains("secondes") || ago.Contains("seconde"))
|
||||
{
|
||||
// Number of seconds to remove
|
||||
int seconds = ParseUtil.CoerceInt(Regex.Match(ago.ToString(), @"\d+").Value);
|
||||
// Removing
|
||||
release = release.AddSeconds(-seconds);
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Unable to detect release date of torrent", "error");
|
||||
//throw new Exception("Unable to detect release date of torrent");
|
||||
}
|
||||
}
|
||||
return release;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output message for logging or developpment (console)
|
||||
/// </summary>
|
||||
/// <param name="message">Message to output</param>
|
||||
/// <param name="level">Level for Logger</param>
|
||||
private void output(string message, string level = "debug")
|
||||
{
|
||||
// Check if we are in dev mode
|
||||
if(DevMode)
|
||||
{
|
||||
// Output message to console
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send message to logger with level
|
||||
switch (level)
|
||||
{
|
||||
default:
|
||||
goto case "debug";
|
||||
case "debug":
|
||||
// Only if Debug Level Enabled on Jackett
|
||||
if (Engine.Logger.IsDebugEnabled)
|
||||
{
|
||||
logger.Debug(message);
|
||||
}
|
||||
break;
|
||||
case "info":
|
||||
logger.Info(message);
|
||||
break;
|
||||
case "error":
|
||||
logger.Error(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate Config entered by user on Jackett
|
||||
/// </summary>
|
||||
private void validateConfig()
|
||||
{
|
||||
output("\nValidating Settings ... \n");
|
||||
|
||||
// Check Username Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Username.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide a username for this tracker to login !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Username (auth) => " + ConfigData.Username.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Password Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Password.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide a password with your username for this tracker to login !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Password (auth) => " + ConfigData.Password.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Max Page Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.Pages.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric maximum number of pages to crawl !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a maximum number of pages to crawl !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Latency Setting
|
||||
if (ConfigData.Latency.Value)
|
||||
{
|
||||
output("\nValidated Setting -- Latency Simulation enabled");
|
||||
|
||||
// Check Latency Start Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.LatencyStart.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Latency Start => " + Convert.ToInt32(ConfigData.LatencyStart.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric latency start in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Latency Simulation enabled, Please enter a start latency !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Latency End Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.LatencyEnd.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Latency End => " + Convert.ToInt32(ConfigData.LatencyEnd.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric latency end in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Latency Simulation enabled, Please enter a end latency !", ConfigData);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Browser Setting
|
||||
if (ConfigData.Browser.Value)
|
||||
{
|
||||
output("\nValidated Setting -- Browser Simulation enabled");
|
||||
|
||||
// Check ACCEPT header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderAccept.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an ACCEPT header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- ACCEPT (header) => " + ConfigData.HeaderAccept.Value.ToString());
|
||||
}
|
||||
|
||||
// Check ACCEPT-LANG header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderAcceptLang.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an ACCEPT-LANG header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- ACCEPT-LANG (header) => " + ConfigData.HeaderAcceptLang.Value.ToString());
|
||||
}
|
||||
|
||||
// Check USER-AGENT header Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.HeaderUserAgent.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Browser Simulation enabled, Please enter an USER-AGENT header !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- USER-AGENT (header) => " + ConfigData.HeaderUserAgent.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Check Dev Cache Settings
|
||||
if (ConfigData.HardDriveCache.Value == true)
|
||||
{
|
||||
output("\nValidated Setting -- DEV Hard Drive Cache enabled");
|
||||
|
||||
// Check if Dev Mode enabled !
|
||||
if (!ConfigData.DevMode.Value)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Hard Drive is enabled but not in DEV MODE, Please enable DEV MODE !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Cache Keep Time Setting
|
||||
if (!string.IsNullOrEmpty(ConfigData.HardDriveCacheKeepTime.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Cache Keep Time (ms) => " + Convert.ToInt32(ConfigData.HardDriveCacheKeepTime.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new ExceptionWithConfigData("Please enter a numeric hard drive keep time in ms !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExceptionWithConfigData("Hard Drive Cache enabled, Please enter a maximum keep time for cache !", ConfigData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delete cache if previously existed
|
||||
cleanCacheStorage(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -52,13 +52,13 @@ namespace Jackett.Indexers
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
var token = new Regex("Avz.CSRF_TOKEN = '(.*?)';").Match(loginPage.Content).Groups[1].ToString();
|
||||
var token = new Regex("<meta name=\"_token\" content=\"(.*?)\">").Match(loginPage.Content).Groups[1].ToString();
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "_token", token },
|
||||
{ "username_email", configData.Username.Value },
|
||||
{ "email_username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "remember", "on" }
|
||||
};
|
||||
{ "remember", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, null, LoginUrl);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("auth/logout"), () =>
|
||||
@@ -91,7 +91,7 @@ namespace Jackett.Indexers
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom["table > tbody > tr"];
|
||||
var rows = dom["table:has(thead) > tbody > tr"];
|
||||
foreach (var row in rows)
|
||||
{
|
||||
CQ qRow = row.Cq();
|
||||
@@ -100,29 +100,47 @@ namespace Jackett.Indexers
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 172800;
|
||||
|
||||
var qLink = row.ChildElements.ElementAt(1).FirstElementChild.Cq();
|
||||
var qLink = qRow.Find("a.torrent-filename"); ;
|
||||
release.Title = qLink.Text().Trim();
|
||||
release.Comments = new Uri(qLink.Attr("href"));
|
||||
release.Guid = release.Comments;
|
||||
|
||||
var qDownload = row.ChildElements.ElementAt(3).FirstElementChild.Cq();
|
||||
var qDownload = qRow.Find("a.torrent-download-icon"); ;
|
||||
release.Link = new Uri(qDownload.Attr("href"));
|
||||
|
||||
var dateStr = row.ChildElements.ElementAt(5).Cq().Text().Trim();
|
||||
var dateStr = qRow.Find("td:eq(3) > span").Text().Trim();
|
||||
release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr);
|
||||
|
||||
var sizeStr = row.ChildElements.ElementAt(6).Cq().Text();
|
||||
var sizeStr = qRow.Find("td:eq(5) > span").Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text()) + release.Seeders;
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("td:eq(6)").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("td:eq(7)").Text().Trim()) + release.Seeders;
|
||||
|
||||
var cat = row.Cq().Find("td:eq(0) i").First().Attr("class")
|
||||
.Replace("gi gi-film", "1")
|
||||
.Replace("gi gi-tv", "2")
|
||||
.Replace("gi gi-music", "3")
|
||||
.Replace("torrent-icon", string.Empty)
|
||||
.Replace("fa fa-", string.Empty)
|
||||
.Replace("film", "1")
|
||||
.Replace("tv", "2")
|
||||
.Replace("music", "3")
|
||||
.Replace("text-pink", string.Empty);
|
||||
release.Category = MapTrackerCatToNewznab(cat.Trim());
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(9)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (row.Cq().Find("i.fa-star").Any())
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (row.Cq().Find("i.fa-star-half-o").Any())
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
if (row.Cq().Find("i.fa-diamond").Any())
|
||||
release.UploadVolumeFactor = 2;
|
||||
else
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
||||
: base(name: "AlphaRatio",
|
||||
description: "Legendary",
|
||||
link: "https://alpharatio.cc/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
@@ -56,13 +56,18 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(23, TorznabCatType.Audio);
|
||||
}
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
var incomingConfig = new ConfigurationDataBasicLogin();
|
||||
incomingConfig.LoadValuesFromJson(configJson);
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", incomingConfig.Username.Value },
|
||||
{ "password", incomingConfig.Password.Value },
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "login", "Login" },
|
||||
{ "keeplogged", "1" }
|
||||
};
|
||||
@@ -74,7 +79,7 @@ namespace Jackett.Indexers
|
||||
CQ dom = response.Content;
|
||||
dom["#loginform > table"].Remove();
|
||||
var errorMessage = dom["#loginform"].Text().Trim().Replace("\n\t", " ");
|
||||
throw new ExceptionWithConfigData(errorMessage, (ConfigurationData)incomingConfig);
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -88,6 +93,25 @@ namespace Jackett.Indexers
|
||||
release.Guid = new Uri(GuidUrl + id);
|
||||
release.Comments = release.Guid;
|
||||
release.Link = new Uri(DownloadUrl + id);
|
||||
release.Category = MapTrackerCatToNewznab(CategoryReverseMapper((string)r["category"]));
|
||||
release.Files = (int)r["fileCount"];
|
||||
release.Grabs = (int)r["snatches"];
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
if ((bool)r["isFreeleech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
if ((bool)r["isPersonalFreeleech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
}
|
||||
if ((bool)r["isNeutralLeech"])
|
||||
{
|
||||
release.DownloadVolumeFactor = 0;
|
||||
release.UploadVolumeFactor = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
@@ -159,7 +183,23 @@ namespace Jackett.Indexers
|
||||
{
|
||||
DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
|
||||
long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks, DateTimeKind.Utc).ToLocalTime();
|
||||
}
|
||||
|
||||
static string CategoryReverseMapper(string categoryName)
|
||||
{
|
||||
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
||||
|
||||
dictionary.Add("TvSD", "1");
|
||||
dictionary.Add("TvHD", "2");
|
||||
dictionary.Add("MovieSD", "6");
|
||||
dictionary.Add("MovieHD", "7");
|
||||
|
||||
if (dictionary.ContainsKey(categoryName))
|
||||
{
|
||||
return dictionary[categoryName];
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
205
src/Jackett/Indexers/Andraste.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Andraste : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Andraste(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Andraste",
|
||||
description: "A German general tracker.",
|
||||
link: "https://andraste.io/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
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 async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.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;
|
||||
}
|
||||
|
||||
public 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 RequestBytesWithCookies(searchUrl);
|
||||
var results = Encoding.GetEncoding("iso-8859-1").GetString(response.Content);
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,9 +24,17 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public class AnimeBytes : BaseIndexer, IIndexer
|
||||
{
|
||||
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?"; } }
|
||||
public bool AllowRaws { get { return configData.IncludeRaw.Value; } }
|
||||
public bool InsertSeason { get { return configData.InsertSeason!=null && configData.InsertSeason.Value; } }
|
||||
|
||||
new ConfigurationDataAnimeBytes configData
|
||||
{
|
||||
@@ -45,7 +53,10 @@ namespace Jackett.Indexers
|
||||
TorznabCatType.BooksComics,
|
||||
TorznabCatType.ConsolePSP,
|
||||
TorznabCatType.ConsoleOther,
|
||||
TorznabCatType.PCGames),
|
||||
TorznabCatType.PCGames,
|
||||
TorznabCatType.AudioMP3,
|
||||
TorznabCatType.AudioLossless,
|
||||
TorznabCatType.AudioOther),
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataAnimeBytes())
|
||||
@@ -53,6 +64,13 @@ namespace Jackett.Indexers
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override IEnumerable<ReleaseInfo> FilterResults(TorznabQuery query, IEnumerable<ReleaseInfo> input)
|
||||
{
|
||||
// Prevent filtering
|
||||
return input;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
@@ -119,12 +137,28 @@ namespace Jackett.Indexers
|
||||
base.LoadFromSavedConfiguration(jsonConfig);
|
||||
}
|
||||
|
||||
private string StripEpisodeNumber(string term)
|
||||
{
|
||||
// Tracer does not support searching with episode number so strip it if we have one
|
||||
term = Regex.Replace(term, @"\W(\dx)?\d?\d$", string.Empty);
|
||||
term = Regex.Replace(term, @"\W(S\d\d?E)?\d?\d$", string.Empty);
|
||||
return term;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
// The result list
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
if (ContainsMusicCategories(query.Categories))
|
||||
{
|
||||
foreach (var result in await GetResults(SearchType.Audio, query.SanitizedSearchTerm))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var result in await GetResults(query.SanitizedSearchTerm))
|
||||
foreach (var result in await GetResults(SearchType.Video, StripEpisodeNumber(query.SanitizedSearchTerm)))
|
||||
{
|
||||
releases.Add(result);
|
||||
}
|
||||
@@ -132,39 +166,45 @@ namespace Jackett.Indexers
|
||||
return releases.ToArray();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> GetResults(string searchTerm)
|
||||
private bool ContainsMusicCategories(int[] categories)
|
||||
{
|
||||
var music = new[]
|
||||
{
|
||||
TorznabCatType.Audio.ID,
|
||||
TorznabCatType.AudioMP3.ID,
|
||||
TorznabCatType.AudioLossless.ID,
|
||||
TorznabCatType.AudioOther.ID,
|
||||
TorznabCatType.AudioForeign.ID
|
||||
};
|
||||
|
||||
return categories.Length == 0 || music.Any(categories.Contains);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ReleaseInfo>> GetResults(SearchType searchType, string searchTerm)
|
||||
{
|
||||
var cleanSearchTerm = HttpUtility.UrlEncode(searchTerm);
|
||||
|
||||
// This tracker only deals with full seasons so chop off the episode/season number if we have it D:
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
var splitindex = searchTerm.LastIndexOf(' ');
|
||||
if (splitindex > -1)
|
||||
searchTerm = searchTerm.Substring(0, splitindex);
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Check cache first so we don't query the server for each episode when searching for each episode in a series.
|
||||
lock (cache)
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
|
||||
var cachedResult = cache.Where(i => i.Query == searchTerm).FirstOrDefault();
|
||||
var cachedResult = cache.Where(i => i.Query == queryUrl).FirstOrDefault();
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
|
||||
var queryUrl = SearchUrl;
|
||||
// Only include the query bit if its required as hopefully the site caches the non query page
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// Get the content from the tracker
|
||||
var response = await RequestStringWithCookiesAndRetry(queryUrl);
|
||||
CQ dom = response.Content;
|
||||
@@ -183,7 +223,11 @@ namespace Jackett.Indexers
|
||||
var seriesCq = series.Cq();
|
||||
|
||||
var synonyms = new List<string>();
|
||||
var mainTitle = seriesCq.Find(".group_title strong a").First().Text().Trim();
|
||||
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("[");
|
||||
@@ -244,6 +288,12 @@ namespace Jackett.Indexers
|
||||
releaseInfo = releaseInfo.Replace("Episode ", "");
|
||||
releaseInfo = releaseInfo.Replace("Season ", "S");
|
||||
releaseInfo = releaseInfo.Trim();
|
||||
int test = 0;
|
||||
if (InsertSeason && int.TryParse(releaseInfo, out test) && releaseInfo.Length==1)
|
||||
{
|
||||
releaseInfo = "S01E0" + releaseInfo;
|
||||
}
|
||||
|
||||
}
|
||||
else if (rowCq.HasClass("torrent"))
|
||||
{
|
||||
@@ -268,33 +318,52 @@ namespace Jackett.Indexers
|
||||
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"), UriKind.Relative);
|
||||
|
||||
var category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim();
|
||||
if (category == "TV Series")
|
||||
release.Category = 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 = TorznabCatType.Movies.ID;
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Game" || category == "Visual Novel")
|
||||
string category = null;
|
||||
if (searchType == SearchType.Video)
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" PSP "))
|
||||
release.Category = TorznabCatType.ConsolePSP.ID;
|
||||
if (description.Contains("PSX"))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" NES "))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" PC "))
|
||||
release.Category = TorznabCatType.PCGames.ID;
|
||||
category = seriesCq.Find("a[title=\"View Torrent\"]").Text().Trim();
|
||||
if (category == "TV Series")
|
||||
release.Category = 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 = TorznabCatType.Movies.ID;
|
||||
|
||||
if (category == "Manga" || category == "Oneshot" || category == "Anthology" || category == "Manhwa" || category == "Manhua" || category == "Light Novel")
|
||||
release.Category = TorznabCatType.BooksComics.ID;
|
||||
|
||||
if (category == "Novel" || category == "Artbook")
|
||||
release.Category = 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 = TorznabCatType.ConsolePSP.ID;
|
||||
if (description.Contains("PSX"))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" NES "))
|
||||
release.Category = TorznabCatType.ConsoleOther.ID;
|
||||
if (description.Contains(" PC "))
|
||||
release.Category = TorznabCatType.PCGames.ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (searchType == SearchType.Audio)
|
||||
{
|
||||
category = seriesCq.Find(".group_img .cat a").Text();
|
||||
if (category == "Single" || category == "Album" || category == "Compilation" || category == "Soundtrack" || category == "Remix CD")
|
||||
{
|
||||
var description = rowCq.Find(".torrent_properties a:eq(1)").Text();
|
||||
if (description.Contains(" Lossless "))
|
||||
release.Category = TorznabCatType.AudioLossless.ID;
|
||||
else if (description.Contains("MP3"))
|
||||
release.Category = TorznabCatType.AudioMP3.ID;
|
||||
else
|
||||
release.Category = TorznabCatType.AudioOther.ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -357,6 +426,17 @@ namespace Jackett.Indexers
|
||||
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 != 0)
|
||||
releases.Add(release);
|
||||
}
|
||||
@@ -372,7 +452,7 @@ namespace Jackett.Indexers
|
||||
// Add to the cache
|
||||
lock (cache)
|
||||
{
|
||||
cache.Add(new CachedQueryResult(searchTerm, releases));
|
||||
cache.Add(new CachedQueryResult(queryUrl, releases));
|
||||
}
|
||||
|
||||
return releases.Select(s => (ReleaseInfo)s.Clone());
|
||||
|
204
src/Jackett/Indexers/AnimeTorrents.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class AnimeTorrents : BaseIndexer, IIndexer
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "ajax/torrents_data.php"; } }
|
||||
private string SearchUrlReferer { get { return SiteLink + "torrents.php?cat=0&searchin=filename&search="; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public AnimeTorrents(IIndexerManagerService i, HttpWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "AnimeTorrents",
|
||||
description: "Definitive source for anime and manga",
|
||||
link: "http://animetorrents.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: c, // Forced HTTP client for custom headers
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesSD); // Anime Movie
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesHD); // Anime Movie HD
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // Anime Series
|
||||
AddCategoryMapping(7, TorznabCatType.TVAnime); // Anime Series HD
|
||||
AddCategoryMapping(5, TorznabCatType.XXXDVD); // Hentai (censored)
|
||||
AddCategoryMapping(9, TorznabCatType.XXXDVD); // Hentai (censored) HD
|
||||
AddCategoryMapping(4, TorznabCatType.XXXDVD); // Hentai (un-censored)
|
||||
AddCategoryMapping(8, TorznabCatType.XXXDVD); // Hentai (un-censored) HD
|
||||
AddCategoryMapping(13, TorznabCatType.BooksForeign); // Light Novel
|
||||
AddCategoryMapping(3, TorznabCatType.BooksComics); // Manga
|
||||
AddCategoryMapping(10, TorznabCatType.BooksComics); // Manga 18+
|
||||
AddCategoryMapping(11, TorznabCatType.TVAnime); // OVA
|
||||
AddCategoryMapping(12, TorznabCatType.TVAnime); // OVA HD
|
||||
AddCategoryMapping(14, TorznabCatType.BooksComics); // Doujin Anime
|
||||
AddCategoryMapping(15, TorznabCatType.XXXDVD); // Doujin Anime 18+
|
||||
AddCategoryMapping(16, TorznabCatType.AudioForeign); // Doujin Music
|
||||
AddCategoryMapping(17, TorznabCatType.BooksComics); // Doujinshi
|
||||
AddCategoryMapping(18, TorznabCatType.BooksComics); // Doujinshi 18+
|
||||
AddCategoryMapping(19, TorznabCatType.Audio); // OST
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "form", "login" },
|
||||
{ "rememberme[]", "1" }
|
||||
};
|
||||
|
||||
var loginPage = await RequestStringWithCookiesAndRetry(LoginUrl, null, null);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom[".ui-state-error"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
queryCollection.Add("total", "146"); // Not sure what this is about but its required!
|
||||
|
||||
var cat = "0";
|
||||
var queryCats = MapTorznabCapsToTrackers(query);
|
||||
if (queryCats.Count == 1)
|
||||
{
|
||||
cat = queryCats.First().ToString();
|
||||
}
|
||||
|
||||
queryCollection.Add("cat", cat);
|
||||
queryCollection.Add("searchin", "filename");
|
||||
queryCollection.Add("search", searchString);
|
||||
queryCollection.Add("page", "1");
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var extraHeaders = new Dictionary<string, string>()
|
||||
{
|
||||
{ "X-Requested-With", "XMLHttpRequest" }
|
||||
};
|
||||
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, SearchUrlReferer, extraHeaders);
|
||||
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
var rows = dom["tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("td:eq(1) a:eq(0)").First();
|
||||
release.Title = qTitleLink.Find("strong").Text().Trim();
|
||||
|
||||
// If we search an get no results, we still get a table just with no info.
|
||||
if (string.IsNullOrWhiteSpace(release.Title))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
var dateString = qRow.Find("td:eq(4)").Text();
|
||||
release.PublishDate = DateTime.ParseExact(dateString, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
|
||||
var qLink = qRow.Find("td:eq(2) a");
|
||||
if (qLink.Length != 0) // newbie users don't see DL links
|
||||
{
|
||||
release.Link = new Uri(qLink.Attr("href"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// use comments link as placeholder
|
||||
// null causes errors during export to torznab
|
||||
// skipping the release prevents newbie users from adding the tracker (empty result)
|
||||
release.Link = release.Comments;
|
||||
}
|
||||
|
||||
var sizeStr = qRow.Find("td:eq(5)").Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
var connections = qRow.Find("td:eq(7)").Text().Trim().Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(connections[0].Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(connections[1].Trim()) + release.Seeders;
|
||||
release.Grabs = ParseUtil.CoerceLong(connections[2].Trim());
|
||||
|
||||
var rCat = row.Cq().Find("td:eq(0) a").First().Attr("href");
|
||||
var rCatIdx = rCat.IndexOf("cat=");
|
||||
if (rCatIdx > -1)
|
||||
{
|
||||
rCat = rCat.Substring(rCatIdx + 4);
|
||||
}
|
||||
|
||||
release.Category = MapTrackerCatToNewznab(rCat);
|
||||
|
||||
if (qRow.Find("img[alt=\"Gold Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else if (qRow.Find("img[alt=\"Silver Torrent\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0.5;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
var ULFactorImg = qRow.Find("img[alt*=\"x Multiplier Torrent\"]");
|
||||
if (ULFactorImg.Length >= 1)
|
||||
{
|
||||
release.UploadVolumeFactor = ParseUtil.CoerceDouble(ULFactorImg.Attr("alt").Split('x')[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
release.UploadVolumeFactor = 1;
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -48,7 +48,7 @@ namespace Jackett.Indexers
|
||||
AddCategoryMapping(2, TorznabCatType.PC);
|
||||
AddCategoryMapping(3, TorznabCatType.BooksEbook);
|
||||
AddCategoryMapping(4, TorznabCatType.AudioAudiobook);
|
||||
AddCategoryMapping(6, TorznabCatType.BooksComics);
|
||||
AddCategoryMapping(7, TorznabCatType.BooksComics);
|
||||
AddCategoryMapping(8, TorznabCatType.TVAnime);
|
||||
AddCategoryMapping(9, TorznabCatType.Movies);
|
||||
AddCategoryMapping(10, TorznabCatType.TVHD);
|
||||
@@ -59,7 +59,8 @@ namespace Jackett.Indexers
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
if (configJson != null)
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
@@ -107,6 +108,13 @@ namespace Jackett.Indexers
|
||||
|
||||
var results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
|
||||
// Occasionally the cookies become invalid, login again if that happens
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestStringWithCookiesAndRetry(searchUrl);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = results.Content;
|
||||
@@ -137,9 +145,21 @@ namespace Jackett.Indexers
|
||||
var sizeStr = row.ChildElements.ElementAt(4).Cq().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Files = ParseUtil.CoerceInt(row.ChildElements.ElementAt(2).Cq().Text().Trim());
|
||||
release.Grabs = ParseUtil.CoerceInt(row.ChildElements.ElementAt(6).Cq().Text().Trim());
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text().Trim()) + release.Seeders;
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("strong:contains(\"Freeleech!\")").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
319
src/Jackett/Indexers/BJShare.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using AngleSharp.Parser.Html;
|
||||
using AngleSharp.Dom;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BJShare : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "torrents.php"; } }
|
||||
string TodayUrl { get { return SiteLink + "torrents.php?action=today"; } }
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BJShare(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BJ-Share",
|
||||
description: "A brazilian tracker.",
|
||||
link: "https://bj-share.me/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
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 async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "keeplogged", "1" }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
var errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
private string StripSearchString(string term)
|
||||
{
|
||||
// Search does not support searching with episode numbers so strip it if we have one
|
||||
// Ww AND filter the result later to archive the proper result
|
||||
term = Regex.Replace(term, @"[S|E]\d\d", string.Empty);
|
||||
return term.Trim();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
|
||||
// if the search string is empty use the "last 24h torrents" view
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
{
|
||||
var results = await RequestStringWithCookies(TodayUrl);
|
||||
try
|
||||
{
|
||||
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)
|
||||
{
|
||||
try
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
release.MinimumSeedTime = 0;
|
||||
|
||||
var qDetailsLink = Row.QuerySelector("a.BJinfoBox");
|
||||
var qTitle = qDetailsLink.QuerySelector("font");
|
||||
release.Title = qTitle.TextContent;
|
||||
|
||||
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 qFreeLeech = Row.QuerySelector("font[color=\"green\"]:contains(Free)");
|
||||
|
||||
release.Description = "";
|
||||
foreach (var Child in qBJinfoBox.ChildNodes)
|
||||
{
|
||||
var type = Child.NodeType;
|
||||
if (type != NodeType.Text)
|
||||
continue;
|
||||
|
||||
var line = Child.TextContent;
|
||||
if (line.StartsWith("Tamanho:"))
|
||||
{
|
||||
string 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();
|
||||
}
|
||||
else
|
||||
{
|
||||
release.Description += line + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var catStr = qCatLink.GetAttribute("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
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.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(string.Format("{0}: Error while parsing row '{1}': {2}", ID, Row.OuterHtml, ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
}
|
||||
else // use search
|
||||
{
|
||||
var searchUrl = BrowseUrl;
|
||||
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("searchstr", StripSearchString(searchString));
|
||||
queryCollection.Add("order_by", "time");
|
||||
queryCollection.Add("order_way", "desc");
|
||||
queryCollection.Add("group_results", "1");
|
||||
queryCollection.Add("action", "basic");
|
||||
queryCollection.Add("searchsubmit", "1");
|
||||
|
||||
foreach (var cat in MapTorznabCapsToTrackers(query))
|
||||
{
|
||||
queryCollection.Add("filter_cat["+cat+"]", "1");
|
||||
}
|
||||
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
|
||||
var results = await RequestStringWithCookies(searchUrl);
|
||||
try
|
||||
{
|
||||
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);
|
||||
|
||||
int GroupCategory = 0;
|
||||
string GroupTitle = null;
|
||||
string GroupYearStr = null;
|
||||
Nullable<DateTime> GroupPublishDate = null;
|
||||
|
||||
foreach (var Row in Rows)
|
||||
{
|
||||
try
|
||||
{
|
||||
var qDetailsLink = Row.QuerySelector("a[href^=\"torrents.php?id=\"]");
|
||||
string Title = qDetailsLink.TextContent;
|
||||
int Category = 0;
|
||||
string YearStr = null;
|
||||
Nullable<DateTime> YearPublishDate = null;
|
||||
|
||||
if (Row.ClassList.Contains("group") || Row.ClassList.Contains("torrent")) // group/ungrouped headers
|
||||
{
|
||||
var qCatLink = Row.QuerySelector("a[href^=\"/torrents.php?filter_cat\"]");
|
||||
string 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);
|
||||
|
||||
if (Row.ClassList.Contains("group")) // group headers
|
||||
{
|
||||
GroupCategory = Category;
|
||||
GroupTitle = Title;
|
||||
GroupYearStr = YearStr;
|
||||
GroupPublishDate = YearPublishDate;
|
||||
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 qSeeders = Row.QuerySelector("td:nth-last-child(3)");
|
||||
var qLeechers = Row.QuerySelector("td:nth-last-child(2)");
|
||||
var qFreeLeech = Row.QuerySelector("strong[title=\"Free\"]");
|
||||
|
||||
if (Row.ClassList.Contains("group_torrent")) // torrents belonging to a group
|
||||
{
|
||||
release.Description = qDetailsLink.TextContent;
|
||||
release.Title = GroupTitle + " " + GroupYearStr;
|
||||
release.PublishDate = GroupPublishDate.Value;
|
||||
release.Category = GroupCategory;
|
||||
}
|
||||
else if (Row.ClassList.Contains("torrent")) // standalone/un grouped torrents
|
||||
{
|
||||
var qDescription = Row.QuerySelector("div.torrent_info");
|
||||
release.Description = qDescription.TextContent;
|
||||
release.Title = Title + " " + YearStr;
|
||||
release.PublishDate = YearPublishDate.Value;
|
||||
release.Category = Category;
|
||||
}
|
||||
|
||||
release.Description = release.Description.Replace(" / Free", ""); // Remove Free Tag
|
||||
release.Title += " " + release.Description; // add year and Description to the release Title to add some meaning to it
|
||||
|
||||
// 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"));
|
||||
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.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(string.Format("{0}: Error while parsing row '{1}': {2}", ID, Row.OuterHtml, ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.Content, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
198
src/Jackett/Indexers/BakaBT.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BakaBT : BaseIndexer, IIndexer
|
||||
{
|
||||
public string SearchUrl { get { return SiteLink + "browse.php?only=0&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&reorder=1&q="; } }
|
||||
public string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
public string id = "bakabt";
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BakaBT",
|
||||
description: "Anime Comunity",
|
||||
link: "https://bakabt.me/",
|
||||
caps: new TorznabCapabilities(TorznabCatType.TVAnime),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var loginForm = await webclient.GetString(new Utils.Clients.WebRequest()
|
||||
{
|
||||
Url = LoginUrl,
|
||||
Type = RequestType.GET
|
||||
});
|
||||
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
{ "returnto", "/index.php" }
|
||||
};
|
||||
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginForm.Cookies, true, null, SiteLink);
|
||||
var responseContent = response.Content;
|
||||
await ConfigureIfOK(response.Cookies, responseContent.Contains("<a href=\"logout.php\">Logout</a>"), () =>
|
||||
{
|
||||
CQ dom = responseContent;
|
||||
var messageEl = dom[".error"].First();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
|
||||
// This tracker only deals with full seasons so chop off the episode/season number if we have it D:
|
||||
if (!string.IsNullOrWhiteSpace(query.SearchTerm))
|
||||
{
|
||||
var splitindex = query.SearchTerm.LastIndexOf(' ');
|
||||
if (splitindex > -1)
|
||||
query.SearchTerm = query.SearchTerm.Substring(0, splitindex);
|
||||
}
|
||||
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.SanitizedSearchTerm;
|
||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||
var response = await RequestStringWithCookiesAndRetry(episodeSearchUrl);
|
||||
|
||||
try
|
||||
{
|
||||
CQ dom = response.Content;
|
||||
var rows = dom[".torrents tr.torrent"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
|
||||
var qRow = row.Cq();
|
||||
var qTitleLink = qRow.Find("a.title").First();
|
||||
var title = qTitleLink.Text().Trim();
|
||||
|
||||
// Insert before the release info
|
||||
var taidx = title.IndexOf('(');
|
||||
var tbidx = title.IndexOf('[');
|
||||
|
||||
if (taidx == -1)
|
||||
taidx = title.Length;
|
||||
|
||||
if (tbidx == -1)
|
||||
tbidx = title.Length;
|
||||
var titleSplit = Math.Min(taidx, tbidx);
|
||||
var titleSeries = title.Substring(0, titleSplit);
|
||||
var releaseInfo = title.Substring(titleSplit);
|
||||
|
||||
// For each over each pipe deliminated name
|
||||
foreach (var name in titleSeries.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = (name + releaseInfo).Trim();
|
||||
// Ensure the season is defined as this tracker only deals with full seasons
|
||||
if (release.Title.IndexOf("Season") == -1)
|
||||
{
|
||||
// Insert before the release info
|
||||
var aidx = release.Title.IndexOf('(');
|
||||
var bidx = release.Title.IndexOf('[');
|
||||
|
||||
if (aidx == -1)
|
||||
aidx = release.Title.Length;
|
||||
|
||||
if (bidx == -1)
|
||||
bidx = release.Title.Length;
|
||||
|
||||
var insertPoint = Math.Min(aidx, bidx);
|
||||
release.Title = release.Title.Substring(0, insertPoint) + "Season 1 " + release.Title.Substring(insertPoint);
|
||||
}
|
||||
|
||||
release.Description = release.Title;
|
||||
release.Guid = new Uri(SiteLink + qTitleLink.Attr("href"));
|
||||
release.Comments = release.Guid;
|
||||
|
||||
release.Link = new Uri(SiteLink + qRow.Find(".peers a").First().Attr("href"));
|
||||
|
||||
release.Seeders = int.Parse(qRow.Find(".peers a").Get(0).InnerText);
|
||||
release.Peers = release.Seeders + int.Parse(qRow.Find(".peers a").Get(1).InnerText);
|
||||
|
||||
release.MinimumRatio = 1;
|
||||
|
||||
var size = qRow.Find(".size").First().Text();
|
||||
release.Size = ReleaseInfo.GetBytes(size);
|
||||
|
||||
//22 Jul 15
|
||||
var dateStr = qRow.Find(".added").First().Text().Replace("'", string.Empty);
|
||||
if (dateStr.Split(' ')[0].Length == 1)
|
||||
dateStr = "0" + dateStr;
|
||||
|
||||
if (string.Equals(dateStr, "yesterday", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now.AddDays(-1);
|
||||
}
|
||||
else if (string.Equals(dateStr, "today", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
release.PublishDate = DateTime.Now;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(dateStr, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(response.Content, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var downloadPage = await RequestStringWithCookies(link.ToString());
|
||||
CQ dom = downloadPage.Content;
|
||||
var downloadLink = dom.Find(".download_link").First().Attr("href");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(downloadLink))
|
||||
{
|
||||
throw new Exception("Unable to find download link.");
|
||||
}
|
||||
|
||||
var response = await RequestBytesWithCookies(SiteLink + downloadLink);
|
||||
return response.Content;
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,13 +17,14 @@ namespace Jackett.Indexers
|
||||
{
|
||||
public abstract class BaseIndexer
|
||||
{
|
||||
public string SiteLink { get; private set; }
|
||||
public string DisplayDescription { get; private set; }
|
||||
public string DisplayName { get; private set; }
|
||||
public string SiteLink { get; protected set; }
|
||||
public string DisplayDescription { get; protected set; }
|
||||
public string DisplayName { get; protected set; }
|
||||
public string Language { get; protected set; }
|
||||
public string ID { get { return GetIndexerID(GetType()); } }
|
||||
|
||||
public bool IsConfigured { get; protected set; }
|
||||
public TorznabCapabilities TorznabCaps { get; private set; }
|
||||
public TorznabCapabilities TorznabCaps { get; protected set; }
|
||||
protected Logger logger;
|
||||
protected IIndexerManagerService indexerService;
|
||||
protected static List<CachedQueryResult> cache = new List<CachedQueryResult>();
|
||||
@@ -38,13 +39,25 @@ namespace Jackett.Indexers
|
||||
set { configData.CookieHeader.Value = value; }
|
||||
}
|
||||
|
||||
|
||||
public string LastError
|
||||
{
|
||||
get { return configData.LastError.Value; }
|
||||
set
|
||||
{
|
||||
bool SaveNeeded = configData.LastError.Value != value && IsConfigured;
|
||||
configData.LastError.Value = value;
|
||||
if (SaveNeeded)
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
protected ConfigurationData configData;
|
||||
|
||||
private List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
|
||||
|
||||
// standard constructor used by most indexers
|
||||
public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null, string downloadBase = null)
|
||||
: this(manager, client, logger, p)
|
||||
{
|
||||
if (!link.EndsWith("/"))
|
||||
throw new Exception("Site link must end with a slash.");
|
||||
@@ -52,12 +65,7 @@ namespace Jackett.Indexers
|
||||
DisplayName = name;
|
||||
DisplayDescription = description;
|
||||
SiteLink = link;
|
||||
this.logger = logger;
|
||||
indexerService = manager;
|
||||
webclient = client;
|
||||
protectionService = p;
|
||||
this.downloadUrlBase = downloadBase;
|
||||
|
||||
this.configData = configData;
|
||||
|
||||
if (caps == null)
|
||||
@@ -66,6 +74,15 @@ namespace Jackett.Indexers
|
||||
|
||||
}
|
||||
|
||||
// minimal constructor used by e.g. cardigann generic indexer
|
||||
public BaseIndexer(IIndexerManagerService manager, IWebClient client, Logger logger, IProtectionService p)
|
||||
{
|
||||
this.logger = logger;
|
||||
indexerService = manager;
|
||||
webclient = client;
|
||||
protectionService = p;
|
||||
}
|
||||
|
||||
public IEnumerable<ReleaseInfo> CleanLinks(IEnumerable<ReleaseInfo> releases)
|
||||
{
|
||||
if (string.IsNullOrEmpty(downloadUrlBase))
|
||||
@@ -141,43 +158,88 @@ namespace Jackett.Indexers
|
||||
|
||||
protected void CleanCache()
|
||||
{
|
||||
foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList())
|
||||
foreach (var expired in cache.Where(i => DateTime.Now - i.Created > cacheTime).ToList())
|
||||
{
|
||||
cache.Remove(expired);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
|
||||
protected async Task FollowIfRedirect(WebClientStringResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null, bool accumulateCookies = false)
|
||||
{
|
||||
var byteResult = new WebClientByteResult();
|
||||
// Map to byte
|
||||
Mapper.Map(response, byteResult);
|
||||
await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies);
|
||||
await FollowIfRedirect(byteResult, referrer, overrideRedirectUrl, overrideCookies, accumulateCookies);
|
||||
// Map to string
|
||||
Mapper.Map(byteResult, response);
|
||||
}
|
||||
|
||||
protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
|
||||
protected async Task FollowIfRedirect(WebClientByteResult response, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null, bool accumulateCookies = false)
|
||||
{
|
||||
// Follow up to 5 redirects
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (!response.IsRedirect)
|
||||
break;
|
||||
await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies);
|
||||
await DoFollowIfRedirect(response, referrer, overrideRedirectUrl, overrideCookies, accumulateCookies);
|
||||
if (accumulateCookies)
|
||||
{
|
||||
CookieHeader = ResolveCookies((CookieHeader != null && CookieHeader != ""? CookieHeader + " " : "") + (overrideCookies != null && overrideCookies != "" ? overrideCookies + " " : "") + response.Cookies);
|
||||
overrideCookies = response.Cookies = CookieHeader;
|
||||
}
|
||||
if (overrideCookies != null && response.Cookies == null)
|
||||
{
|
||||
response.Cookies = overrideCookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null)
|
||||
private String ResolveCookies(String incomingCookies = "")
|
||||
{
|
||||
var redirRequestCookies = (CookieHeader != null && CookieHeader != "" ? CookieHeader + " " : "") + incomingCookies;
|
||||
System.Text.RegularExpressions.Regex expression = new System.Text.RegularExpressions.Regex(@"([^\\,;\s]+)=([^=\\,;\s]+)");
|
||||
Dictionary<string, string> cookieDIctionary = new Dictionary<string, string>();
|
||||
var matches = expression.Match(redirRequestCookies);
|
||||
while (matches.Success)
|
||||
{
|
||||
if (matches.Groups.Count > 2) cookieDIctionary[matches.Groups[1].Value] = matches.Groups[2].Value;
|
||||
matches = matches.NextMatch();
|
||||
}
|
||||
return string.Join("; ", cookieDIctionary.Select(kv => kv.Key.ToString() + "=" + kv.Value.ToString()).ToArray());
|
||||
|
||||
}
|
||||
|
||||
// Update CookieHeader with new cookies and save the config if something changed (e.g. a new CloudFlare clearance cookie was issued)
|
||||
protected void UpdateCookieHeader(string newCookies, string cookieOverride = null)
|
||||
{
|
||||
string newCookieHeader = ResolveCookies((cookieOverride != null && cookieOverride != "" ? cookieOverride + " " : "") + newCookies);
|
||||
if (CookieHeader != newCookieHeader)
|
||||
{
|
||||
logger.Debug(string.Format("updating Cookies {0} => {1}", CookieHeader, newCookieHeader));
|
||||
CookieHeader = newCookieHeader;
|
||||
if (IsConfigured)
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoFollowIfRedirect(WebClientByteResult incomingResponse, string referrer = null, string overrideRedirectUrl = null, string overrideCookies = null, bool accumulateCookies = false)
|
||||
{
|
||||
if (incomingResponse.IsRedirect)
|
||||
{
|
||||
var redirRequestCookies = "";
|
||||
if (accumulateCookies)
|
||||
{
|
||||
redirRequestCookies = ResolveCookies((CookieHeader != "" ? CookieHeader + " " : "") + (overrideCookies != null ? overrideCookies : ""));
|
||||
} else
|
||||
{
|
||||
redirRequestCookies = (overrideCookies != null ? overrideCookies : "");
|
||||
}
|
||||
// Do redirect
|
||||
var redirectedResponse = await webclient.GetBytes(new WebRequest()
|
||||
{
|
||||
Url = overrideRedirectUrl ?? incomingResponse.RedirectingTo,
|
||||
Referer = referrer,
|
||||
Cookies = overrideCookies ?? CookieHeader
|
||||
Cookies = redirRequestCookies
|
||||
});
|
||||
Mapper.Map(redirectedResponse, incomingResponse);
|
||||
}
|
||||
@@ -233,8 +295,10 @@ namespace Jackett.Indexers
|
||||
public async virtual Task<byte[]> Download(Uri link)
|
||||
{
|
||||
var response = await RequestBytesWithCookiesAndRetry(link.ToString());
|
||||
if(response.Status != System.Net.HttpStatusCode.OK && response.Status != System.Net.HttpStatusCode.Continue && response.Status != System.Net.HttpStatusCode.PartialContent)
|
||||
if (response.Status != System.Net.HttpStatusCode.OK && response.Status != System.Net.HttpStatusCode.Continue && response.Status != System.Net.HttpStatusCode.PartialContent)
|
||||
{
|
||||
if(response.Content != null)
|
||||
logger.Error("Failed download response:\n" + Encoding.UTF8.GetString(response.Content));
|
||||
throw new Exception($"Remote server returned {response.Status.ToString()}");
|
||||
}
|
||||
|
||||
@@ -269,15 +333,17 @@ namespace Jackett.Indexers
|
||||
Type = RequestType.GET,
|
||||
Cookies = CookieHeader,
|
||||
Referer = referer,
|
||||
Headers = headers
|
||||
Headers = headers
|
||||
};
|
||||
|
||||
if (cookieOverride != null)
|
||||
request.Cookies = cookieOverride;
|
||||
return await webclient.GetString(request);
|
||||
WebClientStringResult result = await webclient.GetString(request);
|
||||
UpdateCookieHeader(result.Cookies, cookieOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<WebClientStringResult> RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null, Dictionary<string,string> headers = null)
|
||||
protected async Task<WebClientStringResult> RequestStringWithCookiesAndRetry(string url, string cookieOverride = null, string referer = null, Dictionary<string, string> headers = null)
|
||||
{
|
||||
Exception lastException = null;
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -326,7 +392,9 @@ namespace Jackett.Indexers
|
||||
|
||||
if (emulateBrowser.HasValue)
|
||||
request.EmulateBrowser = emulateBrowser.Value;
|
||||
return await webclient.GetString(request);
|
||||
WebClientStringResult result = await webclient.GetString(request);
|
||||
UpdateCookieHeader(result.Cookies, cookieOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<WebClientStringResult> PostDataWithCookiesAndRetry(string url, IEnumerable<KeyValuePair<string, string>> data, string cookieOverride = null, string referer = null, Dictionary<string, string> headers = null, string rawbody = null, bool? emulateBrowser = null)
|
||||
@@ -349,7 +417,7 @@ namespace Jackett.Indexers
|
||||
throw lastException;
|
||||
}
|
||||
|
||||
protected async Task<WebClientStringResult> RequestLoginAndFollowRedirect(string url, IEnumerable<KeyValuePair<string, string>> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null)
|
||||
protected async Task<WebClientStringResult> RequestLoginAndFollowRedirect(string url, IEnumerable<KeyValuePair<string, string>> data, string cookies, bool returnCookiesFromFirstCall, string redirectUrlOverride = null, string referer = null, bool accumulateCookies = false)
|
||||
{
|
||||
var request = new Utils.Clients.WebRequest()
|
||||
{
|
||||
@@ -360,18 +428,22 @@ namespace Jackett.Indexers
|
||||
PostData = data
|
||||
};
|
||||
var response = await webclient.GetString(request);
|
||||
if (accumulateCookies)
|
||||
{
|
||||
response.Cookies = ResolveCookies((request.Cookies == null ? "" : request.Cookies + " ") + response.Cookies);
|
||||
}
|
||||
var firstCallCookies = response.Cookies;
|
||||
|
||||
if (response.IsRedirect)
|
||||
{
|
||||
await FollowIfRedirect(response, request.Url, redirectUrlOverride, response.Cookies);
|
||||
await FollowIfRedirect(response, request.Url, redirectUrlOverride, response.Cookies, accumulateCookies);
|
||||
}
|
||||
|
||||
if (returnCookiesFromFirstCall)
|
||||
{
|
||||
response.Cookies = firstCallCookies;
|
||||
response.Cookies = ResolveCookies(firstCallCookies + (accumulateCookies ? " " + response.Cookies : ""));
|
||||
}
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -400,43 +472,23 @@ namespace Jackett.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddCategoryMapping(string trackerCategory, int newznabCategory)
|
||||
protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory));
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory, newznabCategory.ID));
|
||||
if (!TorznabCaps.Categories.Contains(newznabCategory))
|
||||
TorznabCaps.Categories.Add(newznabCategory);
|
||||
}
|
||||
|
||||
protected void AddCategoryMapping(int trackerCategory, TorznabCategory newznabCategory)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID));
|
||||
if (!TorznabCaps.Categories.Contains(newznabCategory))
|
||||
TorznabCaps.Categories.Add(newznabCategory);
|
||||
}
|
||||
|
||||
protected void AddCategoryMapping(string trackerCategory, TorznabCategory newznabCategory)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory.ID));
|
||||
if (!TorznabCaps.Categories.Contains(newznabCategory))
|
||||
TorznabCaps.Categories.Add(newznabCategory);
|
||||
}
|
||||
|
||||
protected void AddCategoryMapping(int trackerCategory, int newznabCategory)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCategory));
|
||||
AddCategoryMapping(trackerCategory.ToString(), newznabCategory);
|
||||
}
|
||||
|
||||
protected void AddMultiCategoryMapping(TorznabCategory newznabCategory, params int[] trackerCategories)
|
||||
{
|
||||
foreach (var trackerCat in trackerCategories)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCat.ToString(), newznabCategory.ID));
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddMultiCategoryMapping(int trackerCategory, params TorznabCategory[] newznabCategories)
|
||||
{
|
||||
foreach (var newznabCat in newznabCategories)
|
||||
{
|
||||
categoryMapping.Add(new CategoryMapping(trackerCategory.ToString(), newznabCat.ID));
|
||||
AddCategoryMapping(trackerCat, newznabCategory);
|
||||
}
|
||||
}
|
||||
|
||||
|
229
src/Jackett/Indexers/BestFriends.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BestFriends : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string TakeLoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "browse.php"; } }
|
||||
|
||||
new ConfigurationDataCaptchaLogin configData
|
||||
{
|
||||
get { return (ConfigurationDataCaptchaLogin)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BestFriends(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Best Friends",
|
||||
description: "A German general tracker.",
|
||||
link: "http://bf.mine.nu/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCaptchaLogin())
|
||||
{
|
||||
AddCategoryMapping(18, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(8, TorznabCatType.PCMac); // Appz MAC
|
||||
AddCategoryMapping(9, TorznabCatType.PC); // Appz other
|
||||
AddCategoryMapping(7, TorznabCatType.PC); // Appz Windows
|
||||
AddCategoryMapping(23, TorznabCatType.TVDocumentary); // Dokumentationen
|
||||
AddCategoryMapping(32, TorznabCatType.Movies3D); // DVD 3D
|
||||
AddCategoryMapping(15, TorznabCatType.Books); // eBooks
|
||||
AddCategoryMapping(12, TorznabCatType.PCGames); // Games PC
|
||||
AddCategoryMapping(37, TorznabCatType.PCPhoneOther); // Handy_Mobile
|
||||
AddCategoryMapping(24, TorznabCatType.TVHD); // HDTV
|
||||
AddCategoryMapping(22, TorznabCatType.AudioAudiobook); // Hörbücher
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesHD); // Movies 1080p/1080i
|
||||
AddCategoryMapping(31, TorznabCatType.Movies3D); // Movies 3D
|
||||
AddCategoryMapping(2, TorznabCatType.MoviesHD); // Movies 720p/720i
|
||||
AddCategoryMapping(21, TorznabCatType.MoviesBluRay); // Movies BluRay
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesDVD); // Movies DVD/HDDVD
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesSD); // Movies M/SVCD/Other
|
||||
AddCategoryMapping(4, TorznabCatType.MoviesSD); // Movies XVID/DIVX/h.264
|
||||
AddCategoryMapping(10, TorznabCatType.Audio); // Music
|
||||
AddCategoryMapping(25, TorznabCatType.AudioVideo); // Musikvideo
|
||||
AddCategoryMapping(29, TorznabCatType.ConsoleNDS); // Nintendo DS
|
||||
AddCategoryMapping(16, TorznabCatType.Other); // other
|
||||
AddCategoryMapping(13, TorznabCatType.ConsolePS4); // Playstation
|
||||
AddCategoryMapping(28, TorznabCatType.TVHD); // Serien HD
|
||||
AddCategoryMapping(11, TorznabCatType.TVSD); // Serien XviD
|
||||
AddCategoryMapping(33, TorznabCatType.Other); // Specials
|
||||
AddCategoryMapping(30, TorznabCatType.TVSport); // Sport
|
||||
AddCategoryMapping(19, TorznabCatType.TVOTHER); // TVRip
|
||||
AddCategoryMapping(38, TorznabCatType.TVDocumentary); // US Dokus
|
||||
AddCategoryMapping(20, TorznabCatType.MoviesForeign); // US Movies
|
||||
AddCategoryMapping(14, TorznabCatType.TVFOREIGN); // US Serien
|
||||
AddCategoryMapping(36, TorznabCatType.Other); // Wallpaper
|
||||
AddCategoryMapping(26, TorznabCatType.ConsoleWii); // Wii
|
||||
AddCategoryMapping(27, TorznabCatType.ConsoleXbox360); // Xbox 360
|
||||
AddCategoryMapping(3, TorznabCatType.XXX); // XXX
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
CQ dom = loginPage.Content;
|
||||
CQ qCaptchaImg = dom.Find("td.tablea > img").First();
|
||||
|
||||
var CaptchaUrl = SiteLink + qCaptchaImg.Attr("src");
|
||||
var captchaImage = await RequestBytesWithCookies(CaptchaUrl, loginPage.Cookies);
|
||||
configData.CaptchaImage.Value = captchaImage.Content;
|
||||
configData.CaptchaCookie.Value = loginPage.Cookies;
|
||||
return configData;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs1 = new Dictionary<string, string>
|
||||
{
|
||||
{ "proofcode", configData.CaptchaText.Value }
|
||||
};
|
||||
var cookies = configData.CaptchaCookie.Value;
|
||||
var result1 = await RequestLoginAndFollowRedirect(LoginUrl, pairs1, cookies, true, null, LoginUrl, true);
|
||||
if(result1.Content == null || !result1.Content.Contains("takelogin.php"))
|
||||
{
|
||||
CQ dom = result1.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
errorMessage = result1.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
}
|
||||
|
||||
var pairs2 = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(TakeLoginUrl, pairs2, result1.Cookies, true, null, LoginUrl, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public 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("blah", "0");
|
||||
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 RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.tableinborder > tbody > tr:has(td.tableb)"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.75;
|
||||
release.MinimumSeedTime = 0;
|
||||
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();
|
||||
|
||||
// use negative indexes as if a user has "Wartezeit" there's an extra column after the title
|
||||
var qSeeders = qRow.Find("td:nth-last-child(4)");
|
||||
var qLeechers = qRow.Find("td:nth-last-child(3)");
|
||||
var qDateStr = qRow.Find("td:nth-last-child(7)");
|
||||
var qSize = qRow.Find("td:nth-last-child(6)");
|
||||
|
||||
var torrentId = qDetailsLink.Attr("href").Replace("&hit=1", "").Split('=')[1];
|
||||
|
||||
var catStr = qCatLink.Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catStr);
|
||||
|
||||
release.Link = new Uri(SiteLink + "download.php?torrent="+torrentId);
|
||||
release.Comments = new Uri(SiteLink + qDetailsLink.Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
|
||||
var sizeStr = qSize.Text();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qSeeders.Text());
|
||||
release.Peers = ParseUtil.CoerceInt(qLeechers.Text()) + release.Seeders;
|
||||
|
||||
var dateStr = qDateStr.Text();
|
||||
var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyyHH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc;
|
||||
|
||||
var files = qRow.Find("td:nth-last-child(9)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-last-child(5)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("font[color=\"red\"]:contains(OnlyUp)").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,26 +18,26 @@ namespace Jackett.Indexers
|
||||
public class BeyondHD : BaseIndexer, IIndexer
|
||||
{
|
||||
private string SearchUrl { get { return SiteLink + "browse.php?searchin=title&incldead=0&"; } }
|
||||
private string LoginUrl { get { return SiteLink + "login.php?returnto=%2F"; } }
|
||||
private string AjaxLoginUrl { get { return SiteLink + "ajax/takelogin.php"; } }
|
||||
|
||||
new ConfigurationDataRecaptchaLogin configData
|
||||
new ConfigurationDataLoginLink configData
|
||||
{
|
||||
get { return (ConfigurationDataRecaptchaLogin)base.configData; }
|
||||
get { return (ConfigurationDataLoginLink)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "BeyondHD",
|
||||
description: "Without BeyondHD, your HDTV is just a TV",
|
||||
link: "https://beyondhd.me/",
|
||||
link: "https://beyond-hd.me/",
|
||||
caps: new TorznabCapabilities(),
|
||||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRecaptchaLogin())
|
||||
configData: new ConfigurationDataLoginLink())
|
||||
{
|
||||
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
|
||||
@@ -53,7 +53,10 @@ namespace Jackett.Indexers
|
||||
75, // Internal / FraMeSToR 720p
|
||||
49, // Internal / FraMeSToR REMUX
|
||||
61, // Internal / HDX REMUX
|
||||
86 // Internal / SC4R
|
||||
86, // Internal / SC4R
|
||||
95, // Nightripper 1080p
|
||||
96, // Nightripper 720p
|
||||
98 // Nightripper MicroHD
|
||||
);
|
||||
|
||||
AddMultiCategoryMapping(TorznabCatType.TVHD,
|
||||
@@ -62,7 +65,8 @@ namespace Jackett.Indexers
|
||||
48, // TV Show / HDTV
|
||||
89, // TV Show / Packs
|
||||
46, // TV Show / Remux
|
||||
45 // TV Show / WEB-DL
|
||||
45, // TV Show / WEB-DL
|
||||
97 // Nightripper TV Show Encodes
|
||||
);
|
||||
|
||||
AddCategoryMapping(36, TorznabCatType.AudioLossless); // Music / Lossless
|
||||
@@ -76,52 +80,12 @@ namespace Jackett.Indexers
|
||||
|
||||
}
|
||||
|
||||
public override async Task<ConfigurationData> GetConfigurationForSetup()
|
||||
{
|
||||
var loginPage = await RequestStringWithCookies(LoginUrl, string.Empty);
|
||||
string recaptchaSiteKey = new Regex(@"loginwidget', \{[\s]{4,30}'sitekey' : '([0-9A-Za-z]{5,60})',[\s]{4,30}'theme'").Match(loginPage.Content).Groups[1].ToString().Trim();
|
||||
var result = new ConfigurationDataRecaptchaLogin();
|
||||
result.CookieHeader.Value = loginPage.Cookies;
|
||||
result.Captcha.SiteKey = recaptchaSiteKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.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))
|
||||
{
|
||||
// Cookie was manually supplied
|
||||
CookieHeader = configData.Captcha.Cookie;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (!results.Any())
|
||||
{
|
||||
throw new Exception("Your cookie did not work");
|
||||
}
|
||||
|
||||
SaveConfig();
|
||||
IsConfigured = true;
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
IsConfigured = false;
|
||||
throw new Exception("Your cookie did not work: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(AjaxLoginUrl, pairs, configData.CookieHeader.Value, true, SiteLink, LoginUrl);
|
||||
JToken token = JObject.Parse(result.Content);
|
||||
bool success = token.Value<bool?>("success") ?? false;
|
||||
await ConfigureIfOK(result.Cookies, success, () =>
|
||||
|
||||
var result = await RequestStringWithCookies(configData.LoginLink.Value);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("Welcome Back"), () =>
|
||||
{
|
||||
var errorMessage = result.Content;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
@@ -187,6 +151,15 @@ namespace Jackett.Indexers
|
||||
release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(9).Cq().Text());
|
||||
release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(10).Cq().Text()) + release.Seeders;
|
||||
|
||||
var files = qRow.Find("td:nth-child(6)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:nth-child(9) > a").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 0; // ratioless
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
|
||||
}
|
||||
|
199
src/Jackett/Indexers/BitCityReloaded.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using Jackett.Utils.Clients;
|
||||
using NLog;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using CsQuery;
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BitCityReloaded : BaseIndexer, IIndexer
|
||||
{
|
||||
string LoginUrl { get { return SiteLink + "login.php"; } }
|
||||
string BrowseUrl { get { return SiteLink + "uebersicht.php"; } }
|
||||
TimeZoneInfo germanyTz = TimeZoneInfo.CreateCustomTimeZone("W. Europe Standard Time", new TimeSpan(1, 0, 0), "W. Europe Standard Time", "W. Europe Standard Time");
|
||||
|
||||
new ConfigurationDataBasicLoginWithRSSAndDisplay configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BitCityReloaded(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "Bit-City Reloaded",
|
||||
description: "A German general tracker.",
|
||||
link: "https://bc-reloaded.net/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithRSSAndDisplay())
|
||||
{
|
||||
this.configData.DisplayText.Value = "Only the results from the first search result page are shown, adjust your profile settings to show a reasonable amount (it looks like there's no maximum).";
|
||||
this.configData.DisplayText.Name = "Notice";
|
||||
|
||||
AddCategoryMapping(1, TorznabCatType.Other); // Anderes
|
||||
AddCategoryMapping(2, TorznabCatType.TVAnime); // Anime
|
||||
AddCategoryMapping(34, TorznabCatType.PC); // Appz/Linux
|
||||
AddCategoryMapping(35, TorznabCatType.PCMac); // Appz/Mac
|
||||
AddCategoryMapping(36, TorznabCatType.PC); // Appz/Other
|
||||
AddCategoryMapping(20, TorznabCatType.PC); // Appz/Win
|
||||
AddCategoryMapping(3, TorznabCatType.TVDocumentary); // Doku/Alle Formate
|
||||
AddCategoryMapping(4, TorznabCatType.Books); // EBooks
|
||||
AddCategoryMapping(12, TorznabCatType.ConsolePS4); // Games PS / PSX
|
||||
AddCategoryMapping(11, TorznabCatType.ConsoleNDS); // Games/Nintendo DS
|
||||
AddCategoryMapping(10, TorznabCatType.PCGames); // Games/PC
|
||||
AddCategoryMapping(13, TorznabCatType.ConsoleWii); // Games/Wii
|
||||
AddCategoryMapping(14, TorznabCatType.ConsoleXbox); // Games/Xbox & 360
|
||||
AddCategoryMapping(15, TorznabCatType.PCPhoneOther); // Handy & PDA
|
||||
AddCategoryMapping(16, TorznabCatType.AudioAudiobook); // Hörspiel/Hörbuch
|
||||
AddCategoryMapping(30, TorznabCatType.Other); // International
|
||||
AddCategoryMapping(17, TorznabCatType.Other); // MegaPack
|
||||
AddCategoryMapping(43, TorznabCatType.Movies3D); // Movie/3D
|
||||
AddCategoryMapping(5, TorznabCatType.MoviesDVD); // Movie/DVD/R
|
||||
AddCategoryMapping(6, TorznabCatType.MoviesHD); // Movie/HD 1080p
|
||||
AddCategoryMapping(7, TorznabCatType.MoviesHD); // Movie/HD 720p
|
||||
AddCategoryMapping(32, TorznabCatType.MoviesOther); // Movie/TVRip
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesOther); // Movie/XviD,DivX,h264
|
||||
AddCategoryMapping(26, TorznabCatType.XXX); // Movie/XXX
|
||||
AddCategoryMapping(41, TorznabCatType.XXXOther); // Movie/XXX/Other
|
||||
AddCategoryMapping(42, TorznabCatType.XXXPacks); // Movie/XXX/Pack
|
||||
AddCategoryMapping(45, TorznabCatType.MoviesHD); // Movies/4K
|
||||
AddCategoryMapping(33, TorznabCatType.MoviesBluRay); // Movies/BluRay
|
||||
AddCategoryMapping(18, TorznabCatType.Audio); // Musik
|
||||
AddCategoryMapping(19, TorznabCatType.AudioVideo); // Musik Videos
|
||||
AddCategoryMapping(44, TorznabCatType.TVOTHER); // Serie/DVD/R
|
||||
AddCategoryMapping(22, TorznabCatType.TVHD); // Serie/HDTV
|
||||
AddCategoryMapping(38, TorznabCatType.TV); // Serie/Pack
|
||||
AddCategoryMapping(23, TorznabCatType.TVOTHER); // Serie/XviD,DivX,h264
|
||||
AddCategoryMapping(25, TorznabCatType.TVSport); // Sport
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.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);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var errorMessage = dom["#login_error"].Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var queryCollection = new NameValueCollection();
|
||||
queryCollection.Add("showsearch", "0");
|
||||
queryCollection.Add("incldead", "1");
|
||||
queryCollection.Add("blah", "0");
|
||||
queryCollection.Add("team", "0");
|
||||
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 RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
var rows = dom["table.tableinborder[cellpadding=0] > tbody > tr"];
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
release.MinimumRatio = 0.7;
|
||||
release.MinimumSeedTime = 48 * 60 * 60;
|
||||
var qRow = row.Cq();
|
||||
var flagImgs = qRow.Find("table tbody tr: eq(0) td > img");
|
||||
List<string> flags = new List<string>();
|
||||
flagImgs.Each(flagImg => {
|
||||
flags.Add(flagImg.GetAttribute("src").Replace("pic/torrent_", "").Replace(".gif", "").ToUpper());
|
||||
});
|
||||
|
||||
var titleLink = qRow.Find("table tbody tr:eq(0) td a:has(b)").First();
|
||||
var DLLink = qRow.Find("td.tableb > a:has(img[title=\"Torrent herunterladen\"])").First();
|
||||
release.Comments = new Uri(SiteLink + titleLink.Attr("href").Replace("&hit=1", ""));
|
||||
release.Link = new Uri(SiteLink + DLLink.Attr("href"));
|
||||
release.Title = titleLink.Text().Trim();
|
||||
|
||||
if (!query.MatchQueryStringAND(release.Title))
|
||||
continue;
|
||||
|
||||
release.Description = String.Join(", ", flags);
|
||||
release.Guid = release.Link;
|
||||
|
||||
var dateStr = qRow.Find("table tbody tr:eq(1) td:eq(4)").Html().Replace(" ", " ").Trim();
|
||||
var dateGerman = DateTime.SpecifyKind(DateTime.ParseExact(dateStr, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
DateTime pubDateUtc = TimeZoneInfo.ConvertTimeToUtc(dateGerman, germanyTz);
|
||||
release.PublishDate = pubDateUtc.ToLocalTime();
|
||||
|
||||
var sizeStr = qRow.Find("table tbody tr:eq(1) td b").First().Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr.Replace(",", "."));
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(0) font").Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(qRow.Find("table tbody tr:eq(1) td:eq(1) b:eq(1) font").Text().Trim()) + release.Seeders;
|
||||
|
||||
var catId = qRow.Find("td:eq(0) a").First().Attr("href").Split('=')[1];
|
||||
release.Category = MapTrackerCatToNewznab(catId);
|
||||
|
||||
var cat = qRow.Find("td:eq(0) a").First().Attr("href").Substring(1);
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
var files = qRow.Find("td:has(a[href*=\"&filelist=1\"])> b:nth-child(2)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = qRow.Find("td:has(a[href*=\"&tosnatchers=1\"])> b:nth-child(1)").Text();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
if (qRow.Find("img[src=\"pic/torrent_ou.gif\"]").Length >= 1)
|
||||
release.DownloadVolumeFactor = 0;
|
||||
else
|
||||
release.DownloadVolumeFactor = 1;
|
||||
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ namespace Jackett.Indexers
|
||||
{
|
||||
private string LoginUrl { get { return SiteLink + "takelogin.php"; } }
|
||||
private string SearchUrl { get { return SiteLink + "torrents.php?"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php?/{0}/dl.torrent"; } }
|
||||
private string DownloadUrl { get { return SiteLink + "download.php?id={0}"; } }
|
||||
|
||||
new ConfigurationDataBasicLogin configData
|
||||
{
|
||||
|
@@ -42,7 +42,7 @@ namespace Jackett.Indexers
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCaptchaLogin())
|
||||
configData: new ConfigurationDataCaptchaLogin("Ensure that you have the 'Force SSL' option set to 'yes' in your profile on the BitMeTv webpage."))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -129,6 +129,15 @@ namespace Jackett.Indexers
|
||||
//if (!release.Title.ToLower().Contains(title.ToLower()))
|
||||
// continue;
|
||||
|
||||
var files = row.Cq().Find("td:nth-child(4)").Text();
|
||||
release.Files = ParseUtil.CoerceInt(files);
|
||||
|
||||
var grabs = row.Cq().Find("td:nth-child(8)").Get(0).FirstChild.ToString();
|
||||
release.Grabs = ParseUtil.CoerceInt(grabs);
|
||||
|
||||
release.DownloadVolumeFactor = 1;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
|
247
src/Jackett/Indexers/BitSoup.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using CsQuery;
|
||||
using Jackett.Models;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Jackett.Models.IndexerConfig;
|
||||
using System.Collections.Specialized;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BitSoup : BaseIndexer, IIndexer
|
||||
{
|
||||
private string UseLink { get { return (this.configData.AlternateLink.Value != null && this.configData.AlternateLink.Value != "" ? this.configData.AlternateLink.Value : SiteLink); } }
|
||||
private string BrowseUrl { get { return UseLink + "browse.php"; } }
|
||||
private string LoginUrl { get { return UseLink + "takelogin.php"; } }
|
||||
private string LoginReferer { get { return UseLink + "login.php"; } }
|
||||
private List<String> KnownURLs = new List<String> { "https://www.bitsoup.me/", "https://www.bitsoup.org/" };
|
||||
|
||||
new ConfigurationDataBasicLoginWithAlternateLink configData
|
||||
{
|
||||
get { return (ConfigurationDataBasicLoginWithAlternateLink)base.configData; }
|
||||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BitSoup(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BitSoup",
|
||||
description: "SoupieBits",
|
||||
link: "https://www.bitsoup.me/",
|
||||
caps: TorznabUtil.CreateDefaultTorznabTVCaps(),
|
||||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLoginWithAlternateLink())
|
||||
{
|
||||
this.configData.Instructions.Value = this.DisplayName + " has multiple URLs. The default (" + this.SiteLink + ") can be changed by entering a new value in the box below.";
|
||||
this.configData.Instructions.Value += "The following are some known URLs for " + this.DisplayName;
|
||||
this.configData.Instructions.Value += "<ul><li>" + String.Join("</li><li>", this.KnownURLs.ToArray()) + "</li></ul>";
|
||||
|
||||
//AddCategoryMapping("624", TorznabCatType.Console);
|
||||
//AddCategoryMapping("307", TorznabCatType.ConsoleNDS);
|
||||
//AddCategoryMapping("308", TorznabCatType.ConsolePSP);
|
||||
AddCategoryMapping("35", TorznabCatType.ConsoleWii);
|
||||
//AddCategoryMapping("309", TorznabCatType.ConsoleXbox);
|
||||
AddCategoryMapping("12", TorznabCatType.ConsoleXbox360);
|
||||
//AddCategoryMapping("305", TorznabCatType.ConsoleWiiwareVC);
|
||||
//AddCategoryMapping("309", TorznabCatType.ConsoleXBOX360DLC);
|
||||
AddCategoryMapping("38", TorznabCatType.ConsolePS3);
|
||||
//AddCategoryMapping("239", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("245", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("246", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("626", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("628", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("630", TorznabCatType.ConsoleOther);
|
||||
//AddCategoryMapping("307", TorznabCatType.Console3DS);
|
||||
//AddCategoryMapping("308", TorznabCatType.ConsolePSVita);
|
||||
//AddCategoryMapping("307", TorznabCatType.ConsoleWiiU);
|
||||
//AddCategoryMapping("309", TorznabCatType.ConsoleXboxOne);
|
||||
//AddCategoryMapping("308", TorznabCatType.ConsolePS4);
|
||||
//AddCategoryMapping("631", TorznabCatType.Movies);
|
||||
//AddCategoryMapping("631", TorznabCatType.MoviesForeign);
|
||||
//AddCategoryMapping("455", TorznabCatType.MoviesOther);
|
||||
//AddCategoryMapping("633", TorznabCatType.MoviesOther);
|
||||
AddCategoryMapping("19", TorznabCatType.MoviesSD);
|
||||
AddCategoryMapping("41", TorznabCatType.MoviesHD);
|
||||
AddCategoryMapping("17", TorznabCatType.Movies3D);
|
||||
AddCategoryMapping("80", TorznabCatType.MoviesBluRay);
|
||||
AddCategoryMapping("20", TorznabCatType.MoviesDVD);
|
||||
//AddCategoryMapping("631", TorznabCatType.MoviesWEBDL);
|
||||
AddCategoryMapping("6", TorznabCatType.Audio);
|
||||
//AddCategoryMapping("623", TorznabCatType.AudioMP3);
|
||||
AddCategoryMapping("29", TorznabCatType.AudioVideo);
|
||||
//AddCategoryMapping("402", TorznabCatType.AudioVideo);
|
||||
AddCategoryMapping("5", TorznabCatType.AudioAudiobook);
|
||||
//AddCategoryMapping("1", TorznabCatType.AudioLossless);
|
||||
//AddCategoryMapping("403", TorznabCatType.AudioOther);
|
||||
//AddCategoryMapping("642", TorznabCatType.AudioOther);
|
||||
//AddCategoryMapping("1", TorznabCatType.AudioForeign);
|
||||
//AddCategoryMapping("233", TorznabCatType.PC);
|
||||
//AddCategoryMapping("236", TorznabCatType.PC);
|
||||
//AddCategoryMapping("1", TorznabCatType.PC0day);
|
||||
AddCategoryMapping("1", TorznabCatType.PCISO);
|
||||
//AddCategoryMapping("235", TorznabCatType.PCMac);
|
||||
//AddCategoryMapping("627", TorznabCatType.PCPhoneOther);
|
||||
AddCategoryMapping("21", TorznabCatType.PCGames);
|
||||
AddCategoryMapping("4", TorznabCatType.PCGames);
|
||||
//AddCategoryMapping("625", TorznabCatType.PCPhoneIOS);
|
||||
//AddCategoryMapping("625", TorznabCatType.PCPhoneAndroid);
|
||||
AddCategoryMapping("45", TorznabCatType.TV);
|
||||
//AddCategoryMapping("433", TorznabCatType.TV);
|
||||
//AddCategoryMapping("639", TorznabCatType.TVWEBDL);
|
||||
//AddCategoryMapping("433", TorznabCatType.TVWEBDL);
|
||||
//AddCategoryMapping("639", TorznabCatType.TVFOREIGN);
|
||||
//AddCategoryMapping("433", TorznabCatType.TVFOREIGN);
|
||||
AddCategoryMapping("7", TorznabCatType.TVSD);
|
||||
AddCategoryMapping("49", TorznabCatType.TVSD);
|
||||
AddCategoryMapping("42", TorznabCatType.TVHD);
|
||||
//AddCategoryMapping("433", TorznabCatType.TVHD);
|
||||
//AddCategoryMapping("635", TorznabCatType.TVOTHER);
|
||||
//AddCategoryMapping("636", TorznabCatType.TVSport);
|
||||
AddCategoryMapping("23", TorznabCatType.TVAnime);
|
||||
//AddCategoryMapping("634", TorznabCatType.TVDocumentary);
|
||||
AddCategoryMapping("9", TorznabCatType.XXX);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXDVD);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXWMV);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXXviD);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXx264);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXOther);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXImageset);
|
||||
//AddCategoryMapping("1", TorznabCatType.XXXPacks);
|
||||
//AddCategoryMapping("340", TorznabCatType.Other);
|
||||
//AddCategoryMapping("342", TorznabCatType.Other);
|
||||
//AddCategoryMapping("344", TorznabCatType.Other);
|
||||
//AddCategoryMapping("391", TorznabCatType.Other);
|
||||
//AddCategoryMapping("392", TorznabCatType.Other);
|
||||
//AddCategoryMapping("393", TorznabCatType.Other);
|
||||
//AddCategoryMapping("394", TorznabCatType.Other);
|
||||
//AddCategoryMapping("234", TorznabCatType.Other);
|
||||
//AddCategoryMapping("638", TorznabCatType.Other);
|
||||
//AddCategoryMapping("629", TorznabCatType.Other);
|
||||
//AddCategoryMapping("1", TorznabCatType.OtherMisc);
|
||||
//AddCategoryMapping("1", TorznabCatType.OtherHashed);
|
||||
//AddCategoryMapping("408", TorznabCatType.Books);
|
||||
AddCategoryMapping("24", TorznabCatType.BooksEbook);
|
||||
//AddCategoryMapping("406", TorznabCatType.BooksComics);
|
||||
//AddCategoryMapping("407", TorznabCatType.BooksComics);
|
||||
//AddCategoryMapping("409", TorznabCatType.BooksComics);
|
||||
//AddCategoryMapping("410", TorznabCatType.BooksMagazines);
|
||||
//AddCategoryMapping("1", TorznabCatType.BooksTechnical);
|
||||
//AddCategoryMapping("1", TorznabCatType.BooksOther);
|
||||
//AddCategoryMapping("1", TorznabCatType.BooksForeign);
|
||||
}
|
||||
|
||||
public async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
configData.LoadValuesFromJson(configJson);
|
||||
if (configData.AlternateLink.Value != null && configData.AlternateLink.Value != "")
|
||||
{
|
||||
if (!configData.AlternateLink.Value.EndsWith("/"))
|
||||
{
|
||||
configData.AlternateLink.Value = null;
|
||||
throw new Exception("AlternateLink must end with a slash.");
|
||||
}
|
||||
var match = Regex.Match(configData.AlternateLink.Value, "^https?:\\/\\/(?:[\\w]+\\.)+(?:[a-zA-Z]+)\\/$");
|
||||
if (!match.Success)
|
||||
{
|
||||
configData.AlternateLink.Value = null;
|
||||
throw new Exception("AlternateLink must be a valid url.");
|
||||
}
|
||||
}
|
||||
var pairs = new Dictionary<string, string> {
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value },
|
||||
|
||||
};
|
||||
|
||||
var loginPage = await RequestStringWithCookies(UseLink, string.Empty);
|
||||
|
||||
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, loginPage.Cookies, true, null, LoginReferer, true);
|
||||
await ConfigureIfOK(result.Cookies, result.Content != null && result.Content.Contains("logout.php"), () =>
|
||||
{
|
||||
CQ dom = result.Content;
|
||||
var messageEl = dom["body > table.statusbar1 > tbody > tr > td > table > tbody > tr > td > table > tbody > tr > td"].First();
|
||||
var errorMessage = messageEl.Text().Trim();
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchString = query.GetQueryString();
|
||||
var searchUrl = BrowseUrl;
|
||||
var trackerCats = MapTorznabCapsToTrackers(query);
|
||||
var queryCollection = new NameValueCollection();
|
||||
|
||||
|
||||
queryCollection.Add("search", string.IsNullOrWhiteSpace(searchString) ? "" : searchString);
|
||||
if (trackerCats.Count > 1)
|
||||
{
|
||||
for (var ct = 0; ct < trackerCats.Count; ct++) queryCollection.Add("cat" + (ct + 1), trackerCats.ElementAt(ct));
|
||||
}
|
||||
else
|
||||
{
|
||||
queryCollection.Add("cat", (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0"));
|
||||
}
|
||||
//queryCollection.Add("cat", (trackerCats.Count == 1 ? trackerCats.ElementAt(0) : "0"));
|
||||
searchUrl += "?" + queryCollection.GetQueryString();
|
||||
await ProcessPage(releases, searchUrl);
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
private async Task ProcessPage(List<ReleaseInfo> releases, string searchUrl)
|
||||
{
|
||||
var response = await RequestStringWithCookiesAndRetry(searchUrl, null, BrowseUrl);
|
||||
var results = response.Content;
|
||||
try
|
||||
{
|
||||
CQ dom = results;
|
||||
|
||||
var rows = dom["table.koptekst tr"];
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Title = row.Cq().Find("td:eq(1) a").First().Text().Trim();
|
||||
release.Comments = new Uri(UseLink + row.Cq().Find("td:eq(1) a").First().Attr("href"));
|
||||
|
||||
release.Link = new Uri(UseLink + row.Cq().Find("td:eq(2) a").First().Attr("href"));
|
||||
release.Guid = release.Link;
|
||||
release.Description = release.Title;
|
||||
var cat = row.Cq().Find("td:eq(0) a").First().Attr("href").Substring(15);
|
||||
release.Category = MapTrackerCatToNewznab(cat);
|
||||
|
||||
var added = row.Cq().Find("td:eq(7)").First().Text().Trim();
|
||||
release.PublishDate = DateTime.ParseExact(added, "yyyy-MM-ddH:mm:ss", CultureInfo.InvariantCulture);
|
||||
|
||||
var sizeStr = row.Cq().Find("td:eq(8)").First().Text().Trim();
|
||||
release.Size = ReleaseInfo.GetBytes(sizeStr);
|
||||
|
||||
release.Seeders = ParseUtil.CoerceInt(row.Cq().Find("td:eq(10)").First().Text().Trim());
|
||||
release.Peers = ParseUtil.CoerceInt(row.Cq().Find("td:eq(11)").First().Text().Trim()) + release.Seeders;
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|