mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-15 08:24:14 +02:00
Compare commits
103 Commits
v0.17.865
...
v0.17.1036
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c2797e132e | ||
![]() |
d34dbcb626 | ||
![]() |
6740c7c40f | ||
![]() |
17fc2d50cf | ||
![]() |
77af202e2c | ||
![]() |
cdbe24dfdf | ||
![]() |
7983bc9a57 | ||
![]() |
381e674ac4 | ||
![]() |
921093934f | ||
![]() |
ca3466050c | ||
![]() |
993116c96f | ||
![]() |
98dad4c169 | ||
![]() |
7789a72ffb | ||
![]() |
4de2124b98 | ||
![]() |
00c1ffa8c6 | ||
![]() |
c1cbede92f | ||
![]() |
52be410655 | ||
![]() |
0b630cee7c | ||
![]() |
86b369ef1e | ||
![]() |
1310206d9e | ||
![]() |
de6c365865 | ||
![]() |
cbd14c2b2d | ||
![]() |
324abe94a3 | ||
![]() |
0126e20984 | ||
![]() |
22ef17fe5b | ||
![]() |
be64500580 | ||
![]() |
47a4f0f422 | ||
![]() |
db6a8d89a8 | ||
![]() |
483a72babd | ||
![]() |
7a94b8809e | ||
![]() |
dd894ed267 | ||
![]() |
f808a071da | ||
![]() |
2d207a482d | ||
![]() |
38849f57f8 | ||
![]() |
ce2e7d8d1a | ||
![]() |
72585f9761 | ||
![]() |
c862fabeb4 | ||
![]() |
5894372c49 | ||
![]() |
d9ed8b981d | ||
![]() |
86f185d345 | ||
![]() |
9e9c56e4c9 | ||
![]() |
853179fd72 | ||
![]() |
0dd5026cb1 | ||
![]() |
35232b1a5c | ||
![]() |
49a507f6a0 | ||
![]() |
ce527439f2 | ||
![]() |
1e07a196df | ||
![]() |
d8d88962c6 | ||
![]() |
5ac8095741 | ||
![]() |
b2c0cb6ca9 | ||
![]() |
62de0458e5 | ||
![]() |
e1aa849315 | ||
![]() |
b7f5c9711a | ||
![]() |
d51581f503 | ||
![]() |
2329d4c59e | ||
![]() |
84689fbdcd | ||
![]() |
7c5af2dbde | ||
![]() |
7a680c3162 | ||
![]() |
642f66de44 | ||
![]() |
1f4210ded6 | ||
![]() |
51400dd8b2 | ||
![]() |
7e5c63ddff | ||
![]() |
0a38b26436 | ||
![]() |
1d8209f2dd | ||
![]() |
8d370318fb | ||
![]() |
46773ca1ee | ||
![]() |
0a49cae6e6 | ||
![]() |
9cce45e193 | ||
![]() |
b848ecf5e6 | ||
![]() |
6ec5563dc9 | ||
![]() |
de4cabb9b6 | ||
![]() |
df5795fc75 | ||
![]() |
d479ea470d | ||
![]() |
0dccfdd5b8 | ||
![]() |
b12c99cf04 | ||
![]() |
7d7b362985 | ||
![]() |
a1dd0c7581 | ||
![]() |
42b6aaedc9 | ||
![]() |
93b55583bf | ||
![]() |
0ded6a489f | ||
![]() |
f22f37e832 | ||
![]() |
4647fcd783 | ||
![]() |
e269ab5d3b | ||
![]() |
e99ac596fb | ||
![]() |
eb6b085b5f | ||
![]() |
7c19985dcc | ||
![]() |
c3c2f053df | ||
![]() |
c80972768b | ||
![]() |
ab5fd85e30 | ||
![]() |
d606989b1b | ||
![]() |
7edbdf3559 | ||
![]() |
d89fa2e80a | ||
![]() |
fed39f9631 | ||
![]() |
365a1ff04f | ||
![]() |
76b89276e1 | ||
![]() |
b4626fce5f | ||
![]() |
5b8db45c62 | ||
![]() |
47da7dbb72 | ||
![]() |
1dbbf6ca3b | ||
![]() |
2dad460e3f | ||
![]() |
4b341a33ac | ||
![]() |
fab3dcdc76 | ||
![]() |
27a5d8f074 |
5
.github/ISSUE_TEMPLATE.md
vendored
5
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,5 +1,8 @@
|
||||
### Read and complete in full with information about your setup and issue before submitting.
|
||||
### Do not delete the template.
|
||||
|
||||
**Please use the search bar** at the top of the page and make sure you are not creating an already submitted issue.
|
||||
Check closed issues as well, because your issue may have already been fixed.
|
||||
Check closed issues as well, because your issue may have already been fixed. Also check our [Troubleshooting](https://github.com/Jackett/Jackett/wiki/Troubleshooting) for steps to resolve common issues.
|
||||
|
||||
Please read our [Contributing Guidelines](https://github.com/Jackett/Jackett/blob/master/CONTRIBUTING.md) before submitting your issue to ensure a prompt response to your bug.
|
||||
|
||||
|
26
README.md
26
README.md
@@ -54,6 +54,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* E-Hentai
|
||||
* emtrek
|
||||
* Epizod
|
||||
* Erai-Raws
|
||||
* ETTV
|
||||
* EXT Torrents
|
||||
* ExtraTorrent.cd
|
||||
@@ -130,7 +131,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* sukebei-Pantsu
|
||||
* sukebei.Nyaa.si
|
||||
* The Pirate Bay (TPB)
|
||||
* Tjangto (짱토)
|
||||
* TNTfork
|
||||
* Tokyo Tosho
|
||||
* Torlock
|
||||
@@ -157,8 +157,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* Torrentv
|
||||
* TorrentView (토렌트뷰)
|
||||
* TorrentWhiz ( 토렌트위즈)
|
||||
* Torrentz2
|
||||
* Torrentz2k
|
||||
* truPornolabs
|
||||
* Underverse
|
||||
* UnionDHT
|
||||
@@ -191,7 +189,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* Erzsebet
|
||||
* Erzsebet.pl
|
||||
* ExKinoRay
|
||||
* ExtremlymTorrents (XTR)
|
||||
* Genesis-Movement
|
||||
* HamsterStudio
|
||||
* HunTorrent
|
||||
@@ -322,7 +319,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* EbookParadijs
|
||||
* Ebooks-Shares
|
||||
* EfectoDoppler
|
||||
* Elite-Tracker
|
||||
* Empornium (EMP)
|
||||
* EpubLibre
|
||||
* eShareNet
|
||||
@@ -343,7 +339,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* FreeTorrent
|
||||
* FunFile (FF)
|
||||
* FunkyTorrents (FT) [![(invite needed)][inviteneeded]](#)
|
||||
* FunReleases [![(invite needed)][inviteneeded]](#)
|
||||
* Fuzer (FZ)
|
||||
* GFXPeers
|
||||
* Galeriens (LaPauseTorrents)
|
||||
@@ -410,6 +405,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* LosslessClub
|
||||
* M-Team TP (MTTP)
|
||||
* MaDs Revolution
|
||||
* magic-heaven
|
||||
* Magico (Trellas)
|
||||
* Majomparádé (TurkDepo)
|
||||
* MeseVilág (Fairytale World)
|
||||
@@ -427,8 +423,10 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* NetCosmo
|
||||
* NetLab
|
||||
* NorBits
|
||||
* Nordic+
|
||||
* Oasis
|
||||
* oMg[WtF]trackr
|
||||
* OpenCD
|
||||
* Orpheus
|
||||
* OshenPT
|
||||
* Ourbits (HDPter)
|
||||
@@ -463,10 +461,12 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* R3V WTF! [![(invite needed)][inviteneeded]](#)
|
||||
* Racing4Everyone (R4E)
|
||||
* RacingForMe (RFM)
|
||||
* RedBits
|
||||
* Red Star Torrent (RST) [![(invite needed)][inviteneeded]](#)
|
||||
* Redacted (PassTheHeadphones)
|
||||
* RetroFlix
|
||||
* RevolutionTT
|
||||
* ROFD
|
||||
* Romanian Metal Torrents (RMT) [![(invite needed)][inviteneeded]](#)
|
||||
* RPTorrents
|
||||
* SceneHD
|
||||
@@ -478,7 +478,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* SeedFile (SF)
|
||||
* Shareisland
|
||||
* Shazbat
|
||||
* Shellife (SL) [![(invite needed)][inviteneeded]](#)
|
||||
* SiamBIT
|
||||
* SnowPT (SSPT)
|
||||
* SoulVoice [![(invite needed)][inviteneeded]](#)
|
||||
@@ -563,7 +562,6 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
|
||||
* XWTorrents (XWT)
|
||||
* Xthor
|
||||
* YDYPT
|
||||
* YingK
|
||||
* Zamunda.net
|
||||
* Zelka.org
|
||||
* ZonaQ
|
||||
@@ -775,11 +773,13 @@ To use it, please just request a free API key on [OMDb](http://www.omdbapi.com/a
|
||||
### Windows
|
||||
* Install the .NET 5 [SDK](https://www.microsoft.com/net/download/windows)
|
||||
* Clone Jackett
|
||||
* Open PowerShell and from the `src` directory, run `dotnet restore`
|
||||
* Open the Jackett solution in Visual Studio 2019 (version 16.4 or above)
|
||||
* Right-click on the Jackett solution and click 'Rebuild Solution' to restore NuGet packages
|
||||
* Select Jackett.Server as the startup project
|
||||
* In the drop-down menu of the run button select "Jackett.Server" instead of "IIS Express"
|
||||
* Open PowerShell and from the `src` directory:
|
||||
* - run `dotnet msbuild /restore`
|
||||
* - then run `dotnet restore`
|
||||
* - and run `dotnet build`
|
||||
* Open the Jackett solution in Visual Studio 2019 (version 16.9 or above)
|
||||
* Select **Jackett.Server** as the startup project
|
||||
* In the drop-down menu of the run button select **Jackett.Server** instead of _IIS Express_
|
||||
* Build/Start the project
|
||||
|
||||
### OSX
|
||||
|
@@ -301,6 +301,14 @@ stages:
|
||||
targetType: inline
|
||||
failOnStderr: true
|
||||
script: |
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/DateTimeRoutines
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Common
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.IntegrationTests
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Server
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Service
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Test
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Tray
|
||||
dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src/Jackett.Updater
|
||||
dotnet-format --check --verbosity diagnostic --folder ./src/DateTimeRoutines
|
||||
dotnet-format --check --verbosity diagnostic --folder ./src/Jackett.Common
|
||||
dotnet-format --check --verbosity diagnostic --folder ./src/Jackett.IntegrationTests
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -375,7 +375,12 @@
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="6"></td>
|
||||
<th>Indexer</th>
|
||||
<th>Categories</th>
|
||||
<th>Type</th>
|
||||
<th>Type string</th>
|
||||
<th>Language</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -693,6 +698,6 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="../libs/api.js?changed=2017083001"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20210322"></script>
|
||||
<script type="text/javascript" src="../custom.js?changed=20210424"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -14,8 +14,8 @@ links:
|
||||
- https://x1337x.ws/
|
||||
- https://x1337x.eu/
|
||||
- https://x1337x.se/
|
||||
- https://1337x.unblockit.club/
|
||||
- https://1337x.unblocked.monster/
|
||||
- https://1337x.unblockit.onl/
|
||||
- https://1337x.nocensor.space/
|
||||
legacylinks:
|
||||
- https://1337x.unblocked.earth/
|
||||
- https://1337x.unblockit.pro/
|
||||
@@ -42,6 +42,8 @@ legacylinks:
|
||||
- https://1337x.unblockit.ltd/
|
||||
- https://1337x.unblockit.link/
|
||||
- https://1337x.unblockit.buzz/
|
||||
- https://1337x.unblocked.monster/
|
||||
- https://1337x.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -75,7 +75,7 @@ search:
|
||||
all_word_seach: 1
|
||||
# 0 article, 1 comments, 2 static pages, 3 article titles
|
||||
titleonly: "{{ if .Keywords }}3{{ else }}0{{ end }}"
|
||||
searchdate: "{{ if .Keywords }}0{{ else }}1{{ end }}"
|
||||
searchdate: 0
|
||||
searchuser: ""
|
||||
story: "{{ if .Keywords }}{{ .Keywords }}{{ else }}{{ .Today.Year }}{{ end }}"
|
||||
sortby: date
|
||||
|
@@ -47,7 +47,7 @@ caps:
|
||||
- {id: 52, cat: TV, desc: "مسرحيات (Plays)"}
|
||||
- {id: 71, cat: TV, desc: "مسلسلات مدبلجه عربي (Arabic Dubbed Series)"}
|
||||
- {id: 90, cat: TV, desc: "برامج ومسابقات (Shows)"}
|
||||
- {id: 109, cat: TV, desc: "رمضان 2020 (Ramadan 2020)"}
|
||||
- {id: 110, cat: TV, desc: "رمضان 2021 (Ramadan 2021)"}
|
||||
# المرئيات الاجنبية Foreign Movies
|
||||
- {id: 92, cat: TV/Foreign, desc: "تعليمي (Educational)"}
|
||||
- {id: 93, cat: TV/Documentary, desc: "وثائقي (Documentary)"}
|
||||
|
@@ -9,7 +9,7 @@ links:
|
||||
- http://audiobookbay.nl/ # site forces http, does not support https
|
||||
- http://audiobookbay.net/
|
||||
- http://audiobookbayabb.com/
|
||||
- https://audiobookbay.unblockit.club/
|
||||
- https://audiobookbay.unblockit.onl/
|
||||
legacylinks:
|
||||
- https://audiobookbay.la/
|
||||
- https://audiobookbay.unblockit.lat/
|
||||
@@ -18,6 +18,7 @@ legacylinks:
|
||||
- https://audiobookbay.unblockit.ltd/
|
||||
- https://audiobookbay.unblockit.link/
|
||||
- https://audiobookbay.unblockit.buzz/
|
||||
- https://audiobookbay.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -217,13 +217,6 @@ search:
|
||||
args: "02.01.2006 15:04:05 -07:00"
|
||||
size:
|
||||
selector: td.size
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["\u00a0", ""]
|
||||
- name: replace
|
||||
args: [".", ""]
|
||||
- name: replace
|
||||
args: [",", "."]
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
"span:contains(\"OU\")": 0 # only upload is counted
|
||||
|
@@ -13,28 +13,29 @@ legacylinks:
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: Movies/UHD, desc: "UHD"}
|
||||
- {id: 2, cat: Movies/HD, desc: "BluRay"}
|
||||
- {id: 8, cat: Movies/HD, desc: "TV"}
|
||||
- {id: 6, cat: Movies/HD, desc: "WEB-DL"}
|
||||
- {id: 16, cat: Movies/HD, desc: "WEBRip"}
|
||||
- {id: 3, cat: Movies/HD, desc: "Movies HD"}
|
||||
- {id: 25, cat: Movies/3D, desc: "Movies 3D"}
|
||||
- {id: 4, cat: Movies/HD, desc: "BluRay"}
|
||||
- {id: 7, cat: Movies/HD, desc: "TV"}
|
||||
- {id: 5, cat: Movies/HD, desc: "WEB-DL"}
|
||||
- {id: 14, cat: Movies/HD, desc: "WEBRip"}
|
||||
- {id: 27, cat: Movies/HD, desc: "LORD Rips"}
|
||||
- {id: 2, cat: Movies/UHD, desc: "UHD - BluRay"}
|
||||
- {id: 8, cat: Movies/UHD, desc: "UHD - TV"}
|
||||
- {id: 6, cat: Movies/UHD, desc: "UHD - WEB-DL"}
|
||||
- {id: 16, cat: Movies/UHD, desc: "UHD - WEBRip"}
|
||||
- {id: 31, cat: Movies/BluRay, desc: "BluRay Disk"}
|
||||
- {id: 3, cat: Movies/HD, desc: "HD"}
|
||||
- {id: 25, cat: Movies/3D, desc: "HD - 3D"}
|
||||
- {id: 4, cat: Movies/HD, desc: "HD - BluRay"}
|
||||
- {id: 7, cat: Movies/HD, desc: "HD - TV"}
|
||||
- {id: 5, cat: Movies/HD, desc: "HD - WEB-DL"}
|
||||
- {id: 14, cat: Movies/HD, desc: "HD - WEBRip"}
|
||||
- {id: 32, cat: PC/Games, desc: "Oyunlar (Games)"}
|
||||
- {id: 9, cat: Movies/SD, desc: "SD"}
|
||||
- {id: 23, cat: Movies/SD, desc: "BluRay"}
|
||||
- {id: 10, cat: Movies/SD, desc: "DVD"}
|
||||
- {id: 12, cat: Movies/SD, desc: "TV"}
|
||||
- {id: 13, cat: Movies/SD, desc: "WEB-DL"}
|
||||
- {id: 15, cat: Movies/SD, desc: "WEBRip"}
|
||||
- {id: 23, cat: Movies/SD, desc: "SD - BluRay"}
|
||||
- {id: 10, cat: Movies/SD, desc: "SD - DVD"}
|
||||
- {id: 36, cat: Movies/SD, desc: "SD - DVDRip"}
|
||||
- {id: 12, cat: Movies/SD, desc: "SD - TV"}
|
||||
- {id: 13, cat: Movies/SD, desc: "SD - WEB-DL"}
|
||||
- {id: 15, cat: Movies/SD, desc: "SD - WEBRip"}
|
||||
- {id: 17, cat: TV, desc: "TV"}
|
||||
- {id: 26, cat: TV/Other, desc: "TV Program"}
|
||||
- {id: 19, cat: TV, desc: "Yabanci Dizi"}
|
||||
- {id: 18, cat: TV, desc: "Yerli Dizi"}
|
||||
- {id: 20, cat: Movies/HD, desc: "VIP"}
|
||||
- {id: 26, cat: TV/Other, desc: "TV - TV Program"}
|
||||
- {id: 19, cat: TV, desc: "TV - Yabanci Dizi"}
|
||||
- {id: 18, cat: TV, desc: "TV - Yerli Dizi"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -9,7 +9,7 @@ followredirect: true
|
||||
links:
|
||||
# update poster placeholder link too
|
||||
- https://btdb.eu/
|
||||
- https://btdb.unblockit.club/
|
||||
- https://btdb.unblockit.onl/
|
||||
legacylinks:
|
||||
- https://btdb.to/
|
||||
- https://btdb.unblocked.app/
|
||||
@@ -35,6 +35,7 @@ legacylinks:
|
||||
- https://btdb.unblockit.ltd/
|
||||
- https://btdb.unblockit.link/
|
||||
- https://btdb.unblockit.buzz/
|
||||
- https://btdb.unblockit.club/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -6,7 +6,7 @@ language: en-us
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://btsow.cam/
|
||||
- https://btsow.digital
|
||||
legacylinks:
|
||||
- https://btos.pw/
|
||||
- https://btio.pw/
|
||||
@@ -23,6 +23,8 @@ legacylinks:
|
||||
- https://btsow.work/
|
||||
- https://btsow.store/
|
||||
- https://btsow.surf/
|
||||
- https://btsow.com/
|
||||
- https://btsow.cam/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -67,6 +67,7 @@ caps:
|
||||
- {id: 153, cat: TV, desc: "TV-Star Plus"}
|
||||
- {id: 154, cat: TV, desc: "TV-Zee TV"}
|
||||
- {id: 186, cat: TV, desc: "TV-Dangal Tv"}
|
||||
- {id: 218, cat: TV, desc: "TV-Ishara TV"}
|
||||
- {id: 155, cat: TV/Sport, desc: "TV-Sports"}
|
||||
- {id: 156, cat: TV/Documentary, desc: "TV-Documentaries"}
|
||||
- {id: 198, cat: Movies, desc: "TV-MTV"}
|
||||
|
@@ -262,10 +262,10 @@ download:
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
# keywords (any age posts, article titles only search)
|
||||
# keywords (article titles only search)
|
||||
# do=search&subaction=search&story=five+freddy&titleonly=3&searchdate=0&sortby=date&resorder=desc&catlist[]=1&catlist[]=2&catlist[]=3
|
||||
# keywordless (1 day old posts or newer + article body search) query=game
|
||||
# do=search&subaction=search&story=game&titleonly=0&searchdate=1&sortby=date&resorder=desc&catlist[]=0
|
||||
# keywordless (article body search) query=game
|
||||
# do=search&subaction=search&story=game&titleonly=0&searchdate=0&sortby=date&resorder=desc&catlist[]=0
|
||||
paths:
|
||||
- path: index.php
|
||||
inputs:
|
||||
@@ -278,7 +278,7 @@ search:
|
||||
showposts: 1
|
||||
# 0 article, 1 comments, 2 static pages, 3 article titles
|
||||
titleonly: "{{ if .Keywords }}3{{ else }}0{{ end }}"
|
||||
searchdate: "{{ if .Keywords }}0{{ else }}1{{ end }}"
|
||||
searchdate: 0
|
||||
story: "{{ if .Keywords }}{{ .Keywords }}{{ else }}game{{ end }}"
|
||||
sortby: date
|
||||
resorder: desc
|
||||
|
@@ -6,20 +6,9 @@ language: en-us
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- http://www.cilipro1.xyz/
|
||||
- http://www.cilipro2.xyz/
|
||||
- http://www.cilipro3.xyz/
|
||||
- http://www.cilipro4.xyz/
|
||||
- http://www.cilipro5.xyz/
|
||||
- http://www.cilipro6.xyz/
|
||||
- http://www.cilipro7.xyz/
|
||||
- http://www.cilipro8.xyz/
|
||||
- http://www.cilipro9.xyz/
|
||||
- http://www.cilipro10.xyz/
|
||||
- http://www.cilinb1.xyz/
|
||||
- http://www.cilinb2.xyz/
|
||||
- http://www.cilinb3.xyz/
|
||||
- http://www.cilinb4.xyz/
|
||||
- http://www.cilinb5.xyz/
|
||||
- http://www.cilinb6.xyz/
|
||||
- http://www.cilinb7.xyz/
|
||||
@@ -80,6 +69,17 @@ legacylinks:
|
||||
- http://www.lrsoso9.xyz/
|
||||
- http://www.lrsoso10.xyz/
|
||||
- http://www.cilijj.xyz/
|
||||
- http://www.cilipro1.xyz/
|
||||
- http://www.cilipro2.xyz/
|
||||
- http://www.cilipro3.xyz/
|
||||
- http://www.cilipro4.xyz/
|
||||
- http://www.cilipro5.xyz/
|
||||
- http://www.cilipro6.xyz/
|
||||
- http://www.cilipro7.xyz/
|
||||
- http://www.cilipro8.xyz/
|
||||
- http://www.cilipro9.xyz/
|
||||
- http://www.cilipro10.xyz/
|
||||
- http://www.cilinb4.xyz/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -122,7 +122,7 @@ search:
|
||||
- name: replace
|
||||
args: ["&w=52&h=80", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/52x80&w=180&h=270", ""]
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/600x900&w=180&h=270", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
|
@@ -10,8 +10,9 @@ links:
|
||||
- https://www.demonoid.is/
|
||||
- https://www.dnoid.to/
|
||||
- https://www.dnoid.pw/
|
||||
- https://demonoid.unblockit.club/
|
||||
- https://demonoid.unblockit.onl/
|
||||
- https://demonoid.torrentbay.to/
|
||||
- https://demonoid.nocensor.space/
|
||||
legacylinks:
|
||||
- https://demonoid.unblockit.pro/
|
||||
- https://demonoid.unblockit.one/
|
||||
@@ -31,6 +32,7 @@ legacylinks:
|
||||
- https://demonoid.unblockit.ltd/
|
||||
- https://demonoid.unblockit.link/
|
||||
- https://demonoid.unblockit.buzz/
|
||||
- https://demonoid.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -51,6 +51,7 @@ caps:
|
||||
- {id: 178, cat: XXX, desc: "Erotic Video Library / Эротические студии (видео)"}
|
||||
- {id: 85, cat: XXX, desc: "Adult Games / Порноигры"}
|
||||
- {id: 83, cat: XXX, desc: "Hentai, Manga & Cartoons / Хентай, Манга и Мультфильм..."}
|
||||
- {id: 252, cat: XXX, desc: "Chinese Porn / Китайское порно"}
|
||||
- {id: 89, cat: XXX, desc: "Japanese Porn / Японское порно"}
|
||||
- {id: 188, cat: XXX, desc: "Erotic Movies, Documentary & Reality / Эротически..."}
|
||||
- {id: 219, cat: XXX, desc: "Cinema / Зарубежный Кинематограф"}
|
||||
@@ -62,7 +63,9 @@ caps:
|
||||
- {id: 213, cat: XXX, desc: "Music (Video) / Музыка (Видео)"}
|
||||
- {id: 212, cat: XXX, desc: "Games / Игры"}
|
||||
- {id: 211, cat: XXX, desc: "Soft / Софт"}
|
||||
- {id: 251, cat: XXX, desc: "International Releases / Иностранные релизы"}
|
||||
- {id: 210, cat: XXX, desc: "Various / Разное"}
|
||||
- {id: 250, cat: XXX, desc: "Cinema (LGBT) / Кинематограф (ЛГБТ)"}
|
||||
- {id: 205, cat: XXX, desc: "Shemale Transsexual / Транссексуалы"}
|
||||
- {id: 204, cat: XXX, desc: "Bisexual / Бисексуалы"}
|
||||
- {id: 206, cat: XXX, desc: "Gay Clips & Movie Scenes / Ролики, SiteRip'ы..."}
|
||||
|
@@ -9,8 +9,8 @@ followredirect: true
|
||||
links:
|
||||
- https://www.ettvcentral.com/
|
||||
- https://ettv.unblockninja.com/
|
||||
- https://ettv.unblockit.club/
|
||||
- https://ettv.unblocked.monster/
|
||||
- https://ettv.unblockit.onl/
|
||||
- https://ettv.nocensor.space/
|
||||
legacylinks:
|
||||
- https://www.ettv.tv/
|
||||
- https://www.ettv.to/
|
||||
@@ -39,6 +39,8 @@ legacylinks:
|
||||
- https://ettv.unblockit.ltd/
|
||||
- https://ettv.unblockit.link/
|
||||
- https://ettv.unblockit.buzz/
|
||||
- https://ettv.unblocked.monster/
|
||||
- https://ettv.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -7,7 +7,8 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://extratorrents.it/
|
||||
- https://extratorrent.unblockit.club/
|
||||
- https://extratorrent.unblockit.onl/
|
||||
- https://extratorrent.nocensor.space/
|
||||
legacylinks:
|
||||
- https://extratorrent.ag/
|
||||
- https://extratorrent.unblockit.app/
|
||||
@@ -16,6 +17,7 @@ legacylinks:
|
||||
- https://extratorrent.unblockit.link/
|
||||
- https://extratorrent2.unblockninja.com/
|
||||
- https://extratorrent.unblockit.buzz/
|
||||
- https://extratorrent.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -1,179 +0,0 @@
|
||||
---
|
||||
id: extremlymtorrents
|
||||
name: ExtremlymTorrents
|
||||
description: "ExtremlymTorrents (XTR) is a Semi-Private tracker for MOVIES / TV / GENERAL"
|
||||
language: en-us
|
||||
type: semi-private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://extremlymtorrents.ws/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 22, cat: Movies/HD, desc: "720p HD"}
|
||||
- {id: 15, cat: Movies/HD, desc: "1080p HD"}
|
||||
- {id: 40, cat: Movies/UHD, desc: "4K UHD 2160p"}
|
||||
- {id: 12, cat: Movies/BluRay, desc: "BluRay"}
|
||||
- {id: 5, cat: Movies/DVD, desc: "DVDRip"}
|
||||
- {id: 16, cat: Movies/3D, desc: "BluRay 3D"}
|
||||
- {id: 13, cat: TV/HD, desc: "HDTV"}
|
||||
- {id: 47, cat: XXX, desc: "Porn UHD 4K -[+18]- xXx"}
|
||||
- {id: 11, cat: XXX, desc: "Porn -[+18]- xXx"}
|
||||
- {id: 50, cat: XXX, desc: "xXx iMAGESET (+18)"}
|
||||
- {id: 41, cat: TV, desc: "TVRip"}
|
||||
- {id: 6, cat: Audio, desc: "Music Mp3 | FLAC"}
|
||||
- {id: 9, cat: TV, desc: "Kidz | Cartoons"}
|
||||
- {id: 8, cat: Books/EBook, desc: "Comics | EBook"}
|
||||
- {id: 10, cat: TV, desc: "TV Episode | Season Complete"}
|
||||
- {id: 27, cat: Movies/DVD, desc: "DVD | PAL | NTSC"}
|
||||
- {id: 25, cat: Movies/WEB-DL, desc: "WEBRip | WEB-DL"}
|
||||
- {id: 35, cat: Movies, desc: "BRRip | BDRip | HDRip"}
|
||||
- {id: 3, cat: PC, desc: "Applications"}
|
||||
- {id: 17, cat: Console/PSP, desc: "PSP | Playstation "}
|
||||
- {id: 30, cat: TV/SD, desc: "PDTV | SDTV"}
|
||||
- {id: 18, cat: Console/PS3, desc: "PS3 | Playstation 3 "}
|
||||
- {id: 46, cat: Console/PS4, desc: "PS4 | PlayStation 4"}
|
||||
- {id: 20, cat: PC/Mobile-iOS, desc: "Iphone iOS"}
|
||||
- {id: 19, cat: PC/Mobile-Android, desc: "Android Apk"}
|
||||
- {id: 21, cat: Movies, desc: "Pack"}
|
||||
- {id: 49, cat: TV/UHD, desc: "TV UHD | 2160p | Episodes"}
|
||||
- {id: 24, cat: Audio/Video, desc: "VideoClip"}
|
||||
- {id: 26, cat: Console/Wii, desc: "Wii Games"}
|
||||
- {id: 31, cat: TV/Documentary, desc: "DOC's"}
|
||||
- {id: 36, cat: Movies, desc: "CAMRip | REC"}
|
||||
- {id: 38, cat: Movies, desc: "TS: TeleSync | HD-TS"}
|
||||
- {id: 48, cat: Audio/Video, desc: "4K | 2160p | Music Video"}
|
||||
- {id: 28, cat: TV/Anime, desc: "Anime | Japanese"}
|
||||
- {id: 43, cat: XXX, desc: "Hentai | Manga"}
|
||||
- {id: 29, cat: PC/0day, desc: "Windows PC"}
|
||||
- {id: 7, cat: PC/Mac, desc: "Mac"}
|
||||
- {id: 23, cat: PC, desc: "Linux"}
|
||||
- {id: 32, cat: PC/Mobile-Other, desc: "GPS Navigation"}
|
||||
- {id: 45, cat: Audio, desc: "Vinyl Rip"}
|
||||
- {id: 2, cat: Console/XBox 360, desc: "XBOX 360"}
|
||||
- {id: 1, cat: PC/Games, desc: "Games PC"}
|
||||
- {id: 14, cat: Other, desc: "Wallpapers"}
|
||||
- {id: 44, cat: Movies, desc: "Bollywood"}
|
||||
- {id: 42, cat: Other/Misc, desc: "X EXTERN ONLY MAGNET"}
|
||||
- {id: 39, cat: TV/Sport, desc: "Sport TV"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: id
|
||||
options:
|
||||
id: created
|
||||
seeders: seeders
|
||||
size: size
|
||||
name: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: account-login.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: span.titlebar:contains("Access Denied")
|
||||
message:
|
||||
selector: td.text
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="account-logout.php"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: torrents-search.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
# 0 all 1 English 2 etc...
|
||||
lang: 0
|
||||
sort: "{{ .Config.sort }}"
|
||||
order: "{{ .Config.type }}"
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["(\\w+)", " +$1"] # prepend + to each word
|
||||
|
||||
rows:
|
||||
selector: table.xtrz > tbody > tr[class^="ttable_col"]
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="torrents.php?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
language:
|
||||
selector: td:nth-last-child(5)
|
||||
description:
|
||||
optional: true
|
||||
selector: img[src="/images/vip-icon.png"]
|
||||
attribute: src
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["/images/vip-icon.png", " VIP ONLY"]
|
||||
title:
|
||||
selector: a[href^="file.php?id="] b
|
||||
filters:
|
||||
- name: append
|
||||
args: " {{ .Result.language }}{{ .Result.description }}"
|
||||
details:
|
||||
selector: a[href^="file.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
poster:
|
||||
selector: a[href^="file.php?id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
date:
|
||||
selector: td:nth-last-child(1)
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00" # auto adjusted by site account profile
|
||||
- name: dateparse
|
||||
args: "02.01.200615:04:05 -07:00"
|
||||
leechers:
|
||||
selector: td:nth-last-child(2)
|
||||
seeders:
|
||||
selector: td:nth-last-child(3)
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="/images/free.png"]: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
minimumseedtime:
|
||||
# 1 day (as seconds = 24 x 60 x 60)
|
||||
text: 86400
|
||||
# engine n/a
|
@@ -12,8 +12,8 @@ links:
|
||||
- https://eztv.tf/
|
||||
- https://eztv.yt/
|
||||
- https://eztv.unblockninja.com/
|
||||
- https://eztv.unblockit.club/
|
||||
- https://eztv.unblocked.monster/
|
||||
- https://eztv.unblockit.onl/
|
||||
- https://eztv.nocensor.space/
|
||||
legacylinks:
|
||||
- https://eztv.ag/ # redirects to .re
|
||||
- https://eztv.it/ # redirects to .re
|
||||
@@ -42,6 +42,8 @@ legacylinks:
|
||||
- https://eztv.unblockit.ltd/
|
||||
- https://eztv.unblockit.link/
|
||||
- https://eztv.unblockit.buzz/
|
||||
- https://eztv.unblocked.monster/
|
||||
- https://eztv.unblockit.club/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -69,7 +69,7 @@ search:
|
||||
- path: "{{ if .Keywords }}index.php?do=search&type=simple&q=abcd1234{{ else }}%D1%81%D0%B5%D1%80%D0%B8%D0%B0%D0%BB/{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: "{{ if .Keywords }}table.torrents tbody tr{{ else }}div[id^=\"post-id-\"]{{ end }}"
|
||||
selector: "{{ if .Keywords }}table.torrents tbody tr:has(td.td-size){{ else }}div[id^=\"post-id-\"]:has(li.meta-size){{ end }}"
|
||||
filters:
|
||||
- name: andmatch
|
||||
|
||||
|
@@ -160,6 +160,8 @@ caps:
|
||||
- {id: 224, cat: Movies, desc: "Чудо в камере №7"}
|
||||
- {id: 227, cat: Movies, desc: "Сначала убили моего отца"}
|
||||
- {id: 228, cat: Movies, desc: "Айла: Дочь войны"}
|
||||
- {id: 229, cat: TV, desc: "Танцы с птицами"}
|
||||
- {id: 230, cat: TV, desc: "Жизнь в цвете"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -15,13 +15,16 @@ caps:
|
||||
- {id: 93, cat: TV/Sport, desc: "sport catch"}
|
||||
- {id: 96, cat: TV/Sport, desc: "sport sport"}
|
||||
- {id: 97, cat: TV/Sport, desc: "sport divers"}
|
||||
- {id: 81, cat: TV/Anime, desc: "Animation Séries"}
|
||||
- {id: 79, cat: Movies/HD, desc: "Animation Film"}
|
||||
- {id: 65, cat: TV/Documentary, desc: "Documentaires Divers"}
|
||||
- {id: 66, cat: TV/Documentary, desc: "Documentaires audio"}
|
||||
- {id: 74, cat: Books/EBook, desc: "Documentaires Ebook"}
|
||||
- {id: 75, cat: Audio/Audiobook, desc: "Documentaires Ebook audio"}
|
||||
- {id: 81, cat: TV/Anime, desc: "Animation Séries"}
|
||||
- {id: 79, cat: Movies/HD, desc: "Animation Film"}
|
||||
- {id: 2, cat: Movies/DVD, desc: "Film DVDRip"}
|
||||
- {id: 99, cat: Movies/HD, desc: "Film Hdrip"}
|
||||
- {id: 98, cat: Movies/WEB-DL, desc: "Film Webrip"}
|
||||
- {id: 86, cat: Movies/UHD, desc: "Film UHD"}
|
||||
- {id: 87, cat: Movies/HD, desc: "Film REMUX"}
|
||||
- {id: 70, cat: Movies/BluRay, desc: "Film BRRiP - BDRiP"}
|
||||
- {id: 69, cat: Movies/DVD, desc: "Film DVD-r"}
|
||||
@@ -32,12 +35,12 @@ caps:
|
||||
- {id: 16, cat: Movies/WEB-DL, desc: "Film Webrip - HDrip"}
|
||||
- {id: 15, cat: Movies/BluRay, desc: "Film Bluray Full"}
|
||||
- {id: 4, cat: Movies/HD, desc: "Film 1080P"}
|
||||
- {id: 86, cat: Movies/UHD, desc: "Film UHD"}
|
||||
- {id: 102, cat: Movies/UHD, desc: "Film HDR"}
|
||||
- {id: 46, cat: TV/HD, desc: "Serie 1080P"}
|
||||
- {id: 48, cat: TV/HD, desc: "Serie Bluray"}
|
||||
- {id: 60, cat: TV/Other, desc: "Serie Divers"}
|
||||
- {id: 72, cat: TV/HD, desc: "Serie BRRiP - BDRiP"}
|
||||
- {id: 73, cat: TV/HD, desc: "Serie BRRiP - BDRiP"}
|
||||
- {id: 101, cat: TV/UHD, desc: "Serie HDR"}
|
||||
- {id: 12, cat: TV/Other, desc: "Serie DVDRip"}
|
||||
- {id: 30, cat: TV/HD, desc: "Serie 720P"}
|
||||
- {id: 23, cat: TV/WEB-DL, desc: "Serie Web"}
|
||||
@@ -162,13 +165,16 @@ search:
|
||||
":contains(\"sport\"):contains(\"catch\")": 93
|
||||
":contains(\"sport\"):contains(\"sport\")": 96
|
||||
":contains(\"sport\"):contains(\"divers\")": 97
|
||||
":contains(\"Animation\"):contains(\"Séries\")": 81
|
||||
":contains(\"Animation\"):contains(\"Film\")": 79
|
||||
":contains(\"Documentaires\"):contains(\"Divers\")": 65
|
||||
":contains(\"Documentaires\"):contains(\"audio\")": 66
|
||||
":contains(\"Documentaires\"):contains(\"Ebook\")": 74
|
||||
":contains(\"Documentaires\"):contains(\"Ebook audio\")": 75
|
||||
":contains(\"Animation\"):contains(\"Séries\")": 81
|
||||
":contains(\"Animation\"):contains(\"Film\")": 79
|
||||
":contains(\"Film\"):contains(\"DVDRip\")": 2
|
||||
":contains(\"Film\"):contains(\"Hdrip\")": 99
|
||||
":contains(\"Film\"):contains(\"Webrip\")": 98
|
||||
":contains(\"Film\"):contains(\"UHD\")": 86
|
||||
":contains(\"Film\"):contains(\"REMUX\")": 87
|
||||
":contains(\"Film\"):contains(\"BRRiP - BDRiP\")": 70
|
||||
":contains(\"Film\"):contains(\"DVD-r\")": 69
|
||||
@@ -179,12 +185,12 @@ search:
|
||||
":contains(\"Film\"):contains(\"Webrip\")": 16
|
||||
":contains(\"Film\"):contains(\"Bluray\")": 15
|
||||
":contains(\"Film\"):contains(\"1080P\")": 4
|
||||
":contains(\"Film\"):contains(\"UHD\")": 86
|
||||
":contains(\"Film\"):contains(\"HDR\")": 102
|
||||
":contains(\"Serie\"):contains(\"1080P\")": 46
|
||||
":contains(\"Serie\"):contains(\"Bluray\")": 48
|
||||
":contains(\"Serie\"):contains(\"Divers\")": 60
|
||||
":contains(\"Serie\"):contains(\"BRRiP - BDRiP\")": 72
|
||||
":contains(\"Serie\"):contains(\"BRRiP - BDRiP\")": 73
|
||||
":contains(\"Serie\"):contains(\"HDR\")": 101
|
||||
":contains(\"Serie\"):contains(\"DVDRip\")": 12
|
||||
":contains(\"Serie\"):contains(\"720P\")": 30
|
||||
":contains(\"Serie\"):contains(\"Web\")": 23
|
||||
|
@@ -1,215 +0,0 @@
|
||||
---
|
||||
id: funreleases
|
||||
name: FunReleases
|
||||
description: "FunReleases is a Private GERMAN site for TV / MOVIES / GENERAL"
|
||||
language: de-de
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://funreleases.me/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 10, cat: Audio/MP3, desc: "Audio Mp3"}
|
||||
- {id: 11, cat: Audio/MP3, desc: "Audio Pack|MP3"}
|
||||
- {id: 12, cat: Audio/Lossless, desc: "Audio Flac"}
|
||||
- {id: 13, cat: Audio/Lossless, desc: "Audio Pack|Flac"}
|
||||
- {id: 14, cat: Audio, desc: "Audio Hörspiel"}
|
||||
- {id: 15, cat: Audio/Audiobook, desc: "Audio Audiobook"}
|
||||
- {id: 16, cat: Audio/Video, desc: "Audio Videoclip"}
|
||||
- {id: 17, cat: Audio/Other, desc: "Audio Sonstiges"}
|
||||
- {id: 18, cat: Movies/3D, desc: "Filme 3D"}
|
||||
- {id: 19, cat: Movies/BluRay, desc: "Filme Bluray"}
|
||||
- {id: 20, cat: Movies/DVD, desc: "Filme DVD-R"}
|
||||
- {id: 21, cat: Movies/UHD, desc: "Filme HD|4K"}
|
||||
- {id: 22, cat: Movies/HD, desc: "Filme HD|1080p"}
|
||||
- {id: 23, cat: Movies/HD, desc: "Filme HD|720p"}
|
||||
- {id: 24, cat: Movies/SD, desc: "Filme SD"}
|
||||
- {id: 25, cat: Movies/SD, desc: "Filme XviD"}
|
||||
- {id: 26, cat: Movies, desc: "Filme Remux"}
|
||||
- {id: 27, cat: Movies/Foreign, desc: "Filme International"}
|
||||
- {id: 28, cat: Movies/Other, desc: "Filme Sonstiges"}
|
||||
- {id: 29, cat: TV/HD, desc: "Serien HD"}
|
||||
- {id: 30, cat: TV/HD, desc: "Serien Pack|HD"}
|
||||
- {id: 31, cat: TV/SD, desc: "Serien SD"}
|
||||
- {id: 32, cat: TV/SD, desc: "Serien Pack|SD"}
|
||||
- {id: 33, cat: TV, desc: "Serien TV/Shows"}
|
||||
- {id: 34, cat: TV/Foreign, desc: "Serien International"}
|
||||
- {id: 35, cat: TV/Other, desc: "Serien Sonstiges"}
|
||||
- {id: 36, cat: TV/Documentary, desc: "Doku HD"}
|
||||
- {id: 37, cat: TV/Documentary, desc: "Doku Pack|HD"}
|
||||
- {id: 38, cat: TV/Documentary, desc: "Doku SD"}
|
||||
- {id: 39, cat: TV/Documentary, desc: "Doku Pack|SD"}
|
||||
- {id: 40, cat: TV/Documentary, desc: "Doku International"}
|
||||
- {id: 41, cat: TV/Documentary, desc: "Doku Sonstiges"}
|
||||
- {id: 42, cat: PC/Games, desc: "Games Windows"}
|
||||
- {id: 43, cat: Console/PSP, desc: "Games Playstation"}
|
||||
- {id: 44, cat: Console/XBox, desc: "Games XBox"}
|
||||
- {id: 45, cat: Console/NDS, desc: "Games Nintendo"}
|
||||
- {id: 46, cat: PC/Games, desc: "Games Macintosh"}
|
||||
- {id: 47, cat: Console/Other, desc: "Games Sonstiges"}
|
||||
- {id: 48, cat: PC/0day, desc: "Software Windows"}
|
||||
- {id: 49, cat: PC/Mac, desc: "Software Macintosh"}
|
||||
- {id: 50, cat: PC/Mobile-Other, desc: "Software Handy-PDA"}
|
||||
- {id: 51, cat: PC, desc: "Software Linux"}
|
||||
- {id: 52, cat: PC, desc: "Software Sonstiges"}
|
||||
- {id: 53, cat: Books/EBook, desc: "Ebook Buch"}
|
||||
- {id: 54, cat: Books/Comics, desc: "Ebook Comic"}
|
||||
- {id: 55, cat: Books/Mags, desc: "Ebook Magazin"}
|
||||
- {id: 56, cat: Books, desc: "Ebook Zeitung"}
|
||||
- {id: 57, cat: Books/Other, desc: "Ebook Sonstiges"}
|
||||
- {id: 58, cat: TV/Sport, desc: "Sport Fußball"}
|
||||
- {id: 59, cat: TV/Sport, desc: "Sport Formel 1"}
|
||||
- {id: 60, cat: TV/Sport, desc: "Sport Wrestling"}
|
||||
- {id: 61, cat: TV/Sport, desc: "Sport Sonstiges"}
|
||||
- {id: 62, cat: XXX, desc: "XXX HD"}
|
||||
- {id: 63, cat: XXX, desc: "XXX Pack|HD"}
|
||||
- {id: 64, cat: XXX, desc: "XXX SD"}
|
||||
- {id: 65, cat: XXX, desc: "XXX Pack|SD"}
|
||||
- {id: 66, cat: XXX, desc: "XXX Bilder"}
|
||||
- {id: 67, cat: XXX, desc: "XXX Sonstiges"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: 3
|
||||
options:
|
||||
3: created
|
||||
5: seeders
|
||||
4: size
|
||||
2: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: 2
|
||||
options:
|
||||
2: desc
|
||||
1: asc
|
||||
- name: info_tpp
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile. The default is <i>15</i>.
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: body[onLoad^="makeAlert('"]
|
||||
message:
|
||||
selector: body[onLoad^="makeAlert('"]
|
||||
attribute: onLoad
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["makeAlert('Error' , '", ""]
|
||||
- name: replace
|
||||
args: ["');", ""]
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="logout.php"]
|
||||
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: index.php
|
||||
inputs:
|
||||
search: "{{ .Keywords }}"
|
||||
category: "{{ range .Categories }}{{.}};{{end}}"
|
||||
page: torrents
|
||||
# 0 all, 1 activeonly, 2 deadonly
|
||||
active: 0
|
||||
# 0 name, 1 name&descr, 2 descr, 3 uploader, 5 gold, 6 silver, 7 bronze, 8 1xUL, 9 2x, 10 3x, 11 4x, 12 5x, 13 6x, 14 7x, 15 8x, 16 9x, 17 10x
|
||||
options: "{{ if .Config.freeleech }}5{{ else }}0{{ end }}"
|
||||
order: "{{ .Config.sort }}"
|
||||
by: "{{ .Config.type }}"
|
||||
# does not return imdb link in results, and while a few titles have an imdbid in descr the majority do not.
|
||||
|
||||
rows:
|
||||
selector: div.b-content > table > tbody > tr > td > table.lista > tbody > tr:has(a[href^="index.php?page=torrent-details&id="])
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
details:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="index.php?page=downloadcheck&id="]
|
||||
attribute: href
|
||||
poster:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
date:
|
||||
selector: td:nth-last-child(9)
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00" # auto adjusted by site account profile
|
||||
- name: dateparse
|
||||
args: "02/01/2006 -07:00"
|
||||
seeders:
|
||||
selector: td:nth-last-child(8)
|
||||
leechers:
|
||||
selector: td:nth-last-child(7)
|
||||
grabs:
|
||||
selector: td:nth-last-child(6)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="images/gold.gif"]: 0
|
||||
img[src="images/silver.gif"]: 0.5
|
||||
img[src="images/bronze.gif"]: 0.75
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[src="images/2x.gif"]: 2
|
||||
img[src="images/3x.gif"]: 3
|
||||
img[src="images/4x.gif"]: 4
|
||||
img[src="images/5x.gif"]: 5
|
||||
img[src="images/6x.gif"]: 6
|
||||
img[src="images/7x.gif"]: 7
|
||||
img[src="images/8x.gif"]: 8
|
||||
img[src="images/9x.gif"]: 9
|
||||
img[src="images/10x.gif"]: 10
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 0.5
|
||||
minimumseedtime:
|
||||
# 2 days (as seconds = 2 x 24 x 60 x 60)
|
||||
text: 172800 # rules say you must seed but not for how long
|
||||
# xbtitFM 3.0.00
|
@@ -7,6 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.gamestorrents.nu/
|
||||
- https://gamestorrents.nocensor.space/
|
||||
legacylinks:
|
||||
- https://www.gamestorrents.com/
|
||||
- https://www.gamestorrents.tv/
|
||||
|
@@ -28,6 +28,7 @@ caps:
|
||||
- {id: 22, cat: Movies/SD, desc: "Movie BDRip"}
|
||||
- {id: 23, cat: Movies/SD, desc: "Movie DvdRip"}
|
||||
- {id: 95, cat: Movies/SD, desc: "Movie WEBRip"}
|
||||
- {id: 98, cat: Movies/WEB-DL, desc: "Movie WEBDL"}
|
||||
- {id: 24, cat: Movies/DVD, desc: "Movie DVD-R 5"}
|
||||
- {id: 25, cat: Movies/DVD, desc: "Movie DVD-R 9"}
|
||||
- {id: 26, cat: Movies/HD, desc: "Movie Blu-Ray HD"}
|
||||
|
@@ -8,6 +8,7 @@ encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://www.gktorrent.cc/
|
||||
- https://gktorrent.nocensor.space/
|
||||
legacylinks:
|
||||
- https://www.gktorrent.com/
|
||||
- http://www.gktorrent.com/
|
||||
|
@@ -9,8 +9,8 @@ followredirect: true
|
||||
links:
|
||||
- https://www.gtdb.to/
|
||||
- https://glodls.to/
|
||||
- https://glotorrents.unblockit.club/
|
||||
- https://glodls.unblocked.monster/
|
||||
- https://glotorrents.unblockit.onl/
|
||||
- https://glotorrents.nocensor.space/
|
||||
legacylinks:
|
||||
- https://glodls.rocks/
|
||||
- https://glotorrents.unblockit.pro/
|
||||
@@ -36,6 +36,8 @@ legacylinks:
|
||||
- https://glotorrents.unblockit.ltd/
|
||||
- https://glotorrents.unblockit.link/
|
||||
- https://glotorrents.unblockit.buzz/
|
||||
- https://glodls.unblocked.monster/
|
||||
- https://glotorrents.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -162,6 +162,8 @@ search:
|
||||
img.pro_free2up: 2
|
||||
img.pro_2up: 2
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 0.8
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, img
|
||||
|
@@ -78,7 +78,18 @@ search:
|
||||
- name: append
|
||||
args: "]"
|
||||
title:
|
||||
selector: td.titulo a[id]
|
||||
selector: td.titulo a[id]:contains("VOSE")
|
||||
optional: true
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Result.extras }} "
|
||||
- name: append
|
||||
args: " English"
|
||||
- name: re_replace
|
||||
args: ["(?i)T(\\d{1,2})\\b", "S$1"]
|
||||
title:
|
||||
selector: td.titulo a[id]:not(:contains("VOSE"))
|
||||
optional: true
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "{{ .Result.extras }} "
|
||||
|
@@ -162,6 +162,8 @@ search:
|
||||
img.pro_free2up: 2
|
||||
img.pro_2up: 2
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 0.8
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, img
|
||||
|
@@ -6,9 +6,10 @@ language: tr-TR
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://hdturk.de/
|
||||
- https://hdturk.club/
|
||||
legacylinks:
|
||||
- http://hdturk.de/
|
||||
- https://hdturk.de/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -7,7 +7,6 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://idope.se/
|
||||
- https://idope.unblocked.monster/
|
||||
legacylinks:
|
||||
- https://idope.black-mirror.xyz/
|
||||
- https://idope.unblocked.casa/
|
||||
@@ -18,6 +17,7 @@ legacylinks:
|
||||
- https://idope.proxyportal.pw/
|
||||
- https://idope.uk-unblock.pro/
|
||||
- https://idope.unblocked.rest/
|
||||
- https://idope.unblocked.monster/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -6,11 +6,12 @@ language: it-it
|
||||
type: semi-private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://ilcorsaroblu.org/
|
||||
- https://ilcorsaroblu.online/
|
||||
legacylinks:
|
||||
- http://ilcorsaroblu.org/
|
||||
- https://www.ilcorsaroblu.info/
|
||||
- https://www.ilcorsaroblu.org/
|
||||
- https://ilcorsaroblu.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -11,6 +11,7 @@ links:
|
||||
- https://ilcorsaronero.fun/
|
||||
- https://ilcorsaronero.pro/
|
||||
- https://ilcorsaronero.torrentbay.to/
|
||||
- https://ilcorsaronero.nocensor.space/
|
||||
legacylinks:
|
||||
- https://ilcorsaronero.live/
|
||||
- https://ilcorsaronero.vip/
|
||||
@@ -46,7 +47,7 @@ caps:
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
tv-search: [q]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
@@ -62,10 +63,11 @@ search:
|
||||
- path: "{{ if .Keywords }}advsearch.php?&category={{ range .Categories }}{{.}};{{end}}&search={{ .Keywords }}&order=data&by=DESC&page=1{{ else }}/browse/1{{ end }}"
|
||||
- path: "{{ if .Keywords }}advsearch.php?&category={{ range .Categories }}{{.}};{{end}}&search={{ .Keywords }}&order=data&by=DESC&page=2{{ else }}/browse/2{{ end }}"
|
||||
keywordsfilters:
|
||||
- name: re_replace # S01 to 1
|
||||
args: ["(?i)\\bS0*(\\d+)\\b", "$1"]
|
||||
- name: re_replace # S01E01 to 1 1
|
||||
args: ["(?i)\\bS0*(\\d+)E0*(\\d+)\\b", "$1 $2"]
|
||||
# remove searching by season and episode due to inconsistent naming #11471
|
||||
- name: re_replace # remove S01
|
||||
args: ["(?i)(\\s*s\\d+)", ""]
|
||||
- name: re_replace # remove E01
|
||||
args: ["(?i)(\\s*e\\d+)", ""]
|
||||
|
||||
rows:
|
||||
selector: "tr.odd,tr.odd2"
|
||||
|
@@ -117,6 +117,8 @@ search:
|
||||
args: ["&w=52&h=80", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/52x80&w=180&h=270", ""]
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/600x900&w=180&h=270", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
|
@@ -27,7 +27,8 @@ caps:
|
||||
- {id: 15, cat: XXX, desc: "Pregnant"}
|
||||
- {id: 16, cat: XXX, desc: "Fetish"}
|
||||
- {id: 9, cat: XXX, desc: "Gay Forum"}
|
||||
- {id: 18, cat: XXX, desc: "Incest and Taboo"}
|
||||
- {id: 18, cat: XXX, desc: "Incest & Taboo"}
|
||||
- {id: 19, cat: XXX, desc: "Femdom & Strapon"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -12,8 +12,9 @@ links:
|
||||
- https://www.limetorrents.co/
|
||||
- https://limetor.com/
|
||||
- https://www.limetor.pro/
|
||||
- https://limetorrents.unblockit.club/
|
||||
- https://limetorrents.unblockit.onl/
|
||||
- https://limetorrents.unblockninja.com/
|
||||
- https://limetorrents.nocensor.space/
|
||||
legacylinks:
|
||||
- https://www.limetorrents.io/
|
||||
- https://www.limetorrents.cc/
|
||||
@@ -40,6 +41,7 @@ legacylinks:
|
||||
- https://limetorrents.unblockit.ltd/
|
||||
- https://limetorrents.unblockit.link/
|
||||
- https://limetorrents.unblockit.buzz/
|
||||
- https://limetorrents.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
203
src/Jackett.Common/Definitions/magicheaven.yml
Normal file
203
src/Jackett.Common/Definitions/magicheaven.yml
Normal file
@@ -0,0 +1,203 @@
|
||||
---
|
||||
id: magicheaven
|
||||
name: magic-heaven
|
||||
description: "magic-heaven is a Private Torrent Tracker for MOVIES / TV / GENERAL"
|
||||
language: en
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://magic-heaven.info/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 6, cat: PC, desc: "APPS"}
|
||||
- {id: 108, cat: Other, desc: "APPS Wallpapers"}
|
||||
- {id: 8, cat: Movies/BluRay, desc: "BLU-RAY"}
|
||||
- {id: 172, cat: Movies/BluRay, desc: "BLU-RAY 1080p"}
|
||||
- {id: 176, cat: Movies/BluRay, desc: "BLU-RAY 720p"}
|
||||
- {id: 156, cat: Movies/BluRay, desc: "BLU-RAY H264"}
|
||||
- {id: 153, cat: Movies/BluRay, desc: "BLU-RAY X264"}
|
||||
- {id: 36, cat: Movies/SD, desc: "CAMS"}
|
||||
- {id: 161, cat: Movies/Other, desc: "Christmas Movies"}
|
||||
- {id: 164, cat: Movies/Other, desc: "Christmas Music"}
|
||||
- {id: 160, cat: Movies/Other, desc: "Classic Movies"}
|
||||
- {id: 169, cat: Console, desc: "Console Games"}
|
||||
- {id: 175, cat: Console/XBox, desc: "Console Games XBOX"}
|
||||
- {id: 82, cat: Audio/Other, desc: "DJ MUSIC"}
|
||||
- {id: 84, cat: Audio/Other, desc: "DJ MUSIC DMC"}
|
||||
- {id: 102, cat: Audio/Other, desc: "DJ MUSIC Full Tilt Remix"}
|
||||
- {id: 92, cat: Audio/Other, desc: "DJ MUSIC Funkymix"}
|
||||
- {id: 83, cat: Audio/Other, desc: "DJ MUSIC MASTERMIX"}
|
||||
- {id: 93, cat: Audio/Other, desc: "DJ MUSIC Ultimix"}
|
||||
- {id: 90, cat: Audio/Other, desc: " DJ MUSIC X-mix"}
|
||||
- {id: 3, cat: Movies/DVD, desc: "DVD-R"}
|
||||
- {id: 11, cat: Books/EBook, desc: "E BOOKS"}
|
||||
- {id: 114, cat: Audio/Audiobook, desc: "E BOOKS Audio Books"}
|
||||
- {id: 86, cat: Books/Mags, desc: "E BOOKS Magazines"}
|
||||
- {id: 167, cat: Audio/Other, desc: "KARAOKE"}
|
||||
- {id: 14, cat: Movies/Other, desc: "MKV's"}
|
||||
- {id: 13, cat: Audio/MP3, desc: "MP3 RELEASES"}
|
||||
- {id: 17, cat: Movies/Other, desc: "MP4"}
|
||||
- {id: 18, cat: Movies/Other, desc: "MULTI'S"}
|
||||
- {id: 98, cat: Movies/Other, desc: "MULTI'S Movie Box Sets"}
|
||||
- {id: 20, cat: Audio/MP3, desc: "MUSIC - MP3"}
|
||||
- {id: 170, cat: Audio, desc: "MUSIC Artist Albums"}
|
||||
- {id: 150, cat: Audio, desc: "MUSIC Collections"}
|
||||
- {id: 96, cat: Audio, desc: "MUSIC Discographys"}
|
||||
- {id: 97, cat: Audio/Lossless, desc: "MUSIC Flac"}
|
||||
- {id: 171, cat: Audio/Video, desc: "Music-Videos"}
|
||||
- {id: 22, cat: PC/Games, desc: "PC GAMES"}
|
||||
- {id: 81, cat: Movies, desc: "RatioBoosters"}
|
||||
- {id: 26, cat: TV/Sport, desc: "SPORT"}
|
||||
- {id: 7, cat: Audio, desc: "Top 40 Albums/Singles"}
|
||||
- {id: 9, cat: TV, desc: "TV EPISODES"}
|
||||
- {id: 38, cat: TV, desc: "TV SERIES"}
|
||||
- {id: 30, cat: Other, desc: "VIP"}
|
||||
- {id: 158, cat: Other, desc: "VIP GOLD"}
|
||||
- {id: 173, cat: Movies/WEB-DL, desc: "Web-dl"}
|
||||
- {id: 34, cat: Movies/SD, desc: "Xvid / HDrip"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Filter freeleech only
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: added
|
||||
options:
|
||||
added: created
|
||||
seeders: seeders
|
||||
size: size
|
||||
name: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
logout: ""
|
||||
error:
|
||||
- selector: table:has(a[href*="login.php?error=1"])
|
||||
message:
|
||||
text: "ERROR: Incorrect username!"
|
||||
- selector: table:has(a[href*="login.php?error=4"])
|
||||
message:
|
||||
text: "ERROR: Incorrect password!"
|
||||
- selector: table:has(a[href*="login.php?error="])
|
||||
message:
|
||||
text: "ERROR: Something went wrong during login!"
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href*="/logout.php?logouthash="]
|
||||
|
||||
download:
|
||||
before:
|
||||
path: takethanks.php
|
||||
method: post
|
||||
inputs:
|
||||
torrentid: "{{ .DownloadUri.Query.id }}"
|
||||
selector: a[href*="/download.php?id="]
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
do: search
|
||||
# does not support multi categories so defaulting to ALL
|
||||
category: 0
|
||||
# yes incldead, no activeonly
|
||||
include_dead_torrents: yes
|
||||
keywords: "{{ .Keywords }}"
|
||||
# t_name, t_description, t_both, t_uploader, t_genre
|
||||
search_type: t_name
|
||||
sort: "{{ .Config.sort }}"
|
||||
order: "{{ .Config.type }}"
|
||||
# does not return imdb link in results, does not support imdbid searches
|
||||
keywordsfilters:
|
||||
- name: re_replace # replace special characters with "%" (wildcard)
|
||||
args: ["[^a-zA-Z0-9]+", "%"]
|
||||
|
||||
rows:
|
||||
selector: "table#sortabletable tbody tr:has(a[href*=\"download.php?id=\"]){{ if .Config.freeleech }}:has(img[src$=\"/freedownload.gif\"]){{ else }}{{ end }}"
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="browse.php?category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
title:
|
||||
# is often abbreviated
|
||||
selector: a[href*="details.php?id="]
|
||||
title:
|
||||
# usually longer than details title
|
||||
selector: div[id^="port-content-"] div
|
||||
optional: true
|
||||
details:
|
||||
selector: a[href*="details.php?id="]
|
||||
attribute: href
|
||||
poster:
|
||||
selector: div[id^="port-content-"] img
|
||||
attribute: src
|
||||
date:
|
||||
selector: td:nth-child(2) > div:last-child
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d{2}-\d{2}-\d{4} \d{2}:\d{2})
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00" # auto adjusted by site account profile
|
||||
- name: dateparse
|
||||
args: "02-01-2006 15:04 -07:00"
|
||||
download:
|
||||
selector: a[href*="details.php?id="]
|
||||
attribute: href
|
||||
magnet:
|
||||
selector: a[href^="magnet:?xt="]
|
||||
attribute: href
|
||||
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:
|
||||
img[src$="/freedownload.gif"]: 0
|
||||
img[src$="/silverdownload.gif"]: 0.5
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img[src$="/x2.gif"]: 2
|
||||
"*": 1
|
||||
minimumseedtime:
|
||||
# 1 day (as seconds = 24 x 60 x 60)
|
||||
text: 86400
|
||||
# TS Special Edition v.7.5
|
@@ -7,7 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://magnet4you.me/
|
||||
- https://magnet4you.unblocked.monster/
|
||||
- https://magnet4you.nocensor.space/
|
||||
legacylinks:
|
||||
- http://magnet4you.me/
|
||||
- https://magnet4you.black-mirror.xyz/
|
||||
@@ -19,6 +19,7 @@ legacylinks:
|
||||
- https://magnet4you.proxyportal.pw/
|
||||
- https://magnet4you.uk-unblock.pro/
|
||||
- https://magnet4you.unblocked.rest/
|
||||
- https://magnet4you.unblocked.monster/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -28,8 +28,9 @@ caps:
|
||||
- {id: 14, cat: PC, desc: "Maritime Software-Maritime Simulators"}
|
||||
- {id: 12, cat: PC, desc: "Maritime Software-Answers to tests"}
|
||||
- {id: 20, cat: PC, desc: "Maritime Software-Calculation soft"}
|
||||
- {id: 188, cat: PC, desc: "Maritime Software-Other soft"}
|
||||
- {id: 179, cat: PC, desc: "Maritime Software-Programming and SDK software"}
|
||||
- {id: 317, cat: PC, desc: "Maritime Software-Shareware (demo, trial)"}
|
||||
- {id: 188, cat: PC, desc: "Maritime Software-Other soft"}
|
||||
- {id: 21, cat: Books, desc: "Deckofficer's Library"}
|
||||
- {id: 283, cat: Books, desc: "Deckofficer's Library-Radar at sea"}
|
||||
- {id: 114, cat: Books, desc: "Deckofficer's Library-Watch keeping"}
|
||||
|
211
src/Jackett.Common/Definitions/nordicplus.yml
Normal file
211
src/Jackett.Common/Definitions/nordicplus.yml
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
id: nordicplus
|
||||
name: Nordic+
|
||||
description: "Nordic+ is a Private Torrent Tracker for MOVIES / TV / GENERAL"
|
||||
language: en
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://nordicplus.org/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: Movies, desc: "Movies"}
|
||||
- {id: 2, cat: TV, desc: "TV"}
|
||||
- {id: 3, cat: Audio, desc: "Music"}
|
||||
- {id: 4, cat: Console, desc: "Games"}
|
||||
- {id: 5, cat: PC, desc: "Apps"}
|
||||
- {id: 6, cat: Books, desc: "Books"}
|
||||
- {id: 11, cat: Movies/Other, desc: "Movies Boxset"}
|
||||
- {id: 12, cat: Movies/Foreign, desc: "Movies Non Nordic"}
|
||||
- {id: 13, cat: TV/Anime, desc: "Movies Kids"}
|
||||
- {id: 14, cat: TV/Other, desc: "TV Boxset"}
|
||||
- {id: 15, cat: TV/Foreign, desc: "TV Non Nordic"}
|
||||
- {id: 16, cat: TV/Anime, desc: "TV Kids"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid, tvdbid]
|
||||
movie-search: [q, imdbid, tmdbid]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: usemagnet
|
||||
type: checkbox
|
||||
label: "Use Magnet Download Link"
|
||||
default: false
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: created_at
|
||||
options:
|
||||
created_at: created
|
||||
seeders: seeders
|
||||
size: size
|
||||
name: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: login
|
||||
method: form
|
||||
form: form[action$="/login"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
remember: on
|
||||
selectorinputs:
|
||||
_token:
|
||||
selector: input[name="_token"]
|
||||
attribute: value
|
||||
error:
|
||||
- selector: div#ERROR_COPY
|
||||
# test:
|
||||
# path: /
|
||||
# selector: a[href$="/logout"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: torrents/filter
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}categories[]={{.}}&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ else }}{{ .Keywords }}{{ end }}" # for dashboard imdbid search
|
||||
description: ""
|
||||
keywords: ""
|
||||
uploader: ""
|
||||
imdb: "{{ .Query.IMDBIDShort }}"
|
||||
tvdb: "{{ .Query.TVDBID }}"
|
||||
tmdb: "{{ .Query.TMDBID }}"
|
||||
mal: ""
|
||||
igdb: ""
|
||||
start_year: ""
|
||||
end_year: ""
|
||||
sorting: "{{ .Config.sort }}"
|
||||
direction: "{{ .Config.type }}"
|
||||
qty: 100
|
||||
page: 0
|
||||
view: list
|
||||
freeleech: "{{ if .Config.freeleech }}1{{ else }}{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: table > tbody > tr
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="/categories/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/categories/(\\d+)"
|
||||
title:
|
||||
selector: a[href*="/torrents/"]
|
||||
details:
|
||||
selector: a[href*="/torrents/"]
|
||||
attribute: href
|
||||
download1:
|
||||
selector: a[href*="/download/"]
|
||||
attribute: href
|
||||
download2:
|
||||
selector: a[href^="magnet:?"]
|
||||
attribute: href
|
||||
download:
|
||||
text: "{{ if .Config.usemagnet }}{{ .Result.download2 }}{{ else }}{{ .Result.download1 }}{{ end }}"
|
||||
poster:
|
||||
selector: div.torrent-poster img
|
||||
attribute: src
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["&w=60&h=90", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["/img/NoPoster.png", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
selector: td:nth-last-child(3)
|
||||
leechers:
|
||||
selector: td:nth-last-child(2)
|
||||
grabs:
|
||||
selector: td:nth-last-child(1)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
imdb:
|
||||
selector: a[href*="imdb.com/title/tt"]
|
||||
attribute: href
|
||||
tmdbid:
|
||||
selector: a[href*="themoviedb.org/movie/"]
|
||||
attribute: href
|
||||
date:
|
||||
selector: time
|
||||
filters:
|
||||
# translations for Turkish|Estonian|Danish|Italian|Polish|Norwegian|Portuguese|Czech|Russian|Romanian|Spanish|French|German|Bulgarian|Dutch|Chinese|Japanese|Swedish
|
||||
- name: re_replace
|
||||
args: ["(?i)(önce|tagasi|geleden|fa|temu|siden|há|atrás|nazpět|назад|acum|în urmă|hace|il y a|vor|преди|前|sedan)", " ago"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saniye|sekundit|sekunder|secondi|sekund|segundos|sekundami|секунд|secunde|secondes|Sekunden|секунди|seconden|秒前)", "seconds"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(minutit|minutter|minuti|minuty|minutos|минуты|минут|Minuten|минути|minuten|minuter)", "minutes"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(dakika|minut|minuto|minuta|minutt|минута|Minute|minuut|分钟|分)", " minute"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(tundi|timer|ore|godziny|horas|hodiny|hoden|часа|часов|ore|heures|Stunden|timmar)", "hours"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saat|tund|time|ora|godzina|hora|hodina|час|oră|heure|Stunde|uur|小时|時間|timme)", " hour"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(päeva|dage|giorni|dni|dias|dny|дня|дней|zile|días|jours|Tagen|дни|dagen|dagar)", "days"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(gün|päev|dag|giorno|dzień|dia|den|день|zi|día|jour|Tag|ден|天|日)", " day"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(nädalat|uger|settimane|tygodnie|uker|semanas|týdny|недели|недель|săptămâni|semaines|Wochen|седмици|weken|veckor)", "weeks"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(hafta|nädal|uge|settimana|tydzień|uke|semana|týden|неделю|săptămână|semaine|Woche|седмица|周|週間|vecka)", " week"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (ay)", "month"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(kuud|måneder|mesi|miesiące|meses|měsíce|месяца|месяцев|luni|meses|mois|Monaten|месеца|maanden|månader)", "months"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(kuu|måned|mese|miesiąc|mês|měsíc|месяц|lună|mes|Monat|месец|maand|个月|ヶ月|månad)", " month"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(aastat|anni|lata|anos|roky|года|ani|años|ans|Jahren|години)", " years"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(yil|aasta|år|anno|rok|ano|год|año|Jahr|година|jaar|年)", " year"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (an)", "year"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(För |und)", ""] # Misc removals
|
||||
- name: timeago
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
i[class*="fa-id-badge text-orange"]: 0 # 24 Hour FreeLeech From BON Store
|
||||
i[class*="fa-trophy text-purple"]: 0 # Special FreeLeech For Certain User Groups
|
||||
i[class*="fa-star text-bold"]: 0 # Freeleech From Token
|
||||
i[class*="fa-coins text-bold"]: 0 # Freeleech From Token
|
||||
i[class*="fa-globe text-blue"]: 0 # Global Freeleech
|
||||
i[class*="fa-star text-gold"]: 0 # Freeleech
|
||||
i[class*="fa-certificate text-pink"]: 0 # Featured Torrent
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
i[class*="fa-gem text-green"]: 2 # Single Torrent Double Upload
|
||||
i[class*="fa-globe text-green"]: 2 # Global Double Upload
|
||||
i[class*="fa-certificate text-pink"]: 2 # Featured Torrent
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 0.4
|
||||
# UNIT3D 5.2.0
|
@@ -7,6 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://nyaa.net/
|
||||
- https://nyaapantsu.nocensor.space/
|
||||
legacylinks:
|
||||
- https://nyaa.pantsu.cat/
|
||||
- https://nyaa.pt/
|
||||
|
195
src/Jackett.Common/Definitions/opencd.yml
Normal file
195
src/Jackett.Common/Definitions/opencd.yml
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
id: opencd
|
||||
name: OpenCD
|
||||
description: "OpenCD is a CHINESE Private Torrent Tracker for MUSIC"
|
||||
language: zh-cn
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://open.cd/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 2, cat: Audio, desc: "华语流行(Pop)"}
|
||||
- {id: 3, cat: Audio, desc: "古典音乐(Classical)"}
|
||||
- {id: 11, cat: Audio, desc: "民族音乐(Instrumental)"}
|
||||
- {id: 4, cat: Audio, desc: "原声配乐(OST)"}
|
||||
- {id: 5, cat: Audio, desc: "泛摇滚乐(Rock)"}
|
||||
- {id: 8, cat: Audio, desc: "爵士乡村(Jazz)"}
|
||||
- {id: 12, cat: Audio, desc: "新世纪(NewAge)"}
|
||||
- {id: 13, cat: Audio, desc: "舞曲(Dance)"}
|
||||
- {id: 14, cat: Audio, desc: "电子(Electronic)"}
|
||||
- {id: 15, cat: Audio, desc: "民谣(Folk)"}
|
||||
- {id: 16, cat: Audio, desc: "独立(Indie)"}
|
||||
- {id: 17, cat: Audio, desc: "嘻哈(Hip Hop)"}
|
||||
- {id: 18, cat: Audio, desc: "音乐剧(Musical)"}
|
||||
- {id: 9, cat: Audio, desc: "其它类型(Others)"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
music-search: [q, artist]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: info_tpp
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile. Default is 50.
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: 4
|
||||
options:
|
||||
4: created
|
||||
7: seeders
|
||||
5: size
|
||||
1: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: form
|
||||
form: form[action="takelogin.php"]
|
||||
captcha:
|
||||
type: image
|
||||
selector: img[alt="CAPTCHA"]
|
||||
input: imagestring
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
logout: ""
|
||||
securelogin: ""
|
||||
ssl: yes
|
||||
trackerssl: yes
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("失敗"))
|
||||
- selector: td.embedded:has(h2:contains("failed!"))
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="logout.php"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
# https://open.cd/torrents.php?source11=1&source18=1&incldead=0&spstate=0&search=&search_area=0&search_mode=0
|
||||
- path: torrents.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}source{{.}}=1&{{end}}"
|
||||
search: "{{ if .Query.Artist }}{{ .Query.Artist }}{{ else }}{{ .Keywords }}{{ end }}"
|
||||
# 0 incldead, 1 active, 2 dead
|
||||
incldead: 0
|
||||
# 0 all, 1 normal, 2 free, 3 2x, 4 2xfree, 5 50%, 6 2x50%, 7 30%
|
||||
spstate: "{{ if .Config.freeleech }}2{{ else }}0{{ end }}"
|
||||
# 0 title, 1 descr, 3 uploader, 4 imdburl, 5 traklist, 6 artistname
|
||||
search_area: "{{ if .Query.Artist }}6{{ else }}0{{ end }}"
|
||||
# 0 AND, 1 OR, 2 exact
|
||||
search_mode: 0
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
selector: table.torrents > tbody > tr:has(table.torrentname)
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: td:nth-child(1)
|
||||
attribute: title
|
||||
case:
|
||||
td[title="华语流行"]: 2
|
||||
td[title="古典音乐"]: 3
|
||||
td[title="民族音乐"]: 11
|
||||
td[title="原声配乐"]: 4
|
||||
td[title="泛摇滚乐"]: 5
|
||||
td[title="爵士乡村"]: 8
|
||||
td[title="新世纪"]: 12
|
||||
td[title="舞曲"]: 13
|
||||
td[title="电子"]: 14
|
||||
td[title="民谣"]: 15
|
||||
td[title="独立"]: 16
|
||||
td[title="嘻哈"]: 17
|
||||
td[title="音乐剧"]: 18
|
||||
td[title="其它类型"]: 9
|
||||
title:
|
||||
selector: a[href^="plugin_details.php?id="]
|
||||
title:
|
||||
optional: true
|
||||
selector: a[title][href^="plugin_details.php?id="]
|
||||
attribute: title
|
||||
details:
|
||||
selector: a[href^="plugin_details.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
poster:
|
||||
selector: img[id^="attach"]
|
||||
attribute: src
|
||||
filters:
|
||||
- name: prepend
|
||||
args: "https://images.weserv.nl/?url={{ .Config.sitelink }}" # for display on dashboard
|
||||
- name: append
|
||||
args: "&w=180&h=270" # for display on dashboard
|
||||
date:
|
||||
# time type: time elapsed (default)
|
||||
selector: td:nth-child(6) > span[title]
|
||||
attribute: title
|
||||
optional: true
|
||||
filters:
|
||||
- name: append
|
||||
args: " +08:00" # CST
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04:05 -07:00"
|
||||
date:
|
||||
# time added
|
||||
selector: td:nth-child(6):not(:has(span))
|
||||
optional: true
|
||||
filters:
|
||||
- name: append
|
||||
args: " +08:00" # CST
|
||||
- name: dateparse
|
||||
args: "2006-01-0215:04:05 -07:00"
|
||||
size:
|
||||
selector: td:nth-child(7)
|
||||
seeders:
|
||||
selector: td:nth-child(8)
|
||||
leechers:
|
||||
selector: td:nth-child(9)
|
||||
grabs:
|
||||
selector: td:nth-child(10)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img.pro_free: 0
|
||||
img.pro_free2up: 0
|
||||
img.pro_50pctdown: 0.5
|
||||
img.pro_50pctdown2up: 0.5
|
||||
img.pro_30pctdown: 0.3
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img.pro_50pctdown2up: 2
|
||||
img.pro_free2up: 2
|
||||
img.pro_2up: 2
|
||||
"*": 1
|
||||
description:
|
||||
selector: td:nth-child(3)
|
||||
remove: a, img
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
minimumseedtime:
|
||||
# 36 hours (as seconds = 36 x 60 x 60)
|
||||
text: 129600
|
||||
# NexusPHP Standard v1.5 Beta 4 (customised)
|
@@ -9,7 +9,7 @@ followredirect: true
|
||||
links:
|
||||
- https://www.oxtorrent.tv/
|
||||
- https://www.oxtorrent.co/
|
||||
- https://oxtorrent.unblocked.monster/
|
||||
- https://oxtorrent.nocensor.space/
|
||||
legacylinks:
|
||||
- https://wwv.oxtorrent.com/
|
||||
- https://www.smartorrent.tv/
|
||||
@@ -28,6 +28,7 @@ legacylinks:
|
||||
- https://www.oxtorrent.bz/
|
||||
- https://www.oxtorrent.ws/
|
||||
- https://wvw.oxtorrent.ws/
|
||||
- https://oxtorrent.unblocked.monster/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -42,7 +42,7 @@ caps:
|
||||
- {id: 46, cat: TV/Sport, desc: "Hockey"}
|
||||
- {id: 48, cat: TV/Sport, desc: "Hockey - NHL"}
|
||||
- {id: 88, cat: TV/Sport, desc: "Hockey - NHL Playoffs"}
|
||||
- {id: 102, cat: TV/Sport, desc: "Hockey - NHL Playoffs 2018-2019"}
|
||||
- {id: 102, cat: TV/Sport, desc: "Hockey - NHL Playoffs 2018-2020"}
|
||||
- {id: 93, cat: TV/Sport, desc: "Hockey - NHL Playoffs - 2017"}
|
||||
- {id: 80, cat: TV/Sport, desc: "Hockey - NHL Playoffs - 2016"}
|
||||
- {id: 65, cat: TV/Sport, desc: "Hockey - Stanley Cup Finals"}
|
||||
@@ -53,6 +53,7 @@ caps:
|
||||
- {id: 50, cat: TV/Sport, desc: "Hockey - Other"}
|
||||
- {id: 55, cat: TV/Sport, desc: "Baseball"}
|
||||
- {id: 71, cat: TV/Sport, desc: "Baseball - MLB"}
|
||||
- {id: 107, cat: TV/Sport, desc: "Baseball - MLB World Series"}
|
||||
- {id: 72, cat: TV/Sport, desc: "Baseball - Other"}
|
||||
- {id: 85, cat: TV/Sport, desc: "Baseball - Reviews, highlights, documentaries"}
|
||||
- {id: 45, cat: TV/Sport, desc: "Other sports"}
|
||||
@@ -63,6 +64,7 @@ caps:
|
||||
- {id: 75, cat: TV/Sport, desc: "Other sports - Tennis"}
|
||||
- {id: 74, cat: TV/Sport, desc: "Other sports - Fighting"}
|
||||
- {id: 94, cat: TV/Sport, desc: "Other sports - Misc"}
|
||||
- {id: 73, cat: TV/Sport, desc: "Other sports - Auto, moto racing"}
|
||||
- {id: 100, cat: TV/Sport, desc: "Soccer"}
|
||||
- {id: 98, cat: TV/Sport, desc: "Soccer - FIFA World Cup"}
|
||||
- {id: 56, cat: TV/Sport, desc: "Sports on tv"}
|
||||
|
@@ -18,16 +18,17 @@ caps:
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid, tvdbid]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid, tmdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
- name: cookie
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
label: Cookie
|
||||
- name: info
|
||||
type: info
|
||||
label: How to get the Cookie
|
||||
default: "<ol><li>Login to this tracker with your browser<li>Open the <b>DevTools</b> panel by pressing <b>F12</b><li>Select the <b>Network</b> tab<li>Click on the <b>Doc</b> button (Chrome Browser) or <b>HTML</b> button (FireFox)<li>Refresh the page by pressing <b>F5</b><li>Click on the first row entry<li>Select the <b>Headers</b> tab on the Right panel<li>Find <b>'cookie:'</b> in the <b>Request Headers</b> section<li><b>Select</b> and <b>Copy</b> the whole cookie string <i>(everything after 'cookie: ')</i> and <b>Paste</b> here.</ol>"
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
@@ -50,22 +51,9 @@ settings:
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: login
|
||||
method: form
|
||||
form: form[action$="/login"]
|
||||
method: cookie
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
remember: on
|
||||
selectorinputs:
|
||||
_token:
|
||||
selector: input[name="_token"]
|
||||
attribute: value
|
||||
error:
|
||||
- selector: div#ERROR_COPY
|
||||
# test:
|
||||
# path: /
|
||||
# selector: a[href$="/logout"]
|
||||
cookie: "{{ .Config.cookie }}"
|
||||
|
||||
search:
|
||||
paths:
|
||||
@@ -76,7 +64,7 @@ search:
|
||||
description: ""
|
||||
uploader: ""
|
||||
imdb: "{{ .Query.IMDBIDShort }}"
|
||||
tvdb: "{{ .Query.TVDBID }}"
|
||||
tvdb: ""
|
||||
tmdb: "{{ .Query.TMDBID }}"
|
||||
mal: ""
|
||||
igdb: ""
|
||||
|
@@ -9,7 +9,7 @@ followredirect: true
|
||||
links:
|
||||
- https://pirateiro.com/
|
||||
- https://pirateiro.eu/
|
||||
- https://pirateiro.unblockit.club/
|
||||
- https://pirateiro.unblockit.onl/
|
||||
legacylinks:
|
||||
- http://pirateiro.com/
|
||||
- https://pirateiro.unblockit.pro/
|
||||
@@ -25,6 +25,7 @@ legacylinks:
|
||||
- https://pirateiro.unblockit.ltd/
|
||||
- https://pirateiro.unblockit.link/
|
||||
- https://pirateiro.unblockit.buzz/
|
||||
- https://pirateiro.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -96,7 +96,7 @@ caps:
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
@@ -73,7 +73,7 @@ search:
|
||||
"only_free": 0
|
||||
|
||||
rows:
|
||||
selector: tr:has(a[href^="download.php?torrent="])
|
||||
selector: table[cellpadding="3"] > tbody > tr:has(a[href^="download.php?torrent="])
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="browse.php?cat="]
|
||||
@@ -130,4 +130,9 @@ search:
|
||||
case:
|
||||
img[src="./pic/doubleseed.gif"]: 0
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
minimumseedtime:
|
||||
# 1 day (as seconds = 24 x 60 x 60)
|
||||
text: 86400
|
||||
# U-232v3
|
||||
|
225
src/Jackett.Common/Definitions/redbits.yml
Normal file
225
src/Jackett.Common/Definitions/redbits.yml
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
id: redbits
|
||||
name: RedBits
|
||||
description: "RedBits is a SPANISH site for classic content"
|
||||
language: es-es
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://red-bits.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 1, cat: Movies, desc: "Movies"}
|
||||
- {id: 2, cat: TV, desc: "TV"}
|
||||
- {id: 6, cat: TV/Documentary, desc: "Documentaries"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid, tvdbid]
|
||||
movie-search: [q, imdbid, tmdbid]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: created_at
|
||||
options:
|
||||
created_at: created
|
||||
seeders: seeders
|
||||
size: size
|
||||
name: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: login
|
||||
method: form
|
||||
form: form[action$="/login"]
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
remember: on
|
||||
selectorinputs:
|
||||
_token:
|
||||
selector: input[name="_token"]
|
||||
attribute: value
|
||||
error:
|
||||
- selector: div#ERROR_COPY
|
||||
# test:
|
||||
# path: /
|
||||
# selector: a[href$="/logout"]
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: torrents/filter
|
||||
keywordsfilters:
|
||||
- name: re_replace
|
||||
args: ["(?i)\\bS0*(\\d+)\\b", "T$1"]
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}categories[]={{.}}&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ else }}{{ .Keywords }}{{ end }}" # for dashboard imdbid search
|
||||
description: ""
|
||||
keywords: ""
|
||||
uploader: ""
|
||||
imdb: "{{ .Query.IMDBIDShort }}"
|
||||
tvdb: "{{ .Query.TVDBID }}"
|
||||
tmdb: "{{ .Query.TMDBID }}"
|
||||
mal: ""
|
||||
igdb: ""
|
||||
start_year: ""
|
||||
end_year: ""
|
||||
sorting: "{{ .Config.sort }}"
|
||||
direction: "{{ .Config.type }}"
|
||||
qty: 100
|
||||
page: 0
|
||||
view: list
|
||||
freeleech: "{{ if .Config.freeleech }}1{{ else }}{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: table > tbody > tr
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href*="/categories/"]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/categories/(\\d+)"
|
||||
title:
|
||||
selector: a.view-torrent:contains("VOSE")
|
||||
optional: true
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)bdfull", "BluRay"] # BDfull -> BluRay
|
||||
- name: re_replace
|
||||
args: ["(?i)RedBits", ""] # Delete RedBits
|
||||
- name: append
|
||||
args: " English" # Add english to fix Sonarr/Radarr language
|
||||
- name: re_replace
|
||||
args: ["\\[|\\]", " "]
|
||||
- name: re_replace
|
||||
args: [" ", " "]
|
||||
- name: re_replace
|
||||
args: ["(?i)T(\\d{1,2})\\b", "S$1"]
|
||||
title:
|
||||
selector: a.view-torrent:not(:contains("VOSE"))
|
||||
optional: true
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)bdfull", "BluRay"] # BDfull -> BluRay
|
||||
- name: re_replace
|
||||
args: ["(?i)RedBits", ""] # Delete RedBits
|
||||
- name: append
|
||||
args: " Spanish" # Add spanish to fix Sonarr/Radarr language
|
||||
- name: re_replace
|
||||
args: ["\\[|\\]", " "]
|
||||
- name: re_replace
|
||||
args: [" ", " "]
|
||||
- name: re_replace
|
||||
args: ["(?i)T(\\d{1,2})\\b", "S$1"]
|
||||
download:
|
||||
selector: a[href*="/download/"]
|
||||
attribute: href
|
||||
details:
|
||||
selector: a.view-torrent
|
||||
attribute: href
|
||||
poster:
|
||||
selector: div.torrent-poster img
|
||||
attribute: src
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["&w=52&h=80", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/52x80&w=180&h=270", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
selector: td:nth-last-child(3)
|
||||
leechers:
|
||||
selector: td:nth-last-child(2)
|
||||
grabs:
|
||||
selector: td:nth-last-child(1)
|
||||
filters:
|
||||
- name: regexp
|
||||
args: (\d+)
|
||||
imdb:
|
||||
selector: a[href*="imdb.com/title/tt"]
|
||||
attribute: href
|
||||
tmdbid:
|
||||
selector: a[href*="themoviedb.org/movie/"]
|
||||
attribute: href
|
||||
date:
|
||||
selector: time
|
||||
filters:
|
||||
# translations for Turkish|Estonian|Danish|Italian|Polish|Norwegian|Portuguese|Czech|Russian|Romanian|Spanish|French|German|Bulgarian|Dutch|Chinese|Japanese|Swedish
|
||||
- name: re_replace
|
||||
args: ["(?i)(önce|tagasi|geleden|fa|temu|siden|há|atrás|nazpět|назад|acum|în urmă|hace|il y a|vor|преди|前|sedan)", " ago"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saniye|sekundit|sekunder|secondi|sekund|segundos|sekundami|секунд|secunde|secondes|Sekunden|секунди|seconden|秒前)", "seconds"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(minutit|minutter|minuti|minuty|minutos|минуты|минут|Minuten|минути|minuten|minuter)", "minutes"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(dakika|minut|minuto|minuta|minutt|минута|Minute|minuut|分钟|分)", " minute"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(tundi|timer|ore|godziny|horas|hodiny|hoden|часа|часов|ore|heures|Stunden|timmar)", "hours"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(saat|tund|time|ora|godzina|hora|hodina|час|oră|heure|Stunde|uur|小时|時間|timme)", " hour"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(päeva|dage|giorni|dni|dias|dny|дня|дней|zile|días|jours|Tagen|дни|dagen|dagar)", "days"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(gün|päev|dag|giorno|dzień|dia|den|день|zi|día|jour|Tag|ден|天|日)", " day"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(nädalat|uger|settimane|tygodnie|uker|semanas|týdny|недели|недель|săptămâni|semaines|Wochen|седмици|weken|veckor)", "weeks"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(hafta|nädal|uge|settimana|tydzień|uke|semana|týden|неделю|săptămână|semaine|Woche|седмица|周|週間|vecka)", " week"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (ay)", "month"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(kuud|måneder|mesi|miesiące|meses|měsíce|месяца|месяцев|luni|meses|mois|Monaten|месеца|maanden|månader)", "months"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(kuu|måned|mese|miesiąc|mês|měsíc|месяц|lună|mes|Monat|месец|maand|个月|ヶ月|månad)", " month"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(aastat|anni|lata|anos|roky|года|ani|años|ans|Jahren|години)", " years"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(yil|aasta|år|anno|rok|ano|год|año|Jahr|година|jaar|年)", " year"]
|
||||
- name: re_replace
|
||||
args: ["(?i) (an)", "year"]
|
||||
- name: re_replace
|
||||
args: ["(?i)(För |und)", ""] # Misc removals
|
||||
- name: timeago
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
i[class*="fa-id-badge text-orange"]: 0 # 24 Hour FreeLeech From BON Store
|
||||
i[class*="fa-trophy text-purple"]: 0 # Special FreeLeech For Certain User Groups
|
||||
i[class*="fa-star text-bold"]: 0 # Freeleech From Token
|
||||
i[class*="fa-coins text-bold"]: 0 # Freeleech From Token
|
||||
i[class*="fa-globe text-blue"]: 0 # Global Freeleech
|
||||
i[class*="fa-star text-gold"]: 0 # Freeleech
|
||||
i[class*="fa-certificate text-pink"]: 0 # Featured Torrent
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
i[class*="fa-gem text-green"]: 2 # Single Torrent Double Upload
|
||||
i[class*="fa-globe text-green"]: 2 # Global Double Upload
|
||||
i[class*="fa-certificate text-pink"]: 2 # Featured Torrent
|
||||
"*": 1
|
||||
minimumseedtime:
|
||||
# 4 days (as seconds = 4 x 24 x 60 x 60)
|
||||
text: 345600
|
||||
# UNIT3D 5.1.0
|
176
src/Jackett.Common/Definitions/rofd.yml
Normal file
176
src/Jackett.Common/Definitions/rofd.yml
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
id: rofd
|
||||
name: ROFD
|
||||
description: "ROFD is a Private GERMAN site for TV / MOVIES / GENERAL"
|
||||
language: de-de
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://rofd.me/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 14, cat: Movies/SD, desc: "Filme XviD"}
|
||||
- {id: 15, cat: Movies/HD, desc: "Filme x264/265"}
|
||||
- {id: 16, cat: Movies/DVD, desc: "Filme DVD"}
|
||||
- {id: 17, cat: TV/Anime, desc: "Filme Kids/Anime"}
|
||||
- {id: 18, cat: TV/Documentary, desc: "Filme Doku"}
|
||||
- {id: 19, cat: Movies/HD, desc: "Filme 720P"}
|
||||
- {id: 20, cat: Movies/HD, desc: "Filme 720P/x265"}
|
||||
- {id: 21, cat: Movies/HD, desc: "Filme 1080P"}
|
||||
- {id: 22, cat: Movies/HD, desc: "Filme 1080P/x265"}
|
||||
- {id: 23, cat: Movies/BluRay, desc: "Filme Blu-Ray"}
|
||||
- {id: 24, cat: Movies/BluRay, desc: "Filme Blu-Ray Remux"}
|
||||
- {id: 25, cat: Movies/UHD, desc: "Filme UHD/4K"}
|
||||
- {id: 43, cat: Movies/3D, desc: "Filme 3D"}
|
||||
- {id: 44, cat: Movies, desc: "Filme International"}
|
||||
- {id: 27, cat: TV/SD, desc: "Serien SD"}
|
||||
- {id: 28, cat: TV/HD, desc: "Serien HD"}
|
||||
- {id: 30, cat: Audio/MP3, desc: "Musik mp3"}
|
||||
- {id: 31, cat: Audio/Lossless, desc: "Musik Flac"}
|
||||
- {id: 32, cat: Audio/Video, desc: "Musik Video"}
|
||||
- {id: 34, cat: PC/Games, desc: "Spiele Windows"}
|
||||
- {id: 35, cat: Console, desc: "Spiele Konsole"}
|
||||
- {id: 36, cat: Console, desc: "Spiele Wimmel"}
|
||||
- {id: 37, cat: Audio/Audiobook, desc: "A-Book"}
|
||||
- {id: 38, cat: Books/EBook, desc: "E-Book/PDF"}
|
||||
- {id: 39, cat: PC, desc: "SOFTWARE"}
|
||||
- {id: 40, cat: TV/Sport, desc: "SPORT"}
|
||||
- {id: 41, cat: Other, desc: "MISC/SONSTIGES"}
|
||||
- {id: 42, cat: XXX, desc: "XXX"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: 3
|
||||
options:
|
||||
3: created
|
||||
5: seeders
|
||||
4: size
|
||||
2: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: 2
|
||||
options:
|
||||
2: desc
|
||||
1: asc
|
||||
- name: info_tpp
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile. The default is <i>15</i>.
|
||||
|
||||
login:
|
||||
path: index.php?page=login
|
||||
method: post
|
||||
inputs:
|
||||
uid: "{{ .Config.username }}"
|
||||
pwd: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: body[onLoad^="makeAlert('"]
|
||||
message:
|
||||
selector: body[onLoad^="makeAlert('"]
|
||||
attribute: onLoad
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["makeAlert('Error' , '", ""]
|
||||
- name: replace
|
||||
args: ["');", ""]
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href="logout.php"]
|
||||
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: index.php
|
||||
inputs:
|
||||
search: "{{ .Keywords }}"
|
||||
category: "{{ range .Categories }}{{.}};{{end}}"
|
||||
page: torrents
|
||||
# 0 all, 1 activeonly, 2 deadonly
|
||||
active: 0
|
||||
# 0 name, 1 name&descr, 2 descr, 3 uploader, 5 gold, 6 silver, 7 bronze
|
||||
options: "{{ if .Config.freeleech }}5{{ else }}0{{ end }}"
|
||||
order: "{{ .Config.sort }}"
|
||||
by: "{{ .Config.type }}"
|
||||
# does not return imdb link in results, and while a few titles have an imdbid in descr the majority do not.
|
||||
|
||||
rows:
|
||||
selector: div.b-content > table > tbody > tr > td > table.lista > tbody > tr:has(a[href^="index.php?page=torrent-details&id="])
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="index.php?page=torrents&category="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: category
|
||||
title:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
details:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="index.php?page=downloadcheck&id="]
|
||||
attribute: href
|
||||
poster:
|
||||
selector: a[href^="index.php?page=torrent-details&id="]
|
||||
attribute: onmouseover
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "src=(.+?) "
|
||||
date:
|
||||
selector: td:nth-last-child(8)
|
||||
filters:
|
||||
- name: append
|
||||
args: " +00:00" # auto adjusted by site account profile
|
||||
- name: dateparse
|
||||
args: "02/01/2006 -07:00"
|
||||
seeders:
|
||||
selector: td:nth-last-child(7)
|
||||
leechers:
|
||||
selector: td:nth-last-child(6)
|
||||
grabs:
|
||||
selector: td:nth-last-child(5)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["---", "0"]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img[src="images/freeleech.gif"]: 0
|
||||
img[src="images/gold.gif"]: 0
|
||||
img[src="images/silver.gif"]: 0.5
|
||||
img[src="images/bronze.gif"]: 0.75
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
minimumseedtime:
|
||||
# 2 days (as seconds = 2 x 24 x 60 x 60)
|
||||
text: 172800
|
||||
# xbtitFM 3.0.00
|
@@ -8,7 +8,7 @@ encoding: UTF-8
|
||||
links:
|
||||
- http://rutor.info/ # site does not support https ERR_CONNECTION_REFUSED
|
||||
- http://rutor.is/ # site does not support https ERR_CONNECTION_REFUSED
|
||||
- https://rutor.unblocked.monster/
|
||||
- https://rutor.nocensor.space/
|
||||
legacylinks:
|
||||
- http://live-rutor.org/ # domain expired 9 Feb 2020
|
||||
- http://new-rutor.org/ # ERR_NAME_NOT_RESOLVED
|
||||
@@ -22,6 +22,7 @@ legacylinks:
|
||||
- https://rutor.uk-unblock.pro/
|
||||
- https://rutor.root.yt/
|
||||
- https://rutor.unblocked.rest/
|
||||
- https://rutor.unblocked.monster/
|
||||
|
||||
caps:
|
||||
# unfortunately RuTor does not display categories anywhere in its search results page :-(
|
||||
|
@@ -22,10 +22,38 @@ caps:
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
music-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: added
|
||||
options:
|
||||
added: created
|
||||
seeders: seeders
|
||||
size: size
|
||||
name: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: DESC
|
||||
options:
|
||||
DESC: desc
|
||||
ASC: asc
|
||||
- name: info
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile.
|
||||
|
||||
login:
|
||||
path: takeloginn3.php
|
||||
method: post
|
||||
@@ -44,9 +72,14 @@ search:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
|
||||
search: "{{ .Keywords }}"
|
||||
search: "{{ if .Query.IMDBID }}{{ else }}{{ .Keywords }}{{ end }}"
|
||||
incldead: 1
|
||||
descriptions: 0
|
||||
imdbgt: 0
|
||||
imdblt: 10
|
||||
imdb: "{{ .Query.IMDBID }}"
|
||||
sort: "{{ .Config.sort }}"
|
||||
d: "{{ .Config.type }}"
|
||||
|
||||
rows:
|
||||
selector: table#torrent-list > tbody > tr[id]
|
||||
|
@@ -36,6 +36,7 @@ caps:
|
||||
- {id: 23, cat: Audio/Video, desc: "Video Clip"}
|
||||
- {id: 24, cat: XXX, desc: "Adult 18+"}
|
||||
- {id: 36, cat: Movies/3D, desc: "Video 3D"}
|
||||
- {id: 37, cat: Movies/HD, desc: "Animație HD"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -1,82 +0,0 @@
|
||||
---
|
||||
id: shellife
|
||||
name: Shellife
|
||||
description: "Shellife (SL) is a Private Torrent Tracker for NON MAINSTREAM MUSIC"
|
||||
language: en-us
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://shellife.eu/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
1: Audio
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
music-search: [q, artist]
|
||||
|
||||
login:
|
||||
path: takelogin.php
|
||||
method: post
|
||||
inputs:
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
error:
|
||||
- selector: td:has(h2:contains("failed"))
|
||||
test:
|
||||
path: browse.php
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: browse.php
|
||||
inputs:
|
||||
search: "{{ if .Query.Artist }}{{ .Query.Artist }}{{ else }}{{ .Keywords }}{{ end }}"
|
||||
incldead: 1
|
||||
|
||||
rows:
|
||||
selector: table#ct > tbody > tr.torrent_row
|
||||
filters:
|
||||
- name: andmatch
|
||||
|
||||
fields:
|
||||
category:
|
||||
text: 1
|
||||
title:
|
||||
selector: a.altlink
|
||||
title|append|1:
|
||||
text: " - "
|
||||
title|append|2:
|
||||
selector: a[href^="details.php?id="]
|
||||
details:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
date:
|
||||
text: now
|
||||
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:
|
||||
img[alt="Freeleech"]: 0
|
||||
img[alt="Free"]: 0
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a.altlink, a[href^="details.php?id="], div[id^="news"]
|
||||
filters:
|
||||
- name: trim
|
||||
args: "-"
|
||||
- name: trim
|
||||
args: " "
|
||||
# engine n/a
|
@@ -6,6 +6,8 @@ language: en-us
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://teamos-hkrg.com/
|
||||
legacylinks:
|
||||
- https://www.teamos-hkrg.com/
|
||||
|
||||
caps:
|
||||
|
@@ -121,7 +121,7 @@ search:
|
||||
- name: replace
|
||||
args: ["&w=52&h=80", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/52x80&w=180&h=270", ""]
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/600x900&w=180&h=270", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
|
@@ -11,17 +11,25 @@ links:
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 21, cat: PC, desc: "Appz"}
|
||||
- {id: 27, cat: Books/EBook, desc: "E-Books"}
|
||||
- {id: 22, cat: PC/Games, desc: "Games"}
|
||||
- {id: 23, cat: Other, desc: "Misc."}
|
||||
- {id: 24, cat: Movies/SD, desc: "Movies SD"}
|
||||
- {id: 11, cat: Movies/HD, desc: "Movies HD"}
|
||||
- {id: 3, cat: Movies, desc: "Movies Packs"}
|
||||
- {id: 20, cat: Audio/Lossless, desc: "Music FLAC"}
|
||||
- {id: 4, cat: Audio/Lossless, desc: "Music FLAC"}
|
||||
- {id: 17, cat: Audio, desc: "Music Packs"}
|
||||
- {id: 18, cat: TV/HD, desc: "TV HD"}
|
||||
- {id: 16, cat: TV, desc: "TV Packs"}
|
||||
- {id: 19, cat: TV/SD, desc: "TV SD"}
|
||||
- {id: 28, cat: Console, desc: "Games/Console"}
|
||||
- {id: 33, cat: PC/Games, desc: "Games/PC"}
|
||||
- {id: 32, cat: Console, desc: "Games/Roms"}
|
||||
- {id: 23, cat: Other, desc: "Misc"}
|
||||
- {id: 31, cat: Movies/3D, desc: "Movies/3D"}
|
||||
- {id: 26, cat: Movies/3D, desc: "Movies/Cam"}
|
||||
- {id: 11, cat: Movies/HD, desc: "Movies/HD"}
|
||||
- {id: 3, cat: Movies, desc: "Movies/Packs"}
|
||||
- {id: 24, cat: Movies/SD, desc: "Movies/SD"}
|
||||
- {id: 30, cat: Movies/UHD, desc: "Movies/UHD"}
|
||||
- {id: 20, cat: Audio/Lossless, desc: "Music/FLAC"}
|
||||
- {id: 4, cat: Audio/MP3, desc: "Music/MP3"}
|
||||
- {id: 17, cat: Audio, desc: "Music/Packs"}
|
||||
- {id: 18, cat: TV/HD, desc: "TV/HD"}
|
||||
- {id: 16, cat: TV, desc: "TV/Packs"}
|
||||
- {id: 19, cat: TV/SD, desc: "TV/SD"}
|
||||
- {id: 29, cat: TV/UHD, desc: "TV/UHD"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
|
@@ -7,6 +7,8 @@ type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.thesceneplace.com/
|
||||
legacylinks:
|
||||
- http://www.thesceneplace.com/ # site is no longer forcing http
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
@@ -98,7 +100,7 @@ search:
|
||||
# does not support imdbid search and does not return imdb link in results
|
||||
|
||||
rows:
|
||||
selector: "table.lista > tbody > tr:has(a[href^=\"index.php?page=torrent-details&id=\"]){{ if .Config.freeleech }}:has(img[src=\"images/freeleech.gif\"]){{ else }}{{ end }}"
|
||||
selector: "table.lista > tbody > tr:has(a[href^=\"index.php?page=torrent-details&id=\"]){{ if .Config.freeleech }}:has(img[src=\"images/freeleech.gif\"]){{ else }}{{ end }}{{ if .Config.freeleech }}, table.lista > tbody > tr:has(a[href^=\"index.php?page=torrent-details&id=\"]):has(img[src=\"images/gold.gif\"]){{ else }}{{ end }}"
|
||||
|
||||
fields:
|
||||
category:
|
||||
@@ -149,7 +151,17 @@ search:
|
||||
img[src="images/bronze.gif"]: 0.75
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
case:
|
||||
img[src="images/2x.gif"]: 2
|
||||
img[src="images/3x.gif"]: 3
|
||||
img[src="images/4x.gif"]: 4
|
||||
img[src="images/5x.gif"]: 5
|
||||
img[src="images/6x.gif"]: 6
|
||||
img[src="images/7x.gif"]: 7
|
||||
img[src="images/8x.gif"]: 8
|
||||
img[src="images/9x.gif"]: 9
|
||||
img[src="images/10x.gif"]: 10
|
||||
"*": 1
|
||||
minimumratio:
|
||||
text: 1.0
|
||||
minimumseedtime:
|
||||
|
@@ -1,95 +0,0 @@
|
||||
---
|
||||
id: tjangto
|
||||
name: Tjangto
|
||||
description: "Tjangto (짱토) is a Public KOREAN tracker for Korean media."
|
||||
language: ko-KR
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://www.jjangt.com/
|
||||
legacylinks:
|
||||
- https://www.jjangtorrent.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: "tani", cat: TV/Anime, desc: "애니 (Animation)"}
|
||||
- {id: "tv", cat: TV, desc: "TV프로 (TV)"}
|
||||
- {id: "tmovie", cat: Movies, desc: "영화 (Movie)"}
|
||||
- {id: "tdrama", cat: TV, desc: "드라마 (Drama)"}
|
||||
- {id: "tent", cat: TV, desc: "예능 (Entertainment)"}
|
||||
- {id: "tmusic", cat: Audio, desc: "음악 (Music)"}
|
||||
- {id: "util", cat: PC, desc: "유틸 (Software)"}
|
||||
- {id: "torrent_amav", cat: XXX, desc: "서양 (Adult)"}
|
||||
- {id: "torrent_nmav", cat: XXX, desc: "일본노모 (Adult)"}
|
||||
- {id: "torrent_ymav", cat: XXX, desc: "일본유모 (Adult)"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: flaresolverr
|
||||
type: info
|
||||
label: FlareSolverr
|
||||
default: This site may use Cloudflare DDoS Protection, therefore Jackett requires <a href="https://github.com/Jackett/Jackett#configuring-flaresolverr" target="_blank">FlareSolver</a> to access it.
|
||||
|
||||
download:
|
||||
selector: a[href*="magnet:?xt="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: append
|
||||
args: "&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969&tr=udp%3A%2F%2Fp4p.arenabg.com%3A1337%2Fannounce&tr=udp%3A%2F%2Fexplodie.org%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.tiny-vps.com%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.demonii.si%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&tr=udp%3A%2F%2Ftracker.pirateparty.gr%3A6969%2Fannounce&tr=udp%3A%2F%2Fipv4.tracker.harry.lu%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.cyberia.is%3A6969%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2Fdenis.stalker.upeer.me%3A6969%2Fannounce"
|
||||
|
||||
search:
|
||||
paths:
|
||||
# https://www.jjangtorrent.com/bbs/search.php?site=1&sfl=wr_subject%7C%7Cwr_content&sop=and&stx=sin
|
||||
- path: bbs/search.php
|
||||
inputs:
|
||||
stx: "{{ if .Keywords }}{{ .Keywords }}{{ else }}{{ .Today.Year }}{{ end }}"
|
||||
srows: 100
|
||||
gr_id: ""
|
||||
# wr_subject||wr_content, wr_subject, wr_content, mb_id, wr_name
|
||||
sfl: wr_subject
|
||||
# or, and
|
||||
sop: and
|
||||
site: 1
|
||||
|
||||
rows:
|
||||
selector: section.sch_res_list ul li
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: div.sch_tit a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: bo_table
|
||||
title:
|
||||
selector: div.sch_tit a
|
||||
details:
|
||||
selector: div.sch_tit a
|
||||
attribute: href
|
||||
download:
|
||||
selector: div.sch_tit a
|
||||
attribute: href
|
||||
date:
|
||||
# 2020-05-12 12:00:52
|
||||
selector: span.sch_datetime
|
||||
filters:
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04:05"
|
||||
size:
|
||||
text: "512 MB"
|
||||
seeders:
|
||||
text: 1
|
||||
leechers:
|
||||
text: 1
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
@@ -7,7 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.tokyotosho.info/
|
||||
- https://tokyotosho.unblocked.monster/
|
||||
- https://tokyotosho.nocensor.space/
|
||||
legacylinks:
|
||||
- https://tokyotosho.black-mirror.xyz/
|
||||
- https://tokyotosho.unblocked.casa/
|
||||
@@ -18,6 +18,7 @@ legacylinks:
|
||||
- https://tokyotosho.proxyportal.pw/
|
||||
- https://tokyotosho.uk-unblock.pro/
|
||||
- https://tokyotosho.unblocked.rest/
|
||||
- https://tokyotosho.unblocked.monster/
|
||||
|
||||
settings:
|
||||
- name: cat
|
||||
|
@@ -10,7 +10,8 @@ links:
|
||||
- https://www.torlock.com/
|
||||
- https://www.torlock2.com/
|
||||
- https://www.torlock.icu/
|
||||
- https://torlock.unblockit.club/
|
||||
- https://torlock.unblockit.onl/
|
||||
- https://torlock.nocensor.space/
|
||||
legacylinks:
|
||||
- https://torlock.com/
|
||||
- https://torlock.unblockit.pro/
|
||||
@@ -26,6 +27,7 @@ legacylinks:
|
||||
- https://torlock.unblockit.ltd/
|
||||
- https://torlock.unblockit.link/
|
||||
- https://torlock.unblockit.buzz/
|
||||
- https://torlock.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -7,6 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://www.toros.co/
|
||||
- https://toros.nocensor.space/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -7,6 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrent4you.me/
|
||||
- https://torrent4you.nocensor.space/
|
||||
legacylinks:
|
||||
- http://torrent4you.me/
|
||||
|
||||
|
@@ -7,8 +7,8 @@ type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://www.torrent9.srl/
|
||||
- https://torrent9.unblocked.monster/
|
||||
- https://www.torrent9.ninja/
|
||||
- https://torrent9.nocensor.space/
|
||||
|
||||
legacylinks:
|
||||
- https://www.torrents9.pw/
|
||||
@@ -51,6 +51,9 @@ legacylinks:
|
||||
- https://wvw.torrent9.one/
|
||||
- https://wwv.torrent9.one/
|
||||
- https://vww.torrent9.one/
|
||||
- https://www.torrent9.srl/
|
||||
- https://torrent9.unblocked.monster/
|
||||
- https://www.torrent9.la/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -8,8 +8,8 @@ encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://www.torrentdownload.info/
|
||||
- https://torrentdownload.unblockit.club/
|
||||
- https://torrentdownload.unblocked.monster/
|
||||
- https://torrentdownload.unblockit.onl/
|
||||
- https://torrentdownload.nocensor.space/
|
||||
legacylinks:
|
||||
- https://torrentdownload.unblockit.pro/
|
||||
- https://torrentdownload.unblockit.one/
|
||||
@@ -33,6 +33,8 @@ legacylinks:
|
||||
- https://torrentdownload.unblockit.ltd/
|
||||
- https://torrentdownload.unblockit.link/
|
||||
- https://torrentdownload.unblockit.buzz/
|
||||
- https://torrentdownload.unblocked.monster/
|
||||
- https://torrentdownload.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -9,8 +9,8 @@ followredirect: true
|
||||
links:
|
||||
- https://www.torrentdownloads.info/
|
||||
- https://www.torrentdownloads.me/
|
||||
- https://torrentdownloads.unblockit.club/
|
||||
- https://torrentdownloads.unblocked.monster/
|
||||
- https://torrentdownloads.unblockit.onl/
|
||||
- https://torrentdownloads.nocensor.space/
|
||||
legacylinks:
|
||||
- https://torrentdownloads.unblockit.pro/
|
||||
- https://torrentdownloads.unblockit.one/
|
||||
@@ -34,6 +34,8 @@ legacylinks:
|
||||
- https://torrentdownloads.unblockit.ltd/
|
||||
- https://torrentdownloads.unblockit.link/
|
||||
- https://torrentdownloads.unblockit.buzz/
|
||||
- https://torrentdownloads.unblocked.monster/
|
||||
- https://torrentdownloads.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -9,7 +9,8 @@ followredirect: true
|
||||
links:
|
||||
- https://www.torrentfunk.com/
|
||||
- https://www.torrentfunk2.com/
|
||||
- https://torrentfunk.unblockit.club/
|
||||
- https://torrentfunk.unblockit.onl/
|
||||
- https://torrentfunk.nocensor.space/
|
||||
legacylinks:
|
||||
- https://torrentfunk.unblockit.pro/
|
||||
- https://torrentfunk.unblockit.one/
|
||||
@@ -24,6 +25,7 @@ legacylinks:
|
||||
- https://torrentfunk.unblockit.ltd/
|
||||
- https://torrentfunk.unblockit.link/
|
||||
- https://torrentfunk.unblockit.buzz/
|
||||
- https://torrentfunk.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -11,8 +11,8 @@ links:
|
||||
- https://torrentgalaxy.mx/
|
||||
- https://torrentgalaxy.su/
|
||||
- https://torrentgalaxy.unblockninja.com/
|
||||
- https://torrentgalaxy.unblockit.club/
|
||||
- https://tgx.unblocked.monster/
|
||||
- https://torrentgalaxy.unblockit.onl/
|
||||
- https://torrentgalaxy.nocensor.space/
|
||||
legacylinks:
|
||||
- https://torrentgalaxy.org/ # redirects to *.to
|
||||
- https://torrentgalaxy.unblockit.pro/
|
||||
@@ -39,6 +39,8 @@ legacylinks:
|
||||
- https://torrentgalaxy.unblockit.ltd/
|
||||
- https://torrentgalaxy.unblockit.link/
|
||||
- https://torrentgalaxy.unblockit.buzz/
|
||||
- https://tgx.unblocked.monster/
|
||||
- https://torrentgalaxy.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -154,6 +154,8 @@ search:
|
||||
args: ["&w=52&h=80", "&w=180&h=270"] # for display on dashboard
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/52x80&w=180&h=270", ""]
|
||||
- name: replace
|
||||
args: ["https://images.weserv.nl/?url=https://via.placeholder.com/600x900&w=180&h=270", ""]
|
||||
size:
|
||||
selector: td:nth-last-child(4)
|
||||
seeders:
|
||||
|
@@ -6,12 +6,11 @@ language: en-us
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrentparadise.org/
|
||||
legacylinks:
|
||||
- https://torrentparadise.to/
|
||||
- https://torrentparadise.cc/
|
||||
- https://torrentparadise.la/
|
||||
- https://torrentparadise.cl/
|
||||
- https://torrentparadise.org/
|
||||
|
||||
caps:
|
||||
categories:
|
||||
|
@@ -7,13 +7,17 @@ type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentqq81.com/
|
||||
- https://torrentqq85.com/
|
||||
legacylinks:
|
||||
- https://torrentqq76.com/
|
||||
- https://torrentqq77.com/
|
||||
- https://torrentqq78.com/
|
||||
- https://torrentqq79.com/
|
||||
- https://torrentqq80.com/
|
||||
- https://torrentqq81.com/
|
||||
- https://torrentqq82.com/
|
||||
- https://torrentqq83.com/
|
||||
- https://torrentqq84.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
@@ -75,7 +79,6 @@ search:
|
||||
selector: a[href$=".html"][title]
|
||||
attribute: href
|
||||
date:
|
||||
# note: this will cause 0m date results for MM-dd dates that are higher than current date, as Jackett dateparse assumes year is now.
|
||||
selector: div.wr-date:contains("-")
|
||||
optional: true
|
||||
filters:
|
||||
|
@@ -7,7 +7,7 @@ type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentview32.com/
|
||||
- https://torrentview33.com/
|
||||
legacylinks:
|
||||
- https://torrentview.net/
|
||||
- https://torrentview3.net/
|
||||
@@ -39,6 +39,7 @@ legacylinks:
|
||||
- https://torrentview29.com/
|
||||
- https://torrentview30.com/
|
||||
- https://torrentview31.com/
|
||||
- https://torrentview32.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -7,9 +7,10 @@ type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentwiz23.me/
|
||||
- https://torrentwiz24.me/
|
||||
legacylinks:
|
||||
- https://torrentwiz22.me/
|
||||
- https://torrentwiz23.me/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -1,169 +0,0 @@
|
||||
---
|
||||
id: torrentz2
|
||||
name: Torrentz2
|
||||
description: "Torrentz2 is a Public torrent meta-search engine combining results from dozens of torrent sites"
|
||||
language: en-us
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
followredirect: true
|
||||
links:
|
||||
- https://torrentzwealmisr.onion.ly/
|
||||
- https://torrentz.unblockit.buzz/
|
||||
- https://torrentz2.unblockninja.com/
|
||||
legacylinks:
|
||||
- https://torrentz.unblockit.pro/
|
||||
- https://torrentz.unblockit.one/
|
||||
- https://torrentz2.black-mirror.xyz/
|
||||
- https://torrentz2.unblocked.casa/
|
||||
- https://torrentz2.proxyportal.fun/
|
||||
- https://torrentz2.uk-unblock.xyz/
|
||||
- https://torrentz2.ind-unblock.xyz/
|
||||
- https://torrentz.unblockit.me/
|
||||
- https://torrentz.unblockit.pw/
|
||||
- https://torrentz2.eu/ # domain suspended by registry
|
||||
- https://torrentz.unblockit.id/
|
||||
- https://torrentz.unblockit.win/
|
||||
- https://torrentz2.unblocked.bar/
|
||||
- https://torrentz2.proxyportal.pw/
|
||||
- https://torrentz2.uk-unblock.pro/
|
||||
- https://torrentz.unblockit.top/
|
||||
- https://torrentz.unblockit.lat/
|
||||
- https://torrentz2.is/ # Error: 503 Service Unavailable
|
||||
- https://torrentsmirror.com/ # Error: 503 Service Unavailable
|
||||
- https://torrentz.pl/ # Error: 503 Service Unavailable
|
||||
- https://torrentz.unblockit.app/
|
||||
- https://torrentz2.unblocked.rest/
|
||||
- https://torrentz.unblockit.dev/
|
||||
- https://torrentz.unblockit.ltd/
|
||||
- https://torrentz.unblockit.link/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: "video tv", cat: TV, desc: "video tv"}
|
||||
- {id: "video anime", cat: TV/Anime, desc: "video anime"}
|
||||
- {id: video, cat: Movies, desc: video}
|
||||
- {id: "movies divx xvid", cat: Movies, desc: "movies divx xvid"}
|
||||
- {id: "video movie dvd", cat: Movies, desc: "video movie dvd"}
|
||||
- {id: "video movie dvdrip", cat: Movies, desc: "video movie dvdrip"}
|
||||
- {id: "video movie hd", cat: Movies/HD, desc: "video movie hd"}
|
||||
- {id: ebook, cat: Books/EBook, desc: ebook}
|
||||
- {id: "ebook comics", cat: Books/Comics, desc: "ebook comics"}
|
||||
- {id: "ebook magazine", cat: Books/Mags, desc: "ebook magazine"}
|
||||
- {id: "ebook tutorial", cat: Books/Technical, desc: "ebook tutorial"}
|
||||
- {id: "ebook audio book", cat: Audio/Audiobook, desc: "ebook audio book"}
|
||||
- {id: audio, cat: Audio, desc: audio}
|
||||
- {id: "audio music lossless", cat: Audio/Lossless, desc: "audio music lossless"}
|
||||
- {id: "audio music mp3", cat: Audio/MP3, desc: "audio music mp3"}
|
||||
- {id: application, cat: PC/0day, desc: application}
|
||||
- {id: game, cat: PC/Games, desc: game}
|
||||
- {id: "game pc", cat: PC/Games, desc: "game pc"}
|
||||
- {id: "game xbox", cat: Console/XBox, desc: "game xbox"}
|
||||
- {id: "game nintendo", cat: Console/NDS, desc: game nintendo"}
|
||||
- {id: adult, cat: XXX, desc: adult}
|
||||
- {id: "adult amateur", cat: XXX, desc: "adult amateur"}
|
||||
- {id: "adult anal", cat: XXX, desc: "adult anal"}
|
||||
- {id: "adult asian", cat: XXX, desc: "adult asian"}
|
||||
- {id: "adult blowjobs", cat: XXX, desc: "adult blowjobs"}
|
||||
- {id: "adult creampie", cat: XXX, desc: "adult creampie"}
|
||||
- {id: "adult double p", cat: XXX, desc: "adult double p"}
|
||||
- {id: "adult fisting", cat: XXX, desc: "adult fisting"}
|
||||
- {id: "adult hairy", cat: XXX, desc: "adult hairy"}
|
||||
- {id: "adult hentai", cat: XXX, desc: "adult hentai"}
|
||||
- {id: "adult interracial", cat: XXX, desc: "adult interracial"}
|
||||
- {id: "adult lesbian", cat: XXX, desc: "adult lesbian"}
|
||||
- {id: "adult milf", cat: XXX, desc: "adult milf"}
|
||||
- {id: "adult squirting", cat: XXX, desc: "adult squirting"}
|
||||
- {id: "adult tattoo", cat: XXX, desc: "adult tattoo"}
|
||||
- {id: other, cat: Other, desc: other}
|
||||
- {id: images, cat: Other, desc: images}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: itorrents-links
|
||||
type: checkbox
|
||||
label: "Add download links via itorrents.org"
|
||||
default: false
|
||||
- name: filter-safe
|
||||
type: checkbox
|
||||
label: "Exclude adult content from results"
|
||||
default: true
|
||||
- name: filter-verified
|
||||
type: checkbox
|
||||
label: "Only include verifed content in results"
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: A
|
||||
options:
|
||||
_: peers
|
||||
N: rating
|
||||
A: created
|
||||
S: size
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: "{{ if .Config.filter-verified }}verified{{ else }}search{{ end }}{{ re_replace .Config.sort \"_\" \"\" }}"
|
||||
inputs:
|
||||
f: "{{ if .Keywords }}title: {{ .Keywords }}{{ else }}{{ end }}"
|
||||
safe: "{{ if .Config.filter-safe }}1{{ else }}0{{ end }}"
|
||||
|
||||
rows:
|
||||
selector: "html body #wrap .results dl:has(a)"
|
||||
# andmatch filter removed, see #3737
|
||||
|
||||
fields:
|
||||
title:
|
||||
selector: dt a
|
||||
details:
|
||||
selector: dt a
|
||||
attribute: href
|
||||
download-itorrents:
|
||||
selector: dt a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/(\\w+)"
|
||||
- name: toupper
|
||||
- name: prepend
|
||||
args: http://itorrents.org/torrent/
|
||||
- name: append
|
||||
args: ".torrent"
|
||||
download:
|
||||
text: "{{ if .Config.itorrents-links }}{{ .Result.download-itorrents }}{{ else }}{{ end }}"
|
||||
infohash:
|
||||
selector: dt a
|
||||
attribute: href
|
||||
filters:
|
||||
- name: regexp
|
||||
args: "/(\\w+)"
|
||||
category:
|
||||
text: other
|
||||
category|noappend:
|
||||
optional: true
|
||||
selector: dt
|
||||
remove: a
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["[^a-zA-Z0-9\\s]+", ""]
|
||||
- name: trim
|
||||
date:
|
||||
selector: dd span:nth-child(2)
|
||||
attribute: title
|
||||
size:
|
||||
selector: dd span:nth-child(3)
|
||||
seeders:
|
||||
selector: dd span:nth-child(4)
|
||||
leechers:
|
||||
selector: dd span:nth-child(5)
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
@@ -1,113 +0,0 @@
|
||||
---
|
||||
id: torrentz2k
|
||||
name: Torrentz2k
|
||||
description: "Torrentz2k is a Public torrent indexer"
|
||||
language: en-us
|
||||
type: public
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://torrentz2is.me/
|
||||
legacylinks:
|
||||
- https://torrentz2k.xyz/
|
||||
- https://torrentz2k.pw/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: book, cat: Books, desc: Books}
|
||||
- {id: film, cat: Movies, desc: Movies}
|
||||
- {id: gamepad, cat: PC/Games, desc: Games}
|
||||
- {id: list, cat: Other, desc: Other}
|
||||
- {id: close, cat: XXX, desc: XXX}
|
||||
- {id: music, cat: Audio, desc: "Music MP3"}
|
||||
- {id: sellsy, cat: Audio/Lossless, desc: "Music Lossless"}
|
||||
- {id: play-circle, cat: TV, desc: WEBTV}
|
||||
- {id: smile-o, cat: TV/Anime, desc: Anime}
|
||||
- {id: television, cat: TV, desc: TV}
|
||||
- {id: wrench, cat: PC, desc: Apps}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep]
|
||||
movie-search: [q]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: flaresolverr
|
||||
type: info
|
||||
label: FlareSolverr
|
||||
default: This site may use Cloudflare DDoS Protection, therefore Jackett requires <a href="https://github.com/Jackett/Jackett#configuring-flaresolverr" target="_blank">FlareSolver</a> to access it.
|
||||
|
||||
search:
|
||||
# https://torrentz2is.me/search/
|
||||
paths:
|
||||
- path: search/
|
||||
method: post
|
||||
- path: "search/{{ if .Keywords }}{{ .Keywords }}{{ else }}:latest:{{ end }}/category/all/page/2"
|
||||
method: post
|
||||
inputs:
|
||||
page: 2
|
||||
inputs:
|
||||
q: "{{ if .Keywords }}{{ .Keywords }}{{ else }}:latest:{{ end }}"
|
||||
category: all
|
||||
keywordsfilters:
|
||||
# the site uses % for wildcard
|
||||
- name: re_replace
|
||||
args: ["[^a-zA-Z0-9]+", "%"]
|
||||
|
||||
rows:
|
||||
selector: table > tbody > tr:has(i)
|
||||
filters:
|
||||
- name: andmatch
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: i
|
||||
attribute: class
|
||||
filters:
|
||||
- name: append
|
||||
args: " list"
|
||||
# extract the second class
|
||||
- name: split
|
||||
args: [" ", 1]
|
||||
# remove fa- prefix
|
||||
- name: replace
|
||||
args: ["fa-", ""]
|
||||
title:
|
||||
selector: div.btntitle a
|
||||
attribute: title
|
||||
details:
|
||||
# details page is only accessible via form and post which Cardigann does not support.
|
||||
text: "{{ .Config.sitelink }}"
|
||||
infohash:
|
||||
selector: input[name="id"]
|
||||
attribute: value
|
||||
date:
|
||||
selector: td:nth-child(6)
|
||||
filters:
|
||||
- name: re_replace
|
||||
args: ["(?i)sec", "seconds"]
|
||||
- name: re_replace
|
||||
args: ["(?i)min", "minutes"]
|
||||
- name: re_replace
|
||||
args: ["(?i)hr", "hours"]
|
||||
- name: re_replace
|
||||
args: ["(?i)mon", "months"]
|
||||
- name: re_replace
|
||||
args: ["(?i)yr", "years"]
|
||||
- name: append
|
||||
args: " ago"
|
||||
- name: timeago
|
||||
_size:
|
||||
selector: td:nth-child(5)
|
||||
size:
|
||||
text: "{{ if .Result._size }}{{ .Result._size }}{{ else }}0 B{{ end }}"
|
||||
seeders:
|
||||
selector: td:nth-child(3)
|
||||
leechers:
|
||||
selector: td:nth-child(4)
|
||||
downloadvolumefactor:
|
||||
text: 0
|
||||
uploadvolumefactor:
|
||||
text: 1
|
||||
# engine n/a
|
@@ -63,13 +63,13 @@ settings:
|
||||
asc: asc
|
||||
|
||||
login:
|
||||
path: http://foro.unionfansub.com/member.php
|
||||
path: https://foro.unionfansub.com/member.php
|
||||
method: post
|
||||
inputs:
|
||||
quick_username: "{{ .Config.username }}"
|
||||
quick_password: "{{ .Config.password }}"
|
||||
action: do_login
|
||||
url: "http://torrent.unionfansub.com/browse.php"
|
||||
url: "https://torrent.unionfansub.com/"
|
||||
quick_login: 1
|
||||
quick_remember: yes
|
||||
error:
|
||||
|
@@ -1,182 +0,0 @@
|
||||
---
|
||||
id: yingk
|
||||
name: YingK
|
||||
description: "YingK is a CHINESE Private Torrent Tracker for HD MOVIES / TV / GENERAL"
|
||||
language: zh-cn
|
||||
type: private
|
||||
encoding: UTF-8
|
||||
links:
|
||||
- https://yingk.com/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
- {id: 401, cat: Movies, desc: "Movies/电影"}
|
||||
- {id: 402, cat: TV, desc: "TV Series/电视剧"}
|
||||
- {id: 403, cat: TV, desc: "TV Shows/综艺"}
|
||||
- {id: 405, cat: TV/Anime, desc: "Animations/动漫"}
|
||||
- {id: 404, cat: TV/Documentary, desc: "Documentaries/纪录片"}
|
||||
- {id: 408, cat: Audio, desc: "Music/音乐"}
|
||||
- {id: 407, cat: TV/Sport, desc: "Sports/体育"}
|
||||
- {id: 406, cat: Audio/Video, desc: "MusicVideo/MV"}
|
||||
- {id: 412, cat: Books, desc: "Study/学习"}
|
||||
- {id: 409, cat: Other, desc: "Misc/其他"}
|
||||
|
||||
modes:
|
||||
search: [q]
|
||||
tv-search: [q, season, ep, imdbid]
|
||||
movie-search: [q, imdbid]
|
||||
music-search: [q]
|
||||
book-search: [q]
|
||||
|
||||
settings:
|
||||
- name: username
|
||||
type: text
|
||||
label: Username
|
||||
- name: password
|
||||
type: password
|
||||
label: Password
|
||||
- name: freeleech
|
||||
type: checkbox
|
||||
label: Search freeleech only
|
||||
default: false
|
||||
- name: english_title
|
||||
type: checkbox
|
||||
label: "Use English titles instead of Chinese ones (when available)."
|
||||
default: false
|
||||
- name: sort
|
||||
type: select
|
||||
label: Sort requested from site
|
||||
default: 4
|
||||
options:
|
||||
4: created
|
||||
7: seeders
|
||||
5: size
|
||||
1: title
|
||||
- name: type
|
||||
type: select
|
||||
label: Order requested from site
|
||||
default: desc
|
||||
options:
|
||||
desc: desc
|
||||
asc: asc
|
||||
- name: info_tpp
|
||||
type: info
|
||||
label: Results Per Page
|
||||
default: For best results, change the <b>Torrents per page:</b> setting to <b>100</b> on your account profile.
|
||||
|
||||
login:
|
||||
path: login.php
|
||||
method: form
|
||||
form: form[action="takelogin.php"]
|
||||
inputs:
|
||||
logintype: username
|
||||
username: "{{ .Config.username }}"
|
||||
password: "{{ .Config.password }}"
|
||||
logout: ""
|
||||
thispagewidth: yes
|
||||
error:
|
||||
- selector: td.embedded:has(h2:contains("姿势不正确"))
|
||||
test:
|
||||
path: index.php
|
||||
selector: a[href^="userdetails.php?id="] # no logout link anyplace?
|
||||
|
||||
search:
|
||||
paths:
|
||||
- path: torrents.php
|
||||
inputs:
|
||||
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
|
||||
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{ else }}{{ .Keywords }}{{ end }}"
|
||||
# 0 incldead, 1 active, 2 dead, 3 no rating
|
||||
incldead: 0
|
||||
# 0 all, 1 normal, 2 free, 3 2x, 4 2xfree, 5 50%, 6 2x50%, 7 30%, 8 all promotions
|
||||
spstate: "{{ if .Config.freeleech }}8{{ else }}0{{ end }}"
|
||||
# 0 all, 1 popular, 2 classic, 3 recomended, 4 normal
|
||||
picktype: 0
|
||||
# 0 title, 3 uploader, 4 movie info, 5 movie number, 6 torrent hash, 7 active
|
||||
search_area: "{{ if .Query.IMDBID }}5{{ else }}0{{ end }}"
|
||||
# 0 AND, 1 OR, 2 exact
|
||||
search_mode: 0
|
||||
sort: "{{ .Config.sort }}"
|
||||
type: "{{ .Config.type }}"
|
||||
# supports imdbid searches but does not display imdb links in results.
|
||||
|
||||
rows:
|
||||
selector: table.torrents > tbody > tr:not(:has(td[class="colhead"]))
|
||||
after: 1
|
||||
|
||||
fields:
|
||||
category:
|
||||
selector: a[href^="?cat="]
|
||||
attribute: href
|
||||
filters:
|
||||
- name: querystring
|
||||
args: cat
|
||||
title_raw:
|
||||
selector: a[href^="details.php?id="]
|
||||
title_raw:
|
||||
optional: true
|
||||
selector: a[title][href^="details.php?id="]
|
||||
attribute: title
|
||||
# note: final title processing is at the bottom after descriptions is fetched.
|
||||
details:
|
||||
selector: a[href^="details.php?id="]
|
||||
attribute: href
|
||||
download:
|
||||
selector: a[href^="download.php?id="]
|
||||
attribute: href
|
||||
imdb:
|
||||
selector: a[href*="imdb.com/title/tt"]
|
||||
attribute: href
|
||||
seeders:
|
||||
selector: td:nth-child(3)
|
||||
leechers:
|
||||
selector: td:nth-child(4)
|
||||
filters:
|
||||
- name: replace
|
||||
args: ["-1", "0"]
|
||||
grabs:
|
||||
selector: td:nth-child(5)
|
||||
date:
|
||||
# time type: time elapsed (default)
|
||||
selector: td:nth-child(9) > span[title]
|
||||
attribute: title
|
||||
optional: true
|
||||
filters:
|
||||
- name: append
|
||||
args: " +08:00" # CST
|
||||
- name: dateparse
|
||||
args: "2006-01-02 15:04:05 -07:00"
|
||||
date:
|
||||
# time added
|
||||
selector: td:nth-child(9):not(:has(span))
|
||||
optional: true
|
||||
filters:
|
||||
- name: append
|
||||
args: " +08:00" # CST
|
||||
- name: dateparse
|
||||
args: "2006-01-0215:04:05 -07:00"
|
||||
size:
|
||||
selector: td:nth-child(10)
|
||||
downloadvolumefactor:
|
||||
case:
|
||||
img.pro_free: 0
|
||||
img.pro_free2up: 0
|
||||
img.pro_50pctdown: 0.5
|
||||
img.pro_50pctdown2up: 0.5
|
||||
img.pro_30pctdown: 0.3
|
||||
"*": 1
|
||||
uploadvolumefactor:
|
||||
case:
|
||||
img.pro_50pctdown2up: 2
|
||||
img.pro_free2up: 2
|
||||
img.pro_2up: 2
|
||||
"*": 1
|
||||
description:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, img
|
||||
title_english:
|
||||
selector: td:nth-child(2)
|
||||
remove: a, img, b, span
|
||||
title:
|
||||
text: "{{ if and .Config.english_title .Result.title_english }}{{ .Result.title_english }}{{ else }}{{ .Result.title_raw }}{{ end }}"
|
||||
# engine n/a (likely based on NexusPHP)
|
@@ -8,6 +8,7 @@ encoding: UTF-8
|
||||
links:
|
||||
- https://yourbittorrent.com/
|
||||
- https://yourbittorrent2.com/
|
||||
- https://yourbittorrent.nocensor.space/
|
||||
legacylinks:
|
||||
- https://yourbittorrent.host/
|
||||
|
||||
|
@@ -9,8 +9,8 @@ followredirect: true
|
||||
links:
|
||||
- https://zooqle.com/
|
||||
- https://zooqle.unblockninja.com/
|
||||
- https://zooqle.unblockit.club/
|
||||
- https://zooqle.unblocked.monster/
|
||||
- https://zooqle.unblockit.onl/
|
||||
- https://zooqle.nocensor.space/
|
||||
legacylinks:
|
||||
- https://zooqle.unblockit.pro/
|
||||
- https://zooqle.unblockit.one/
|
||||
@@ -34,6 +34,8 @@ legacylinks:
|
||||
- https://zooqle.unblockit.ltd/
|
||||
- https://zooqle.unblockit.link/
|
||||
- https://zooqle.unblockit.buzz/
|
||||
- https://zooqle.unblocked.monster/
|
||||
- https://zooqle.unblockit.club/
|
||||
|
||||
caps:
|
||||
categorymappings:
|
||||
|
@@ -3,10 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -17,8 +15,6 @@ using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using WebClient = Jackett.Common.Utils.Clients.WebClient;
|
||||
@@ -36,12 +32,6 @@ namespace Jackett.Common.Indexers
|
||||
private string SearchUrl => SiteLink + "torrents.php";
|
||||
private string DetailsUrl => SiteLink + "torrents.php?id=";
|
||||
private string ReplaceMulti => ConfigData.ReplaceMulti.Value;
|
||||
private bool Latency => ConfigData.Latency.Value;
|
||||
private bool DevMode => ConfigData.DevMode.Value;
|
||||
private bool CacheMode => ConfigData.HardDriveCache.Value;
|
||||
private static string Directory => Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name.ToLower(), MethodBase.GetCurrentMethod().DeclaringType?.Name.ToLower());
|
||||
|
||||
private readonly Dictionary<string, string> emulatedBrowserHeaders = new Dictionary<string, string>();
|
||||
|
||||
private ConfigurationDataAbnormal ConfigData
|
||||
{
|
||||
@@ -76,8 +66,6 @@ namespace Jackett.Common.Indexers
|
||||
Language = "fr-fr";
|
||||
Encoding = Encoding.UTF8;
|
||||
Type = "private";
|
||||
// NET::ERR_CERT_DATE_INVALID expired 29 July 2020
|
||||
w.AddTrustedCertificate(new Uri(SiteLink).Host, "9cb32582b564256146616afddbdb8e7c94c428ed");
|
||||
|
||||
AddCategoryMapping("MOVIE|DVDR", TorznabCatType.MoviesDVD, "DVDR");
|
||||
AddCategoryMapping("MOVIE|DVDRIP", TorznabCatType.MoviesSD, "DVDRIP");
|
||||
@@ -115,34 +103,7 @@ namespace Jackett.Common.Indexers
|
||||
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;
|
||||
ValidateConfig();
|
||||
|
||||
// Building login form data
|
||||
var pairs = new Dictionary<string, string> {
|
||||
@@ -152,20 +113,9 @@ namespace Jackett.Common.Indexers
|
||||
{ "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.GetResultAsync(request);
|
||||
logger.Info("\nAbnormal - Perform loggin.. with " + LoginUrl);
|
||||
var response = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, LoginUrl, true);
|
||||
|
||||
// Test if we are logged in
|
||||
await ConfigureIfOK(response.Cookies, response.Cookies.Contains("session="), () =>
|
||||
@@ -179,11 +129,11 @@ namespace Jackett.Common.Indexers
|
||||
var left = dom.QuerySelector(".info").TextContent.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);
|
||||
logger.Info("Abnormal - Login failed: \"" + message + "\" and " + left + " tries left before being banned for 6 hours !", "error");
|
||||
throw new ExceptionWithConfigData("Abnormal - Login failed: " + message, configData);
|
||||
});
|
||||
|
||||
output("-> Login Success");
|
||||
logger.Info("-> Login Success");
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -206,41 +156,26 @@ namespace Jackett.Common.Indexers
|
||||
var qRowList = new List<IElement>();
|
||||
var searchTerm = query.GetQueryString();
|
||||
var searchUrl = SearchUrl;
|
||||
var nbResults = 0;
|
||||
var 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);
|
||||
var request = BuildQuery(searchTerm, query, searchUrl);
|
||||
|
||||
// Getting results & Store content
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(await queryExec(request));
|
||||
var dom = parser.ParseDocument(await QueryExecAsync(request));
|
||||
|
||||
try
|
||||
{
|
||||
// Find torrent rows
|
||||
var firstPageRows = findTorrentRows(dom);
|
||||
var firstPageRows = FindTorrentRows(dom);
|
||||
|
||||
// Add them to torrents list
|
||||
qRowList.AddRange(firstPageRows);
|
||||
|
||||
// Check if there are pagination links at bottom
|
||||
var qPagination = dom.QuerySelectorAll(".linkbox > a");
|
||||
int pageLinkCount;
|
||||
int nbResults;
|
||||
if (qPagination.Length > 0)
|
||||
{
|
||||
// Calculate numbers of pages available for this search query (Based on number results and number of torrents on first page)
|
||||
@@ -260,13 +195,13 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
output("\nNo result found for your query, please try another search term ...\n", "info");
|
||||
logger.Info("\nAbnormal - No 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 !");
|
||||
logger.Info("\nAbnormal - Found " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
|
||||
logger.Info("\nAbnormal - There 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)
|
||||
@@ -274,20 +209,17 @@ namespace Jackett.Common.Indexers
|
||||
// Starting with page #2
|
||||
for (var i = 2; i <= Math.Min(int.Parse(ConfigData.Pages.Value), pageLinkCount); i++)
|
||||
{
|
||||
output("\nProcessing page #" + i);
|
||||
|
||||
// Request our page
|
||||
latencyNow();
|
||||
logger.Info("\nAbnormal - Processing page #" + i);
|
||||
|
||||
// Build our query
|
||||
var pageRequest = buildQuery(searchTerm, query, searchUrl, i);
|
||||
var pageRequest = BuildQuery(searchTerm, query, searchUrl, i);
|
||||
|
||||
// Getting results & Store content
|
||||
parser = new HtmlParser();
|
||||
dom = parser.ParseDocument(await queryExec(pageRequest));
|
||||
dom = parser.ParseDocument(await QueryExecAsync(pageRequest));
|
||||
|
||||
// Process page results
|
||||
var additionalPageRows = findTorrentRows(dom);
|
||||
var additionalPageRows = FindTorrentRows(dom);
|
||||
|
||||
// Add them to torrents list
|
||||
qRowList.AddRange(additionalPageRows);
|
||||
@@ -297,11 +229,8 @@ namespace Jackett.Common.Indexers
|
||||
// Loop on results
|
||||
foreach (var row in qRowList)
|
||||
{
|
||||
output("\n=>> Torrent #" + (releases.Count + 1));
|
||||
|
||||
// ID
|
||||
var id = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(2) > a").GetAttribute("href"), @"\d+").Value);
|
||||
output("ID: " + id);
|
||||
|
||||
// Release Name
|
||||
var name = row.QuerySelector("td:nth-of-type(2) > a").TextContent;
|
||||
@@ -311,39 +240,22 @@ namespace Jackett.Common.Indexers
|
||||
var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])");
|
||||
name = regex.Replace(name, "$1" + ReplaceMulti + "$2");
|
||||
}
|
||||
output("Release: " + name);
|
||||
|
||||
// Category
|
||||
var categoryId = row.QuerySelector("td:nth-of-type(1) > a").GetAttribute("href").Replace("torrents.php?cat[]=", string.Empty);
|
||||
var newznab = MapTrackerCatToNewznab(categoryId);
|
||||
output("Category: " + MapTrackerCatToNewznab(categoryId).First().ToString() + " (" + categoryId + ")");
|
||||
|
||||
// Seeders
|
||||
var seeders = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(6)").TextContent, @"\d+").Value);
|
||||
output("Seeders: " + seeders);
|
||||
|
||||
// Leechers
|
||||
var leechers = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(7)").TextContent, @"\d+").Value);
|
||||
output("Leechers: " + leechers);
|
||||
|
||||
// Completed
|
||||
var completed = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(6)").TextContent, @"\d+").Value);
|
||||
output("Completed: " + completed);
|
||||
|
||||
// Size
|
||||
var sizeStr = row.QuerySelector("td:nth-of-type(5)").TextContent.Replace("Go", "gb").Replace("Mo", "mb").Replace("Ko", "kb");
|
||||
var size = ReleaseInfo.GetBytes(sizeStr);
|
||||
output("Size: " + sizeStr + " (" + size + " bytes)");
|
||||
var categoryId = row.QuerySelector("td:nth-of-type(1) > a").GetAttribute("href").Replace("torrents.php?cat[]=", string.Empty); // Category
|
||||
var newznab = MapTrackerCatToNewznab(categoryId); // Newznab Category
|
||||
var seeders = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(6)").TextContent, @"\d+").Value); // Seeders
|
||||
var leechers = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(7)").TextContent, @"\d+").Value); // Leechers
|
||||
var completed = ParseUtil.CoerceInt(Regex.Match(row.QuerySelector("td:nth-of-type(6)").TextContent, @"\d+").Value); // Completed
|
||||
var sizeStr = row.QuerySelector("td:nth-of-type(5)").TextContent.Replace("Go", "gb").Replace("Mo", "mb").Replace("Ko", "kb"); // Size
|
||||
var size = ReleaseInfo.GetBytes(sizeStr); // Size in bytes
|
||||
|
||||
// Publish DateToString
|
||||
var datestr = row.QuerySelector("span.time").GetAttribute("title");
|
||||
var dateLocal = DateTime.SpecifyKind(DateTime.ParseExact(datestr, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture), DateTimeKind.Unspecified);
|
||||
var date = TimeZoneInfo.ConvertTimeToUtc(dateLocal, FranceTz);
|
||||
output("Released on: " + date);
|
||||
|
||||
// Torrent Details URL
|
||||
var details = new Uri(DetailsUrl + id);
|
||||
output("Details: " + details.AbsoluteUri);
|
||||
|
||||
// Torrent Download URL
|
||||
Uri downloadLink = null;
|
||||
@@ -352,12 +264,11 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
// Download link available
|
||||
downloadLink = new Uri(SiteLink + link);
|
||||
output("Download Link: " + downloadLink.AbsoluteUri);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No download link available -- Must be on pending ( can't be downloaded now...)
|
||||
output("Download Link: Not available, torrent pending ? Skipping ...");
|
||||
logger.Info("Abnormal - Download Link: Not available, torrent pending ? Skipping ...");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -366,7 +277,6 @@ namespace Jackett.Common.Indexers
|
||||
if (row.QuerySelector("img[alt=\"Freeleech\"]") != null)
|
||||
{
|
||||
downloadVolumeFactor = 0;
|
||||
output("FreeLeech =)");
|
||||
}
|
||||
|
||||
// Building release infos
|
||||
@@ -387,6 +297,7 @@ namespace Jackett.Common.Indexers
|
||||
DownloadVolumeFactor = downloadVolumeFactor
|
||||
};
|
||||
releases.Add(release);
|
||||
logger.Info("Abnormal - Found Release: " + release.Title + "(" + id + ")");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -406,7 +317,7 @@ namespace Jackett.Common.Indexers
|
||||
/// <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)
|
||||
private string BuildQuery(string term, TorznabQuery query, string url, int page = 0)
|
||||
{
|
||||
var parameters = new NameValueCollection();
|
||||
var categoriesList = MapTorznabCapsToTrackers(query);
|
||||
@@ -451,7 +362,7 @@ namespace Jackett.Common.Indexers
|
||||
// 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);
|
||||
logger.Info("\nAbnormal - Builded query for \"" + term + "\"... " + url);
|
||||
|
||||
// Return our search url
|
||||
return url;
|
||||
@@ -462,77 +373,13 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<string> queryExec(string request)
|
||||
private async Task<string> QueryExecAsync(string request)
|
||||
{
|
||||
string 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;
|
||||
}
|
||||
// Querying tracker directly
|
||||
results = await QueryTrackerAsync(request);
|
||||
|
||||
/// <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<string> queryCache(string request)
|
||||
{
|
||||
string results;
|
||||
|
||||
// Create Directory if not exist
|
||||
System.IO.Directory.CreateDirectory(Directory);
|
||||
|
||||
// Clean Storage Provider Directory from outdated cached queries
|
||||
cleanCacheStorage();
|
||||
|
||||
// File Name
|
||||
var fileName = StringUtil.HashSHA1(request) + ".json";
|
||||
|
||||
// Create fingerprint for request
|
||||
var file = Path.Combine(Directory, fileName);
|
||||
|
||||
// Checking modes states
|
||||
if (File.Exists(file))
|
||||
{
|
||||
// File exist... loading it right now !
|
||||
output("Loading results from hard drive cache ..." + fileName);
|
||||
try
|
||||
{
|
||||
using (var fileReader = File.OpenText(file))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
results = (string)serializer.Deserialize(fileReader, typeof(string));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
output("Error loading cached results ! " + e.Message, "error");
|
||||
results = null;
|
||||
}
|
||||
}
|
||||
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 ..." + fileName);
|
||||
using (var fileWriter = File.CreateText(file))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Serialize(fileWriter, results);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -541,142 +388,31 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<string> queryTracker(string request)
|
||||
private async Task<string> QueryTrackerAsync(string request)
|
||||
{
|
||||
// Cache mode not enabled or cached file didn't exist for our query
|
||||
output("\nQuerying tracker for results....");
|
||||
logger.Info("\nAbnormal - Querying tracker for results....");
|
||||
|
||||
// Request our first page
|
||||
latencyNow();
|
||||
var results = await RequestWithCookiesAndRetryAsync(request, headers: emulatedBrowserHeaders);
|
||||
var results = await RequestWithCookiesAndRetryAsync(request);
|
||||
|
||||
// Return results from tracker
|
||||
return results.ContentString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Hard Drive Cache Storage
|
||||
/// </summary>
|
||||
/// <param name="force">Force Provider Folder deletion</param>
|
||||
private void cleanCacheStorage(bool 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
|
||||
{
|
||||
var 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 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);
|
||||
var 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>List of rows</returns>
|
||||
private IHtmlCollection<IElement> findTorrentRows(IHtmlDocument dom) =>
|
||||
private IHtmlCollection<IElement> FindTorrentRows(IHtmlDocument dom) =>
|
||||
dom.QuerySelectorAll(".torrent_table > tbody > tr:not(.colhead)");
|
||||
|
||||
/// <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 (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()
|
||||
private void ValidateConfig()
|
||||
{
|
||||
output("\nValidating Settings ... \n");
|
||||
logger.Info("\nAbnormal - Validating Settings ... \n");
|
||||
|
||||
// Check Username Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Username.Value))
|
||||
@@ -685,7 +421,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Username (auth) => " + ConfigData.Username.Value.ToString());
|
||||
logger.Info("Abnormal - Validated Setting -- Username (auth) => " + ConfigData.Username.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Password Setting
|
||||
@@ -695,7 +431,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
output("Validated Setting -- Password (auth) => " + ConfigData.Password.Value.ToString());
|
||||
logger.Info("Abnormal - Validated Setting -- Password (auth) => " + ConfigData.Password.Value.ToString());
|
||||
}
|
||||
|
||||
// Check Max Page Setting
|
||||
@@ -703,7 +439,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
try
|
||||
{
|
||||
output("Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
logger.Info("Abnormal - Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -714,116 +450,6 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -65,52 +65,16 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(15, TorznabCatType.TVAnime, "Adult Video");
|
||||
AddCategoryMapping(16, TorznabCatType.TVAnime, "Other");
|
||||
|
||||
// Configure the language select option
|
||||
var languageSelect = new SingleSelectConfigurationItem("Language", new Dictionary<string, string>
|
||||
{
|
||||
{"1", "English"},
|
||||
{"2", "Japanese"},
|
||||
{"3", "Polish"},
|
||||
{"4", "Serbo-Croatian" },
|
||||
{"5", "Dutch"},
|
||||
{"6", "Italian"},
|
||||
{"7", "Russian"},
|
||||
{"8", "German"},
|
||||
{"9", "Hungarian"},
|
||||
{"10", "French"},
|
||||
{"11", "Finnish"},
|
||||
{"12", "Vietnamese"},
|
||||
{"13", "Greek"},
|
||||
{"14", "Bulgarian"},
|
||||
{"15", "Spanish (Spain)" },
|
||||
{"16", "Portuguese (Brazil)" },
|
||||
{"17", "Portuguese (Portugal)" },
|
||||
{"18", "Swedish"},
|
||||
{"19", "Arabic"},
|
||||
{"20", "Danish"},
|
||||
{"21", "Chinese (Simplified)" },
|
||||
{"22", "Bengali"},
|
||||
{"23", "Romanian"},
|
||||
{"24", "Czech"},
|
||||
{"25", "Mongolian"},
|
||||
{"26", "Turkish"},
|
||||
{"27", "Indonesian"},
|
||||
{"28", "Korean"},
|
||||
{"29", "Spanish (LATAM)" },
|
||||
{"30", "Persian"},
|
||||
{"31", "Malaysian"}
|
||||
})
|
||||
{ Value = "1" };
|
||||
configData.AddDynamic("languageid", languageSelect);
|
||||
AddLanguageConfiguration();
|
||||
|
||||
// Configure the sort selects
|
||||
var sortBySelect = new SingleSelectConfigurationItem("Sort by", new Dictionary<string, string>
|
||||
{
|
||||
{"upload_timestamp", "created"},
|
||||
{"seeders", "seeders"},
|
||||
{"size", "size"},
|
||||
{"filename", "title"}
|
||||
})
|
||||
{"upload_timestamp", "created"},
|
||||
{"seeders", "seeders"},
|
||||
{"size", "size"},
|
||||
{"filename", "title"}
|
||||
})
|
||||
{ Value = "upload_timestamp" };
|
||||
configData.AddDynamic("sortrequestedfromsite", sortBySelect);
|
||||
|
||||
@@ -125,8 +89,47 @@ namespace Jackett.Common.Indexers
|
||||
EnableConfigurableRetryAttempts();
|
||||
}
|
||||
|
||||
private string GetLang => ((SingleSelectConfigurationItem)configData.GetDynamic("languageid")).Value;
|
||||
|
||||
private void AddLanguageConfiguration()
|
||||
{
|
||||
// Configure the language select option
|
||||
var languageSelect = new MultiSelectConfigurationItem("Language (None ticked = ALL)", new Dictionary<string, string>
|
||||
{
|
||||
{"1", "English"},
|
||||
{"2", "Japanese"},
|
||||
{"3", "Polish"},
|
||||
{"4", "Serbo-Croatian"},
|
||||
{"5", "Dutch"},
|
||||
{"6", "Italian"},
|
||||
{"7", "Russian"},
|
||||
{"8", "German"},
|
||||
{"9", "Hungarian"},
|
||||
{"10", "French"},
|
||||
{"11", "Finnish"},
|
||||
{"12", "Vietnamese"},
|
||||
{"13", "Greek"},
|
||||
{"14", "Bulgarian"},
|
||||
{"15", "Spanish (Spain)"},
|
||||
{"16", "Portuguese (Brazil)"},
|
||||
{"17", "Portuguese (Portugal)"},
|
||||
{"18", "Swedish"},
|
||||
{"19", "Arabic"},
|
||||
{"20", "Danish"},
|
||||
{"21", "Chinese (Simplified)"},
|
||||
{"22", "Bengali"},
|
||||
{"23", "Romanian"},
|
||||
{"24", "Czech"},
|
||||
{"25", "Mongolian"},
|
||||
{"26", "Turkish"},
|
||||
{"27", "Indonesian"},
|
||||
{"28", "Korean"},
|
||||
{"29", "Spanish (LATAM)"},
|
||||
{"30", "Persian"},
|
||||
{"31", "Malaysian"}
|
||||
})
|
||||
{ Values = new[] { "" } };
|
||||
configData.AddDynamic("languageid", languageSelect);
|
||||
}
|
||||
|
||||
private string GetSortBy => ((SingleSelectConfigurationItem)configData.GetDynamic("sortrequestedfromsite")).Value;
|
||||
|
||||
private string GetOrder => ((SingleSelectConfigurationItem)configData.GetDynamic("orderrequestedfromsite")).Value;
|
||||
@@ -144,6 +147,15 @@ namespace Jackett.Common.Indexers
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the selected languages, formatted so that they can be used in a query string.
|
||||
/// </summary>
|
||||
private string GetLanguagesForQuery()
|
||||
{
|
||||
var languagesConfig = (MultiSelectConfigurationItem)configData.GetDynamic("languageid");
|
||||
return string.Join(",", languagesConfig.Values);
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
// Prepare the search query
|
||||
@@ -152,7 +164,6 @@ namespace Jackett.Common.Indexers
|
||||
{ "q", query.SearchTerm ?? string.Empty },
|
||||
{ "s", GetSortBy },
|
||||
{ "o", GetOrder },
|
||||
{ "lang_id", GetLang },
|
||||
{ "group_id", "0" } // No group
|
||||
};
|
||||
|
||||
@@ -163,14 +174,18 @@ namespace Jackett.Common.Indexers
|
||||
if (searchCategories.Count > 0)
|
||||
catString = "&id=" + string.Join(",", searchCategories);
|
||||
|
||||
// Get Selected Languages
|
||||
// AniDex throws errors when the commas between language IDs are url encoded.
|
||||
var langIds = "&lang_id=" + GetLanguagesForQuery();
|
||||
|
||||
// Make search request
|
||||
var searchUri = GetAbsoluteUrl("?" + queryParameters.GetQueryString() + catString);
|
||||
var searchUri = GetAbsoluteUrl("?" + queryParameters.GetQueryString() + catString + langIds);
|
||||
var response = await RequestWithCookiesAndRetryAsync(searchUri.AbsoluteUri);
|
||||
|
||||
// Check for DDOS Guard
|
||||
if (response.Status == HttpStatusCode.Forbidden)
|
||||
{
|
||||
await ConfigureDDoSGuardCookie();
|
||||
await ConfigureDDoSGuardCookieAsync();
|
||||
response = await RequestWithCookiesAndRetryAsync(searchUri.AbsoluteUri);
|
||||
}
|
||||
|
||||
@@ -195,10 +210,12 @@ namespace Jackett.Common.Indexers
|
||||
foreach (var r in rows)
|
||||
try
|
||||
{
|
||||
var language = "";
|
||||
var release = new ReleaseInfo();
|
||||
|
||||
release.Category = ParseValueFromRow(r, nameof(release.Category), "td:nth-child(1) a", (e) => MapTrackerCatToNewznab(e.Attributes["href"].Value.Substring(5)));
|
||||
release.Title = ParseStringValueFromRow(r, nameof(release.Title), "td:nth-child(3) span");
|
||||
language = ParseValueFromRow(r, nameof(language), "td:nth-child(1) img", (e) => e.Attributes["title"].Value);
|
||||
release.Title = ParseStringValueFromRow(r, nameof(release.Title), "td:nth-child(3) span") + " " + language;
|
||||
release.Link = ParseValueFromRow(r, nameof(release.Link), "a[href^=\"/dl/\"]", (e) => GetAbsoluteUrl(e.Attributes["href"].Value));
|
||||
release.MagnetUri = ParseValueFromRow(r, nameof(release.MagnetUri), "a[href^=\"magnet:?\"]", (e) => new Uri(e.Attributes["href"].Value));
|
||||
release.Size = ParseValueFromRow(r, nameof(release.Size), "td:nth-child(7)", (e) => ReleaseInfo.GetBytes(e.Text()));
|
||||
@@ -226,7 +243,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ConfigureDDoSGuardCookie()
|
||||
private async Task ConfigureDDoSGuardCookieAsync()
|
||||
{
|
||||
const string ddosPostUrl = "https://check.ddos-guard.net/check.js";
|
||||
var response = await RequestWithCookiesAsync(ddosPostUrl, string.Empty);
|
||||
|
276
src/Jackett.Common/Indexers/BeyondHDAPI.cs
Normal file
276
src/Jackett.Common/Indexers/BeyondHDAPI.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class BeyondHDAPI : BaseWebIndexer
|
||||
{
|
||||
private readonly string APIBASE = "https://beyond-hd.me/api/torrents/";
|
||||
|
||||
private new ConfigurationDataAPIKeyAndRSSKey configData
|
||||
{
|
||||
get => (ConfigurationDataAPIKeyAndRSSKey)base.configData;
|
||||
set => base.configData = value;
|
||||
}
|
||||
|
||||
public BeyondHDAPI(IIndexerConfigurationService configService, WebClient wc, Logger l,
|
||||
IProtectionService ps, ICacheService cs)
|
||||
: base(id: "beyond-hd-api",
|
||||
name: "Beyond-HD (API)",
|
||||
description: "Without BeyondHD, your HDTV is just a TV",
|
||||
link: "https://beyond-hd.me/",
|
||||
caps: new TorznabCapabilities
|
||||
{
|
||||
LimitsDefault = 100,
|
||||
LimitsMax = 100,
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
|
||||
}
|
||||
},
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
cacheService: cs,
|
||||
configData: new ConfigurationDataAPIKeyAndRSSKey())
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping("Movies", TorznabCatType.Movies);
|
||||
AddCategoryMapping("TV", TorznabCatType.TV);
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
IsConfigured = false;
|
||||
try
|
||||
{
|
||||
var results = await PerformQuery(new TorznabQuery());
|
||||
if (results.Count() == 0)
|
||||
throw new Exception("Testing returned no results!");
|
||||
IsConfigured = true;
|
||||
SaveConfig();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ExceptionWithConfigData(e.Message, configData);
|
||||
}
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var apiKey = configData.ApiKey.Value;
|
||||
var apiUrl = $"{APIBASE}{apiKey}";
|
||||
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>
|
||||
{
|
||||
{ BHDParams.action, "search" },
|
||||
{ BHDParams.rsskey, configData.RSSKey.Value },
|
||||
{ BHDParams.search, query.SanitizedSearchTerm },
|
||||
};
|
||||
|
||||
if (query.IsTVSearch)
|
||||
{
|
||||
postData.Add(BHDParams.categories, "TV");
|
||||
|
||||
if (query.Season != 0)
|
||||
postData[BHDParams.search] = $"{query.SanitizedSearchTerm} {query.GetEpisodeSearchString()}";
|
||||
}
|
||||
else if (query.IsMovieSearch)
|
||||
{
|
||||
postData.Add(BHDParams.categories, "Movies");
|
||||
}
|
||||
|
||||
var imdbId = ParseUtil.GetImdbID(query.ImdbID);
|
||||
if (imdbId != null)
|
||||
postData.Add(BHDParams.imdb_id, imdbId.ToString());
|
||||
if (query.IsTmdbQuery)
|
||||
postData.Add(BHDParams.tmdb_id, query.TmdbID.Value.ToString());
|
||||
|
||||
var bhdResponse = await GetBHDResponse(apiUrl, postData);
|
||||
var releaseInfos = bhdResponse.results.Select(mapToReleaseInfo);
|
||||
|
||||
return releaseInfos;
|
||||
}
|
||||
|
||||
private ReleaseInfo mapToReleaseInfo(BHDResult bhdResult)
|
||||
{
|
||||
var uri = new Uri(bhdResult.url);
|
||||
var downloadUri = new Uri(bhdResult.download_url);
|
||||
|
||||
var releaseInfo = new ReleaseInfo
|
||||
{
|
||||
Title = bhdResult.name,
|
||||
Seeders = bhdResult.seeders,
|
||||
Guid = new Uri(bhdResult.url),
|
||||
Details = new Uri(bhdResult.url),
|
||||
Link = downloadUri,
|
||||
InfoHash = bhdResult.info_hash,
|
||||
Peers = bhdResult.leechers + bhdResult.seeders,
|
||||
Grabs = bhdResult.times_completed,
|
||||
PublishDate = bhdResult.created_at,
|
||||
Size = bhdResult.size,
|
||||
Category = MapTrackerCatToNewznab(bhdResult.category)
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(bhdResult.imdb_id))
|
||||
releaseInfo.Imdb = ParseUtil.GetImdbID(bhdResult.imdb_id);
|
||||
|
||||
releaseInfo.DownloadVolumeFactor = 1;
|
||||
releaseInfo.UploadVolumeFactor = 1;
|
||||
|
||||
if (bhdResult.freeleech == 1 || bhdResult.limited == 1)
|
||||
releaseInfo.DownloadVolumeFactor = 0;
|
||||
if (bhdResult.promo25 == 1)
|
||||
releaseInfo.DownloadVolumeFactor = .75;
|
||||
if (bhdResult.promo50 == 1)
|
||||
releaseInfo.DownloadVolumeFactor = .50;
|
||||
if (bhdResult.promo75 == 1)
|
||||
releaseInfo.DownloadVolumeFactor = .25;
|
||||
|
||||
return releaseInfo;
|
||||
}
|
||||
|
||||
private async Task<BHDResponse> GetBHDResponse(string apiUrl, Dictionary<string, string> postData)
|
||||
{
|
||||
var request = new WebRequest
|
||||
{
|
||||
PostData = postData,
|
||||
Type = RequestType.POST,
|
||||
Url = apiUrl
|
||||
};
|
||||
|
||||
var response = await webclient.GetResultAsync(request);
|
||||
|
||||
var bhdresponse = JsonConvert.DeserializeObject<BHDResponse>(response.ContentString);
|
||||
return bhdresponse;
|
||||
}
|
||||
|
||||
internal class BHDParams
|
||||
{
|
||||
internal const string action = "action"; // string - The torrents endpoint action you wish to perform. (search)
|
||||
internal const string rsskey = "rsskey"; // string - Your personal RSS key (RID) if you wish for results to include the uploaded_by and download_url fields
|
||||
internal const string page = "page"; // int - The page number of the results. Only if the result set has more than 100 total matches.
|
||||
|
||||
internal const string search = "search"; // string - The torrent name. It does support !negative searching. Example: Christmas Movie
|
||||
internal const string info_hash = "info_hash"; // string - The torrent info_hash. This is an exact match.
|
||||
internal const string folder_name = "folder_name"; // string - The torrent folder name. This is an exact match.file_name string The torrent included file names. This is an exact match.
|
||||
internal const string size = "size"; // int - The torrent size. This is an exact match.
|
||||
internal const string uploaded_by = "uploaded_by"; // string - The uploaders username. Only non anonymous results will be returned.
|
||||
internal const string imdb_id = "imdb_id"; // int - The ID of the matching IMDB page.
|
||||
internal const string tmdb_id = "tmdb_id"; // int - The ID of the matching TMDB page.
|
||||
internal const string categories = "categories"; // string - Any categories separated by comma(s). TV, Movies)
|
||||
internal const string types = "types"; // string - Any types separated by comma(s). BD Remux, 1080p, etc.)
|
||||
internal const string sources = "sources"; // string - Any sources separated by comma(s). Blu-ray, WEB, DVD, etc.)
|
||||
internal const string genres = "genres"; // string - Any genres separated by comma(s). Action, Anime, StandUp, Western, etc.)
|
||||
internal const string groups = "groups"; // string - Any internal release groups separated by comma(s).FraMeSToR, BHDStudio, BeyondHD, RPG, iROBOT, iFT, ZR, MKVULTRA
|
||||
internal const string freeleech = "freeleech"; // int - The torrent freeleech status. 1 = Must match.
|
||||
internal const string limited = "limited"; // int - The torrent limited UL promo. 1 = Must match.
|
||||
internal const string promo25 = "promo25"; // int - The torrent 25% promo. 1 = Must match.
|
||||
internal const string promo50 = "promo50"; // int - The torrent 50% promo. 1 = Must match.
|
||||
internal const string promo75 = "promo75"; // int - The torrent 75% promo. 1 = Must match.
|
||||
internal const string refund = "refund"; // int - The torrent refund promo. 1 = Must match.
|
||||
internal const string rescue = "rescue"; // int - The torrent rescue promo. 1 = Must match.
|
||||
internal const string rewind = "rewind"; // int - The torrent rewind promo. 1 = Must match.
|
||||
internal const string stream = "stream"; // int - The torrent Stream Optimized flag. 1 = Must match.
|
||||
internal const string sd = "sd"; // int - The torrent SD flag. 1 = Must match.
|
||||
internal const string pack = "pack"; // int - The torrent TV pack flag. 1 = Must match.
|
||||
internal const string h264 = "h264"; // int - The torrent x264/h264 codec flag. 1 = Must match.
|
||||
internal const string h265 = "h265"; // int - The torrent x265/h265 codec flag. 1 = Must match.
|
||||
internal const string alive = "alive"; // int - The torrent has at least 1 seeder. 1 = Must match.
|
||||
internal const string dying = "dying"; // int - The torrent has less than 3 seeders. 1 = Must match.
|
||||
internal const string dead = "dead"; // int - The torrent has no seeders. 1 = Must match.
|
||||
internal const string reseed = "reseed"; // int - The torrent has no seeders and an active reseed request. 1 = Must match.
|
||||
internal const string seeding = "seeding"; // int - The torrent is seeded by you. 1 = Must match.
|
||||
internal const string leeching = "leeching"; // int - The torrent is being leeched by you. 1 = Must match.
|
||||
internal const string completed = "completed"; // int - The torrent has been completed by you. 1 = Must match.
|
||||
internal const string incomplete = "incomplete"; // int - The torrent has not been completed by you. 1 = Must match.
|
||||
internal const string notdownloaded = "notdownloaded"; // int - The torrent has not been downloaded you. 1 = Must match.
|
||||
internal const string min_bhd = "min_bhd"; // int - The minimum BHD rating.
|
||||
internal const string vote_bhd = "vote_bhd"; // int - The minimum number of BHD votes.
|
||||
internal const string min_imdb = "min_imdb"; // int - The minimum IMDb rating.
|
||||
internal const string vote_imdb = "vote_imdb"; // int - The minimum number of IMDb votes.
|
||||
internal const string min_tmdb = "min_tmdb"; // int - The minimum TMDb rating.
|
||||
internal const string vote_tmdb = "vote_tmdb"; // int - The minimum number of TDMb votes.
|
||||
internal const string min_year = "min_year"; // int - The earliest release year.
|
||||
internal const string max_year = "max_year"; // int - The latest release year.
|
||||
internal const string sort = "sort"; // string - Field to sort results by. (bumped_at, created_at, seeders, leechers, times_completed, size, name, imdb_rating, tmdb_rating, bhd_rating). Default is bumped_at
|
||||
internal const string order = "order"; // string - The direction of the sort of results. (asc, desc). Default is desc
|
||||
|
||||
// Most of the comma separated fields are OR searches.
|
||||
internal const string features = "features"; // string - Any features separated by comma(s). DV, HDR10, HDR10P, Commentary)
|
||||
internal const string countries = "countries"; // string - Any production countries separated by comma(s). France, Japan, etc.)
|
||||
internal const string languages = "languages"; // string - Any spoken languages separated by comma(s). French, English, etc.)
|
||||
internal const string audios = "audios"; // string - Any audio tracks separated by comma(s). English, Japanese,etc.)
|
||||
internal const string subtitles = "subtitles"; // string - Any subtitles separated by comma(s). Dutch, Finnish, Swedish, etc.)
|
||||
|
||||
}
|
||||
|
||||
class BHDResponse
|
||||
{
|
||||
public int status_code { get; set; } // The status code of the post request. (0 = Failed and 1 = Success)
|
||||
public int page { get; set; } // The current page of results that you're on.
|
||||
public int total_pages { get; set; } // int The total number of pages of results matching your query.
|
||||
public int total_results { get; set; } // The total number of results matching your query.
|
||||
public bool success { get; set; } // The status of the call. (True = Success, False = Error)
|
||||
public BHDResult[] results { get; set; } // The results that match your query.
|
||||
}
|
||||
|
||||
class BHDResult
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
public string folder_name { get; set; }
|
||||
public string info_hash { get; set; }
|
||||
public long size { get; set; }
|
||||
public string uploaded_by { get; set; }
|
||||
public string category { get; set; }
|
||||
public string type { get; set; }
|
||||
public int seeders { get; set; }
|
||||
public int leechers { get; set; }
|
||||
public int times_completed { get; set; }
|
||||
public string imdb_id { get; set; }
|
||||
public string tmdb_id { get; set; }
|
||||
public decimal bhd_rating { get; set; }
|
||||
public decimal tmdb_rating { get; set; }
|
||||
public decimal imdb_rating { get; set; }
|
||||
public int tv_pack { get; set; }
|
||||
public int promo25 { get; set; }
|
||||
public int promo50 { get; set; }
|
||||
public int promo75 { get; set; }
|
||||
public int freeleech { get; set; }
|
||||
public int rewind { get; set; }
|
||||
public int refund { get; set; }
|
||||
public int limited { get; set; }
|
||||
public int rescue { get; set; }
|
||||
public DateTime bumped_at { get; set; }
|
||||
public DateTime created_at { get; set; }
|
||||
public string url { get; set; }
|
||||
public string download_url { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -1129,7 +1129,7 @@ namespace Jackett.Common.Indexers
|
||||
return Element.QuerySelector(Selector);
|
||||
}
|
||||
|
||||
protected string handleSelector(selectorBlock Selector, IElement Dom, Dictionary<string, object> variables = null)
|
||||
protected string handleSelector(selectorBlock Selector, IElement Dom, Dictionary<string, object> variables = null, bool required = true)
|
||||
{
|
||||
if (Selector.Text != null)
|
||||
{
|
||||
@@ -1147,7 +1147,9 @@ namespace Jackett.Common.Indexers
|
||||
selection = QuerySelector(Dom, Selector.Selector);
|
||||
if (selection == null)
|
||||
{
|
||||
throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", Selector.Selector, Dom.ToHtmlPretty()));
|
||||
if (required)
|
||||
throw new Exception(string.Format("Selector \"{0}\" didn't match {1}", Selector.Selector, Dom.ToHtmlPretty()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,13 +1172,21 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
}
|
||||
if (value == null)
|
||||
throw new Exception(string.Format("None of the case selectors \"{0}\" matched {1}", string.Join(",", Selector.Case), selection.ToHtmlPretty()));
|
||||
{
|
||||
if (required)
|
||||
throw new Exception(string.Format("None of the case selectors \"{0}\" matched {1}", string.Join(",", Selector.Case), selection.ToHtmlPretty()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (Selector.Attribute != null)
|
||||
{
|
||||
value = selection.GetAttribute(Selector.Attribute);
|
||||
if (value == null)
|
||||
throw new Exception(string.Format("Attribute \"{0}\" is not set for element {1}", Selector.Attribute, selection.ToHtmlPretty()));
|
||||
{
|
||||
if (required)
|
||||
throw new Exception(string.Format("Attribute \"{0}\" is not set for element {1}", Selector.Attribute, selection.ToHtmlPretty()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1401,9 +1411,16 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
string value = null;
|
||||
var variablesKey = ".Result." + FieldName;
|
||||
var isOptional = OptionalFields.Contains(Field.Key) || FieldModifiers.Contains("optional") || Field.Value.Optional;
|
||||
try
|
||||
{
|
||||
value = handleSelector(Field.Value, Row, variables);
|
||||
value = handleSelector(Field.Value, Row, variables, !isOptional);
|
||||
if (isOptional && value == null)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (FieldName)
|
||||
{
|
||||
case "download":
|
||||
@@ -1560,7 +1577,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
if (!variables.ContainsKey(variablesKey))
|
||||
variables[variablesKey] = null;
|
||||
if (OptionalFields.Contains(Field.Key) || FieldModifiers.Contains("optional") || Field.Value.Optional)
|
||||
if (isOptional)
|
||||
{
|
||||
variables[variablesKey] = null;
|
||||
continue;
|
||||
|
@@ -23,9 +23,10 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
private const int MaxItemsPerPage = 15;
|
||||
private const int MaxSearchPageLimit = 6; // 15 items per page * 6 pages = 90
|
||||
private string _language;
|
||||
|
||||
public override string[] AlternativeSiteLinks { get; protected set; } = {
|
||||
public override string[] LegacySiteLinks { get; protected set; } = {
|
||||
"https://www.cinecalidad.to/",
|
||||
"https://www.cinecalidad.im/", // working but outdated, maybe copycat
|
||||
"https://www.cinecalidad.is/",
|
||||
"https://www.cinecalidad.li/",
|
||||
"https://www.cinecalidad.eu/",
|
||||
@@ -34,18 +35,14 @@ namespace Jackett.Common.Indexers
|
||||
"https://cinecalidad.mrunblock.icu/"
|
||||
};
|
||||
|
||||
public override string[] LegacySiteLinks { get; protected set; } = {
|
||||
"https://www.cinecalidad.to/",
|
||||
"https://www.cinecalidad.im/" // working but outdated, maybe copycat
|
||||
};
|
||||
|
||||
public Cinecalidad(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
|
||||
ICacheService cs)
|
||||
: base(id: "cinecalidad",
|
||||
name: "Cinecalidad",
|
||||
description: "Películas Full HD en Castellano y Latino Dual.",
|
||||
link: "https://www.cinecalidad.is/",
|
||||
caps: new TorznabCapabilities {
|
||||
description: "Películas Full HD en Latino y Inglés Dual.",
|
||||
link: "https://www.cine-calidad.com/",
|
||||
caps: new TorznabCapabilities
|
||||
{
|
||||
MovieSearchParams = new List<MovieSearchParam> { MovieSearchParam.Q }
|
||||
},
|
||||
configService: configService,
|
||||
@@ -59,24 +56,12 @@ namespace Jackett.Common.Indexers
|
||||
Language = "es-es";
|
||||
Type = "public";
|
||||
|
||||
var language = new SingleSelectConfigurationItem("Select language", new Dictionary<string, string>
|
||||
{
|
||||
{"castellano", "Castilian Spanish"},
|
||||
{"latino", "Latin American Spanish"}
|
||||
})
|
||||
{
|
||||
Value = "castellano"
|
||||
};
|
||||
configData.AddDynamic("language", language);
|
||||
|
||||
AddCategoryMapping(1, TorznabCatType.MoviesHD);
|
||||
}
|
||||
|
||||
public override void LoadValuesFromJson(JToken jsonConfig, bool useProtectionService = false)
|
||||
{
|
||||
base.LoadValuesFromJson(jsonConfig, useProtectionService);
|
||||
var language = (SingleSelectConfigurationItem)configData.GetDynamic("language");
|
||||
_language = language?.Value ?? "castellano";
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
@@ -95,8 +80,6 @@ namespace Jackett.Common.Indexers
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var templateUrl = SiteLink;
|
||||
if (_language.Equals("castellano"))
|
||||
templateUrl += "espana/";
|
||||
templateUrl += "{0}"; // placeholder for page
|
||||
|
||||
var maxPages = 2; // we scrape only 2 pages for recent torrents
|
||||
@@ -137,7 +120,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
var parser = new HtmlParser();
|
||||
var dom = parser.ParseDocument(results.ContentString);
|
||||
var protectedLink = dom.QuerySelector("a[service=BitTorrent]").GetAttribute("href");
|
||||
var protectedLink = dom.QuerySelector("li:contains('Torrent')").ParentElement.GetAttribute("href");
|
||||
if (protectedLink.Contains("/ouo.io/"))
|
||||
{
|
||||
// protected link =>
|
||||
@@ -178,11 +161,11 @@ namespace Jackett.Common.Indexers
|
||||
var title = qImg.GetAttribute("title");
|
||||
if (!CheckTitleMatchWords(query.GetQueryString(), title))
|
||||
continue; // skip if it doesn't contain all words
|
||||
title += _language.Equals("castellano") ? " MULTi/SPANiSH" : " MULTi/LATiN SPANiSH";
|
||||
title += " 1080p BDRip x264";
|
||||
title += " MULTi LATiN SPANiSH 1080p BDRip x264";
|
||||
|
||||
var poster = new Uri(GetAbsoluteUrl(qImg.GetAttribute("src")));
|
||||
var link = new Uri(GetAbsoluteUrl(row.QuerySelector("a").GetAttribute("href")));
|
||||
var extract = row.QuerySelector("noscript").InnerHtml.Split('\'');
|
||||
var link = new Uri(GetAbsoluteUrl(extract[1]));
|
||||
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
|
@@ -1,307 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal class EliteTracker : BaseWebIndexer
|
||||
{
|
||||
private string LoginUrl => SiteLink + "takelogin.php";
|
||||
private string BrowseUrl => SiteLink + "browse.php";
|
||||
private new ConfigurationDataEliteTracker configData => (ConfigurationDataEliteTracker)base.configData;
|
||||
|
||||
public EliteTracker(IIndexerConfigurationService configService, WebClient webClient, Logger logger,
|
||||
IProtectionService ps, ICacheService cs)
|
||||
: base(id: "elitetracker",
|
||||
name: "Elite-Tracker",
|
||||
description: "French Torrent Tracker",
|
||||
link: "https://elite-tracker.net/",
|
||||
caps: new TorznabCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
},
|
||||
configService: configService,
|
||||
logger: logger,
|
||||
p: ps,
|
||||
cacheService: cs,
|
||||
client: webClient,
|
||||
configData: new ConfigurationDataEliteTracker()
|
||||
)
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "fr-fr";
|
||||
Type = "private";
|
||||
|
||||
AddCategoryMapping(27, TorznabCatType.TVAnime, "Animation/Animes");
|
||||
AddCategoryMapping(90, TorznabCatType.TVAnime, "Animes - 3D");
|
||||
AddCategoryMapping(99, TorznabCatType.TVAnime, "Animes - 4K");
|
||||
AddCategoryMapping(63, TorznabCatType.TVAnime, "Animes - DVD");
|
||||
AddCategoryMapping(56, TorznabCatType.TVAnime, "Animes - HD");
|
||||
AddCategoryMapping(89, TorznabCatType.TVAnime, "Animes - HDRip");
|
||||
AddCategoryMapping(87, TorznabCatType.TVAnime, "Animes - Pack");
|
||||
AddCategoryMapping(88, TorznabCatType.TVAnime, "Animes - SD");
|
||||
AddCategoryMapping(59, TorznabCatType.TVAnime, "Animes - Serie");
|
||||
|
||||
AddCategoryMapping(3, TorznabCatType.PC0day, "APPLICATION");
|
||||
AddCategoryMapping(74, TorznabCatType.PCMobileAndroid, "APPLICATION - ANDROID");
|
||||
AddCategoryMapping(57, TorznabCatType.PCMobileiOS, "APPLICATION - IPHONE");
|
||||
AddCategoryMapping(6, TorznabCatType.PC0day, "APPLICATION - LINUX");
|
||||
AddCategoryMapping(5, TorznabCatType.PCMac, "APPLICATION - MAC");
|
||||
AddCategoryMapping(4, TorznabCatType.PC0day, "APPLICATION - WINDOWS");
|
||||
|
||||
AddCategoryMapping(38, TorznabCatType.TVDocumentary, "DOCUMENTAIRES");
|
||||
AddCategoryMapping(97, TorznabCatType.TVDocumentary, "DOCUMENTAIRES - PACK");
|
||||
|
||||
AddCategoryMapping(34, TorznabCatType.Books, "EBOOKS");
|
||||
AddCategoryMapping(86, TorznabCatType.Books, "EBOOKS - ABOOKS");
|
||||
|
||||
AddCategoryMapping(48, TorznabCatType.MoviesHD, "FiLMS HD");
|
||||
AddCategoryMapping(51, TorznabCatType.MoviesHD, "FiLMS HD - 1080p");
|
||||
AddCategoryMapping(98, TorznabCatType.MoviesUHD, "FiLMS HD - 2160p");
|
||||
AddCategoryMapping(70, TorznabCatType.Movies3D, "FiLMS HD - 3D");
|
||||
AddCategoryMapping(84, TorznabCatType.MoviesUHD, "FiLMS HD - 4K");
|
||||
AddCategoryMapping(50, TorznabCatType.MoviesHD, "FiLMS HD - 720P");
|
||||
AddCategoryMapping(49, TorznabCatType.MoviesBluRay, "FiLMS HD - BluRay");
|
||||
AddCategoryMapping(78, TorznabCatType.MoviesHD, "FiLMS HD - HDRip");
|
||||
AddCategoryMapping(105, TorznabCatType.MoviesUHD, "FiLMS HD - VOSTFR 4k");
|
||||
AddCategoryMapping(95, TorznabCatType.MoviesHD, "FiLMS HD - VOSTFR HD");
|
||||
AddCategoryMapping(85, TorznabCatType.MoviesHD, "FiLMS HD - x265");
|
||||
|
||||
AddCategoryMapping(7, TorznabCatType.Movies, "FiLMS SD");
|
||||
AddCategoryMapping(91, TorznabCatType.Movies3D, "FiLMS SD - 3D");
|
||||
AddCategoryMapping(11, TorznabCatType.MoviesDVD, "FiLMS SD - DVD");
|
||||
AddCategoryMapping(53, TorznabCatType.MoviesSD, "FiLMS SD - DVD-SCREENER");
|
||||
AddCategoryMapping(9, TorznabCatType.MoviesDVD, "FiLMS SD - R5");
|
||||
AddCategoryMapping(8, TorznabCatType.MoviesSD, "FiLMS SD - SCREENER");
|
||||
AddCategoryMapping(10, TorznabCatType.MoviesSD, "FiLMS SD - SDRip");
|
||||
AddCategoryMapping(40, TorznabCatType.Movies, "FiLMS SD - VO");
|
||||
AddCategoryMapping(39, TorznabCatType.Movies, "FiLMS SD - VOSTFR");
|
||||
|
||||
AddCategoryMapping(15, TorznabCatType.Console, "JEUX VIDEO");
|
||||
AddCategoryMapping(76, TorznabCatType.Console3DS, "JEUX VIDEO - 3DS");
|
||||
AddCategoryMapping(18, TorznabCatType.ConsoleNDS, "JEUX VIDEO - DS");
|
||||
AddCategoryMapping(55, TorznabCatType.PCMobileiOS, "JEUX VIDEO - IPHONE");
|
||||
AddCategoryMapping(80, TorznabCatType.PCGames, "JEUX VIDEO - LINUX");
|
||||
AddCategoryMapping(96, TorznabCatType.ConsoleOther, "JEUX VIDEO - NSW");
|
||||
AddCategoryMapping(79, TorznabCatType.PCMac, "JEUX VIDEO - OSX");
|
||||
AddCategoryMapping(22, TorznabCatType.PCGames, "JEUX VIDEO - PC");
|
||||
AddCategoryMapping(66, TorznabCatType.ConsolePS3, "JEUX VIDEO - PS2");
|
||||
AddCategoryMapping(58, TorznabCatType.ConsolePS3, "JEUX VIDEO - PS3");
|
||||
AddCategoryMapping(81, TorznabCatType.ConsolePS4, "JEUX VIDEO - PS4");
|
||||
AddCategoryMapping(20, TorznabCatType.ConsolePSP, "JEUX VIDEO - PSP");
|
||||
AddCategoryMapping(75, TorznabCatType.ConsolePS3, "JEUX VIDEO - PSX");
|
||||
AddCategoryMapping(19, TorznabCatType.ConsoleWii, "JEUX VIDEO - WII");
|
||||
AddCategoryMapping(83, TorznabCatType.ConsoleWiiU, "JEUX VIDEO - WiiU");
|
||||
AddCategoryMapping(16, TorznabCatType.ConsoleXBox, "JEUX VIDEO - XBOX");
|
||||
AddCategoryMapping(82, TorznabCatType.ConsoleXBoxOne, "JEUX VIDEO - XBOX ONE");
|
||||
AddCategoryMapping(17, TorznabCatType.ConsoleXBox360, "JEUX VIDEO - XBOX360");
|
||||
|
||||
AddCategoryMapping(23, TorznabCatType.Audio, "MUSIQUES");
|
||||
AddCategoryMapping(26, TorznabCatType.Audio, "MUSIQUES - CLIP/CONCERT");
|
||||
AddCategoryMapping(61, TorznabCatType.AudioLossless, "MUSIQUES - FLAC");
|
||||
AddCategoryMapping(60, TorznabCatType.AudioMP3, "MUSIQUES - MP3");
|
||||
|
||||
AddCategoryMapping(30, TorznabCatType.TV, "SERIES");
|
||||
AddCategoryMapping(77, TorznabCatType.TVSD, "SERIES - DVD");
|
||||
AddCategoryMapping(100, TorznabCatType.TVUHD, "SERIES - 4k");
|
||||
AddCategoryMapping(67, TorznabCatType.TVHD, "SERIES - FR HD");
|
||||
AddCategoryMapping(31, TorznabCatType.TVSD, "SERIES - FR SD");
|
||||
AddCategoryMapping(102, TorznabCatType.TVUHD, "SERIES - Pack 4k");
|
||||
AddCategoryMapping(92, TorznabCatType.TVHD, "SERIES - Pack FR HD");
|
||||
AddCategoryMapping(73, TorznabCatType.TVSD, "SERIES - Pack FR SD");
|
||||
AddCategoryMapping(94, TorznabCatType.TVHD, "SERIES - Pack VOSTFR HD");
|
||||
AddCategoryMapping(93, TorznabCatType.TVSD, "SERIES - Pack VOSTFR SD");
|
||||
AddCategoryMapping(68, TorznabCatType.TVHD, "SERIES - VO HD");
|
||||
AddCategoryMapping(32, TorznabCatType.TVSD, "SERIES - VO SD");
|
||||
AddCategoryMapping(101, TorznabCatType.TVUHD, "SERIES - 4k");
|
||||
AddCategoryMapping(69, TorznabCatType.TVHD, "SERIES - VOSTFR HD");
|
||||
AddCategoryMapping(33, TorznabCatType.TVSD, "SERIES - VOSTFR SD");
|
||||
|
||||
AddCategoryMapping(47, TorznabCatType.TV, "SPECTACLES/EMISSIONS");
|
||||
AddCategoryMapping(71, TorznabCatType.TV, "SPECTACLES/EMISSIONS - Emissions");
|
||||
AddCategoryMapping(103, TorznabCatType.TV, "SPECTACLES/EMISSIONS - Emissions Pack");
|
||||
AddCategoryMapping(72, TorznabCatType.TV, "SPECTACLES/EMISSIONS - Spectacles");
|
||||
|
||||
AddCategoryMapping(35, TorznabCatType.TVSport, "SPORT");
|
||||
AddCategoryMapping(36, TorznabCatType.TVSport, "SPORT - CATCH");
|
||||
AddCategoryMapping(65, TorznabCatType.TVSport, "SPORT - UFC");
|
||||
|
||||
AddCategoryMapping(37, TorznabCatType.XXX, "XXX");
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "username", configData.Username.Value },
|
||||
{ "password", configData.Password.Value }
|
||||
};
|
||||
|
||||
var result = await RequestWithCookiesAsync(LoginUrl, "", RequestType.POST, data: pairs);
|
||||
|
||||
await ConfigureIfOK(result.Cookies, result.Cookies != null, () =>
|
||||
{
|
||||
var errorMessage = result.ContentString;
|
||||
throw new ExceptionWithConfigData(errorMessage, configData);
|
||||
});
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
|
||||
var pairs = new Dictionary<string, string>
|
||||
{
|
||||
{"do", "search"},
|
||||
{"search_type", query.IsImdbQuery ? "t_genre" : "t_name"},
|
||||
{"keywords", query.IsImdbQuery ? query.ImdbID : query.GetQueryString()},
|
||||
{"category", "0"} // multi cat search not supported
|
||||
};
|
||||
|
||||
var results = await RequestWithCookiesAsync(BrowseUrl, method: RequestType.POST, data: pairs);
|
||||
if (results.IsRedirect)
|
||||
{
|
||||
// re-login
|
||||
await ApplyConfiguration(null);
|
||||
results = await RequestWithCookiesAsync(BrowseUrl, method: RequestType.POST, data: pairs);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var lastDate = DateTime.Now;
|
||||
|
||||
var parser = new HtmlParser();
|
||||
var doc = parser.ParseDocument(results.ContentString);
|
||||
var rows = doc.QuerySelectorAll("table[id='sortabletable'] > tbody > tr");
|
||||
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
if (row.Children.Length != 9)
|
||||
continue; // not a torrent line
|
||||
|
||||
var cat = row.Children[0].QuerySelector("a").GetAttribute("href").Split('=')[1];
|
||||
var title = row.Children[1].QuerySelector("a").TextContent;
|
||||
var qLinks = row.Children[2].QuerySelectorAll("a");
|
||||
var link = new Uri(configData.TorrentHTTPSMode.Value ? qLinks[1].GetAttribute("href") : qLinks[0].GetAttribute("href"));
|
||||
var details = new Uri(row.Children[1].QuerySelector("a").GetAttribute("href"));
|
||||
var size = row.Children[4].TextContent;
|
||||
var grabs = row.Children[5].QuerySelector("a").TextContent;
|
||||
var seeders = ParseUtil.CoerceInt(row.Children[6].QuerySelector("a").TextContent);
|
||||
var leechers = ParseUtil.CoerceInt(row.Children[7].QuerySelector("a").TextContent);
|
||||
var qTags = row.Children[1].QuerySelector("div:has(span[style=\"float: right;\"])");
|
||||
var dlVolumeFactor = 1.0;
|
||||
if (qTags.QuerySelector("img[alt^=\"TORRENT GRATUIT\"]") != null)
|
||||
dlVolumeFactor = 0.0;
|
||||
else if (qTags.QuerySelector("img[alt^=\"TORRENT SILVER\"]") != null)
|
||||
dlVolumeFactor = 0.5;
|
||||
|
||||
var upVolumeFactor = qTags.QuerySelector("img[alt^=\"TORRENT X2\"]") != null ? 2.0 : 1.0;
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800,
|
||||
Category = MapTrackerCatToNewznab(cat),
|
||||
Title = title,
|
||||
Link = link,
|
||||
Details = details,
|
||||
Size = ReleaseInfo.GetBytes(size),
|
||||
Seeders = seeders,
|
||||
Grabs = ParseUtil.CoerceLong(grabs),
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
UploadVolumeFactor = upVolumeFactor,
|
||||
Peers = leechers + seeders,
|
||||
Guid = link
|
||||
};
|
||||
|
||||
var qTooltip = row.Children[1].QuerySelector("div.tooltip-content");
|
||||
if (qTooltip != null)
|
||||
{
|
||||
var qPoster = qTooltip.QuerySelector("img");
|
||||
if (qPoster != null)
|
||||
{
|
||||
release.Poster = new Uri(qPoster.GetAttribute("src"));
|
||||
qPoster.Remove();
|
||||
}
|
||||
|
||||
qTooltip.QuerySelector("div:contains(\"Total Hits\")").Remove();
|
||||
|
||||
var qLongTitle = qTooltip.QuerySelector("div");
|
||||
release.Title = qLongTitle.TextContent;
|
||||
qLongTitle.Remove();
|
||||
|
||||
var description = qTooltip.TextContent.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(description))
|
||||
release.Description = description;
|
||||
}
|
||||
|
||||
// issue #5064 replace multi keyword
|
||||
if (!string.IsNullOrEmpty(configData.ReplaceMulti.Value))
|
||||
{
|
||||
var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])");
|
||||
release.Title = regex.Replace(release.Title, "$1" + configData.ReplaceMulti.Value + "$2");
|
||||
}
|
||||
|
||||
// issue #6855 Replace VOSTFR with ENGLISH
|
||||
if (configData.Vostfr.Value)
|
||||
release.Title = release.Title.Replace("VOSTFR", "ENGLISH").Replace("SUBFRENCH", "ENGLISH");
|
||||
|
||||
var qPretime = qTags.QuerySelector("font.mkprettytime");
|
||||
if (qPretime != null)
|
||||
{
|
||||
if (release.Description == null)
|
||||
release.Description = qPretime.TextContent;
|
||||
else
|
||||
release.Description += "<br>\n" + qPretime.TextContent;
|
||||
release.PublishDate = lastDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
release.PublishDate = DateTime.ParseExact(qTags.TextContent.Trim(), "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
|
||||
lastDate = release.PublishDate;
|
||||
}
|
||||
|
||||
releases.Add(release);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError(results.ContentString, ex);
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
}
|
@@ -45,7 +45,7 @@ namespace Jackett.Common.Indexers
|
||||
|
||||
public override string[] AlternativeSiteLinks { get; protected set; } = {
|
||||
"https://www.epublibre.org/",
|
||||
"https://epublibre.unblockit.club/"
|
||||
"https://epublibre.unblockit.onl/"
|
||||
};
|
||||
|
||||
public override string[] LegacySiteLinks { get; protected set; } = {
|
||||
@@ -55,6 +55,7 @@ namespace Jackett.Common.Indexers
|
||||
"https://epublibre.unblockit.ltd/",
|
||||
"https://epublibre.unblockit.link/",
|
||||
"https://epublibre.unblockit.buzz/",
|
||||
"https://epublibre.unblockit.club/",
|
||||
"https://epublibre.org/"
|
||||
};
|
||||
|
||||
|
382
src/Jackett.Common/Indexers/EraiRaws.cs
Normal file
382
src/Jackett.Common/Indexers/EraiRaws.cs
Normal file
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using static Jackett.Common.Models.IndexerConfig.ConfigurationData;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
{
|
||||
public class EraiRaws : BaseWebIndexer
|
||||
{
|
||||
const string RSS_PATH = "rss-all-magnet";
|
||||
|
||||
private readonly IReadOnlyDictionary<string, int> sizeEstimates = new Dictionary<string, int>() {
|
||||
{ "1080p", 1332 }, // ~1.3GiB
|
||||
{ "720p", 700 },
|
||||
{ "540p", 350 }
|
||||
};
|
||||
|
||||
public EraiRaws(IIndexerConfigurationService configService, Utils.Clients.WebClient wc, Logger l,
|
||||
IProtectionService ps, ICacheService cs)
|
||||
: base(id: "erai-raws",
|
||||
name: "Erai-Raws",
|
||||
description: "Erai-Raws is a team release site for Anime subtitles.",
|
||||
link: "https://www.erai-raws.info/",
|
||||
caps: new TorznabCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q
|
||||
}
|
||||
},
|
||||
configService: configService,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
cacheService: cs,
|
||||
configData: new ConfigurationData())
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "en-us";
|
||||
Type = "public";
|
||||
|
||||
// Add note that download stats are not available
|
||||
configData.AddDynamic(
|
||||
"download-stats-unavailable",
|
||||
new DisplayInfoConfigurationItem("", "<p>Please note that the following stats are not available for this indexer. Default values are used instead. </p><ul><li>Size</li><li>Seeders</li><li>Leechers</li><li>Download Factor</li><li>Upload Factor</li></ul>")
|
||||
);
|
||||
|
||||
// Config item for title detail parsing
|
||||
configData.AddDynamic("title-detail-parsing", new BoolConfigurationItem("Enable Title Detail Parsing"));
|
||||
configData.AddDynamic(
|
||||
"title-detail-parsing-help",
|
||||
new DisplayInfoConfigurationItem("", "Title Detail Parsing will attempt to determine the season and episode number from the release names and reformat them as a suffix in the format S1E1. If successful, this should provide better matching in applications such as Sonarr.")
|
||||
);
|
||||
|
||||
// Configure the category mappings
|
||||
AddCategoryMapping(1, TorznabCatType.TVAnime, "Anime - Sub");
|
||||
}
|
||||
|
||||
private TitleParser titleParser = new TitleParser();
|
||||
|
||||
private bool IsTitleDetailParsingEnabled => ((BoolConfigurationItem)configData.GetDynamic("title-detail-parsing")).Value;
|
||||
|
||||
public string RssFeedUri
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Concat(SiteLink, RSS_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
{
|
||||
LoadValuesFromJson(configJson);
|
||||
var releases = await PerformQuery(new TorznabQuery());
|
||||
|
||||
await ConfigureIfOK(string.Empty, releases.Any(), () =>
|
||||
throw new Exception("Could not find releases from this URL"));
|
||||
|
||||
return IndexerConfigurationStatus.Completed;
|
||||
}
|
||||
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var feedItems = await GetItemsFromFeed();
|
||||
var eraiRawsReleaseInfo = ConvertFeedItemsToEraiRawsReleaseInfo(feedItems);
|
||||
|
||||
// Perform basic filter within Jackett
|
||||
var filteredItems = FilterForQuery(query, eraiRawsReleaseInfo);
|
||||
|
||||
// Convert to release info
|
||||
return ConvertEraiRawsInfoToJackettInfo(filteredItems);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<RssFeedItem>> GetItemsFromFeed()
|
||||
{
|
||||
// Retrieve RSS feed
|
||||
var result = await RequestWithCookiesAndRetryAsync(RssFeedUri);
|
||||
|
||||
// Parse as XML document
|
||||
var xmlDocument = new XmlDocument();
|
||||
xmlDocument.LoadXml(result.ContentString);
|
||||
|
||||
// Parse to RssFeedItems
|
||||
var xmlNodes = xmlDocument.GetElementsByTagName("item");
|
||||
List<RssFeedItem> feedItems = new List<RssFeedItem>();
|
||||
foreach (var n in xmlNodes)
|
||||
{
|
||||
var node = (XmlNode)n;
|
||||
|
||||
if (RssFeedItem.TryParse(node, out RssFeedItem item))
|
||||
{
|
||||
feedItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Could not parse {DisplayName} RSS item '{node.InnerText}'");
|
||||
}
|
||||
}
|
||||
|
||||
return feedItems;
|
||||
}
|
||||
|
||||
private IEnumerable<EraiRawsReleaseInfo> ConvertFeedItemsToEraiRawsReleaseInfo(IEnumerable<RssFeedItem> feedItems)
|
||||
{
|
||||
foreach (var fi in feedItems)
|
||||
{
|
||||
EraiRawsReleaseInfo releaseInfo = new EraiRawsReleaseInfo(fi);
|
||||
|
||||
// Validate the release
|
||||
if (releaseInfo.PublishDate == null)
|
||||
{
|
||||
logger.Warn($"Failed to parse {DisplayName} RSS feed item '{fi.Title}' due to malformed publish date.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (releaseInfo.Link == null)
|
||||
{
|
||||
logger.Warn($"Failed to parse {DisplayName} RSS feed item '{fi.Title}' due to malformed link URI.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// If enabled, perform detailed title parsing
|
||||
if (IsTitleDetailParsingEnabled)
|
||||
{
|
||||
releaseInfo.Title = titleParser.Parse(releaseInfo.Title);
|
||||
}
|
||||
|
||||
yield return releaseInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<EraiRawsReleaseInfo> FilterForQuery(TorznabQuery query, IEnumerable<EraiRawsReleaseInfo> feedItems)
|
||||
{
|
||||
foreach (var fi in feedItems)
|
||||
{
|
||||
if (!query.MatchQueryStringAND(fi.Title))
|
||||
continue;
|
||||
|
||||
yield return fi;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<ReleaseInfo> ConvertEraiRawsInfoToJackettInfo(IEnumerable<EraiRawsReleaseInfo> feedItems)
|
||||
{
|
||||
foreach (var fi in feedItems)
|
||||
{
|
||||
yield return new ReleaseInfo
|
||||
{
|
||||
Title = string.Concat(fi.Title, " - ", fi.Quality),
|
||||
Guid = fi.Link,
|
||||
MagnetUri = fi.Link,
|
||||
PublishDate = fi.PublishDate.Value.ToLocalTime().DateTime,
|
||||
Category = MapTrackerCatToNewznab("1"),
|
||||
|
||||
// Download stats are not available through scraping so set some mock values.
|
||||
Size = GetSizeEstimate(fi),
|
||||
Seeders = 1,
|
||||
Peers = 2,
|
||||
DownloadVolumeFactor = 0,
|
||||
UploadVolumeFactor = 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an estimate of the file size based on the release info.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These estimates are currently only based on Quality. They will be very inaccurate for batch releases.
|
||||
/// </remarks>
|
||||
private long GetSizeEstimate(EraiRawsReleaseInfo releaseInfo)
|
||||
{
|
||||
long sizeEstimateInMiB = 256;
|
||||
if (sizeEstimates.ContainsKey(releaseInfo.Quality.ToLower()))
|
||||
{
|
||||
sizeEstimateInMiB = sizeEstimates[releaseInfo.Quality.ToLower()];
|
||||
}
|
||||
|
||||
// Convert to bytes and return
|
||||
return sizeEstimateInMiB * (1024 * 1024);
|
||||
}
|
||||
|
||||
private static string PrefixOrDefault(string prefix, string value, string def = "")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Concat(prefix, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw RSS feed item containing the data as received.
|
||||
/// </summary>
|
||||
private class RssFeedItem
|
||||
{
|
||||
public static bool TryParse(XmlNode rssItem, out RssFeedItem item)
|
||||
{
|
||||
var title = rssItem.SelectSingleNode("title")?.InnerText;
|
||||
var link = rssItem.SelectSingleNode("link")?.InnerText;
|
||||
var publishDate = rssItem.SelectSingleNode("pubDate")?.InnerText;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(title) ||
|
||||
string.IsNullOrWhiteSpace(link) ||
|
||||
string.IsNullOrWhiteSpace(publishDate))
|
||||
{
|
||||
// One of the properties was empty so fail to parse
|
||||
item = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
item = new RssFeedItem(title, link, publishDate);
|
||||
return true;
|
||||
}
|
||||
|
||||
private RssFeedItem(string title, string link, string publishDate)
|
||||
{
|
||||
Title = title;
|
||||
Link = link;
|
||||
PublishDate = publishDate;
|
||||
}
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string Link { get; }
|
||||
|
||||
public string PublishDate { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Details of an EraiRaws release
|
||||
/// </summary>
|
||||
private class EraiRawsReleaseInfo
|
||||
{
|
||||
public EraiRawsReleaseInfo(RssFeedItem feedItem)
|
||||
{
|
||||
var splitTitle = SplitQualityAndTitle(feedItem.Title);
|
||||
|
||||
Quality = splitTitle.quality;
|
||||
Title = splitTitle.title;
|
||||
|
||||
if (Uri.TryCreate(feedItem.Link, UriKind.Absolute, out Uri magnetUri))
|
||||
{
|
||||
Link = magnetUri;
|
||||
}
|
||||
|
||||
if (DateTimeOffset.TryParse(feedItem.PublishDate, out DateTimeOffset publishDate))
|
||||
{
|
||||
PublishDate = publishDate;
|
||||
}
|
||||
}
|
||||
|
||||
private (string quality, string title) SplitQualityAndTitle(string rawTitle)
|
||||
{
|
||||
var match = Regex.Match(rawTitle, @"^\[(?<quality>[0-9]+[ip])\] (?<title>.*)$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(0.5));
|
||||
if (match.Success)
|
||||
{
|
||||
return (match.Groups["quality"].Value, match.Groups["title"].Value);
|
||||
}
|
||||
|
||||
return (string.Empty, rawTitle);
|
||||
}
|
||||
|
||||
public string Quality { get; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public Uri Link { get; }
|
||||
|
||||
public DateTimeOffset? PublishDate { get; }
|
||||
}
|
||||
|
||||
public class TitleParser
|
||||
{
|
||||
private readonly Dictionary<string, string> DETAIL_SEARCH_SEASON = new Dictionary<string, string> {
|
||||
{ " Season (?<detail>[0-9]+)", "" }, // "Season 2"
|
||||
{ " (?<detail>[0-9]+)(st|nd|rd|th) Season", "" }, // "2nd Season"
|
||||
{ " Part (?<detail>[0-9]+) – ", " – " }, // "<title> Part 2 – <episode>"
|
||||
{ " (?<detail>[0-9]+) – ", " – " } // "<title> 2 – <episode>" - NOT A HYPHEN!
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, string> DETAIL_SEARCH_EPISODE = new Dictionary<string, string> {
|
||||
{ " – (?<detail>[0-9]+)$", " – " }, // "<title> – <episode>" <end_of_title> - NOT A HYPHEN!
|
||||
{ " – (?<detail>[0-9]+) ", " – " } // "<title> – <episode> ..." - NOT A HYPHEN!
|
||||
};
|
||||
|
||||
public string Parse(string title)
|
||||
{
|
||||
var results = SearchTitleForDetails(title, new Dictionary<string, Dictionary<string, string>> {
|
||||
{ "episode", DETAIL_SEARCH_EPISODE },
|
||||
{ "season", DETAIL_SEARCH_SEASON }
|
||||
});
|
||||
|
||||
var seasonEpisodeIdentifier = string.Concat(
|
||||
PrefixOrDefault("S", results.details["season"]).Trim(),
|
||||
PrefixOrDefault("E", results.details["episode"]).Trim()
|
||||
);
|
||||
|
||||
// If title still contains the strange hyphen, insert the identifier after it. Otherwise put it at the end.
|
||||
int strangeHyphenPosition = results.strippedTitle.LastIndexOf("–");
|
||||
if (strangeHyphenPosition > -1)
|
||||
{
|
||||
return string.Concat(
|
||||
results.strippedTitle.Substring(0, strangeHyphenPosition).Trim(),
|
||||
" – ",
|
||||
seasonEpisodeIdentifier,
|
||||
" ",
|
||||
results.strippedTitle.Substring(strangeHyphenPosition + 1).Trim()
|
||||
).Trim();
|
||||
}
|
||||
|
||||
return string.Concat(
|
||||
results.strippedTitle.Trim(),
|
||||
" ",
|
||||
seasonEpisodeIdentifier
|
||||
).Trim();
|
||||
}
|
||||
|
||||
private static (string strippedTitle, Dictionary<string, string> details) SearchTitleForDetails(string title, Dictionary<string, Dictionary<string,string>> definition)
|
||||
{
|
||||
Dictionary<string, string> details = new Dictionary<string, string>();
|
||||
foreach (var search in definition)
|
||||
{
|
||||
var searchResult = SearchTitleForDetail(title, search.Value);
|
||||
details.Add(search.Key, searchResult.detail);
|
||||
title = searchResult.strippedTitle;
|
||||
}
|
||||
|
||||
return (title, details);
|
||||
}
|
||||
|
||||
private static (string strippedTitle, string detail) SearchTitleForDetail(string title, Dictionary<string, string> searchReplacePatterns)
|
||||
{
|
||||
foreach (var srp in searchReplacePatterns)
|
||||
{
|
||||
var match = Regex.Match(title, srp.Key, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(0.5));
|
||||
if (match.Success)
|
||||
{
|
||||
string detail = match.Groups["detail"].Value;
|
||||
var strippedTitle = Regex.Replace(title, srp.Key, srp.Value, RegexOptions.IgnoreCase);
|
||||
return (strippedTitle, detail);
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found so return null
|
||||
return (title, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -307,7 +307,7 @@ namespace Jackett.Common.Indexers
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var anchor = row.QuerySelector("a");
|
||||
if (anchor == null)
|
||||
if (anchor == null || anchor.GetAttribute("href") == "#abajo")
|
||||
continue;
|
||||
|
||||
var episodeTitle = anchor.TextContent.Trim();
|
||||
|
@@ -51,7 +51,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
|
@@ -3,9 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -18,7 +16,6 @@ using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
||||
@@ -32,12 +29,6 @@ namespace Jackett.Common.Indexers
|
||||
private string SearchUrl => SiteLink + "browse.php";
|
||||
private string TorrentDetailsUrl => SiteLink + "details.php?id={id}";
|
||||
private string TorrentDownloadUrl => SiteLink + "download.php?id={id}&passkey={passkey}";
|
||||
private bool Latency => ConfigData.Latency.Value;
|
||||
private bool DevMode => ConfigData.DevMode.Value;
|
||||
private bool CacheMode => ConfigData.HardDriveCache.Value;
|
||||
private static string Directory => Path.Combine(Path.GetTempPath(), "Jackett", MethodBase.GetCurrentMethod().DeclaringType?.Name);
|
||||
|
||||
private readonly Dictionary<string, string> _emulatedBrowserHeaders = new Dictionary<string, string>();
|
||||
|
||||
private ConfigurationDataNorbits ConfigData => (ConfigurationDataNorbits)configData;
|
||||
|
||||
@@ -113,26 +104,7 @@ namespace Jackett.Common.Indexers
|
||||
// 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);
|
||||
_emulatedBrowserHeaders.Add("Referer", LoginUrl);
|
||||
}
|
||||
|
||||
await DoLogin();
|
||||
await DoLoginAsync();
|
||||
|
||||
return IndexerConfigurationStatus.RequiresTesting;
|
||||
}
|
||||
@@ -141,19 +113,18 @@ namespace Jackett.Common.Indexers
|
||||
/// Perform login to racker
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task DoLogin()
|
||||
private async Task DoLoginAsync()
|
||||
{
|
||||
// Build WebRequest for index
|
||||
var myIndexRequest = new WebRequest
|
||||
{
|
||||
Type = RequestType.GET,
|
||||
Url = SiteLink,
|
||||
Headers = _emulatedBrowserHeaders,
|
||||
Encoding = Encoding
|
||||
};
|
||||
|
||||
// Get index page for cookies
|
||||
Output("\nGetting index page (for cookies).. with " + SiteLink);
|
||||
logger.Info("\nNorBits - Getting index page (for cookies).. with " + SiteLink);
|
||||
var indexPage = await webclient.GetResultAsync(myIndexRequest);
|
||||
|
||||
// Building login form data
|
||||
@@ -167,15 +138,13 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
Type = RequestType.GET,
|
||||
Url = LoginUrl,
|
||||
Headers = _emulatedBrowserHeaders,
|
||||
Cookies = indexPage.Cookies,
|
||||
Referer = SiteLink,
|
||||
Encoding = Encoding
|
||||
};
|
||||
|
||||
// Get login page -- (not used, but simulation needed by tracker security's checks)
|
||||
LatencyNow();
|
||||
Output("\nGetting login page (user simulation).. with " + LoginUrl);
|
||||
logger.Info("\nNorBits - Getting login page (user simulation).. with " + LoginUrl);
|
||||
await webclient.GetResultAsync(myRequestLogin);
|
||||
|
||||
// Build WebRequest for submitting authentification
|
||||
@@ -185,14 +154,12 @@ namespace Jackett.Common.Indexers
|
||||
Referer = LoginUrl,
|
||||
Type = RequestType.POST,
|
||||
Url = LoginCheckUrl,
|
||||
Headers = _emulatedBrowserHeaders,
|
||||
Cookies = indexPage.Cookies,
|
||||
Encoding = Encoding
|
||||
};
|
||||
|
||||
// Perform loggin
|
||||
LatencyNow();
|
||||
Output("\nPerform loggin.. with " + LoginCheckUrl);
|
||||
logger.Info("\nPerform loggin.. with " + LoginCheckUrl);
|
||||
var response = await webclient.GetResultAsync(request);
|
||||
|
||||
// Test if we are logged in
|
||||
@@ -204,36 +171,36 @@ namespace Jackett.Common.Indexers
|
||||
var redirectTo = response.RedirectingTo;
|
||||
|
||||
// Oops, unable to login
|
||||
Output("-> Login failed: " + message, "error");
|
||||
logger.Info("NorBits - Login failed: " + message, "error");
|
||||
throw new ExceptionWithConfigData("Login failed: " + message, configData);
|
||||
});
|
||||
|
||||
Output("\nCookies saved for future uses...");
|
||||
logger.Info("\nNorBits - Cookies saved for future uses...");
|
||||
ConfigData.CookieHeader.Value = indexPage.Cookies + " " + response.Cookies + " ts_username=" + ConfigData.Username.Value;
|
||||
|
||||
Output("\n-> Login Success\n");
|
||||
logger.Info("\nNorBits - Login Success\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check logged-in state for provider
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task CheckLogin()
|
||||
private async Task CheckLoginAsync()
|
||||
{
|
||||
// Checking ...
|
||||
Output("\n-> Checking logged-in state....");
|
||||
logger.Info("\nNorBits - Checking logged-in state....");
|
||||
var loggedInCheck = await RequestWithCookiesAsync(SearchUrl);
|
||||
if (!loggedInCheck.ContentString.Contains("logout.php"))
|
||||
{
|
||||
// Cookie expired, renew session on provider
|
||||
Output("-> Not logged, login now...\n");
|
||||
logger.Info("NorBits - Not logged, login now...\n");
|
||||
|
||||
await DoLogin();
|
||||
await DoLoginAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Already logged, session active
|
||||
Output("-> Already logged, continue...\n");
|
||||
logger.Info("NorBits - Already logged, continue...\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,22 +216,7 @@ namespace Jackett.Common.Indexers
|
||||
var searchUrl = SearchUrl;
|
||||
|
||||
// Check login before performing a query
|
||||
await CheckLogin();
|
||||
|
||||
// Check cache first so we don't query the server (if search term used or not in dev mode)
|
||||
if (!DevMode && !string.IsNullOrEmpty(exactSearchTerm))
|
||||
{
|
||||
lock (cache)
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
|
||||
// Search in cache
|
||||
var cachedResult = cache.FirstOrDefault(i => i.Query == exactSearchTerm);
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
}
|
||||
await CheckLoginAsync();
|
||||
|
||||
var SearchTerms = new List<string> { exactSearchTerm };
|
||||
|
||||
@@ -300,76 +252,41 @@ namespace Jackett.Common.Indexers
|
||||
else
|
||||
{
|
||||
// No result found for this query
|
||||
Output("\nNo result found for your query, please try another search term ...\n", "info");
|
||||
logger.Info("\nNorBits - No result found for your query, please try another search term ...\n", "info");
|
||||
break;
|
||||
}
|
||||
|
||||
Output("\nFound " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
|
||||
Output("\nThere are " + firstPageRows.Length + " results on the first page !");
|
||||
logger.Info("\nNorBits - Found " + nbResults + " result(s) (+/- " + firstPageRows.Length + ") in " + pageLinkCount + " page(s) for this query !");
|
||||
logger.Info("\nNorBits - There are " + firstPageRows.Length + " results on the first page !");
|
||||
|
||||
// Loop on results
|
||||
|
||||
foreach (var row in firstPageRows)
|
||||
{
|
||||
Output("Torrent #" + (releases.Count + 1));
|
||||
|
||||
// ID
|
||||
var id = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("href").Split('=').Last();
|
||||
Output("ID: " + id);
|
||||
|
||||
// Release Name
|
||||
var name = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("title");
|
||||
|
||||
// Category
|
||||
var categoryName = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("title");
|
||||
var id = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("href").Split('=').Last(); // ID
|
||||
var name = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(1)").GetAttribute("title"); // Release Name
|
||||
var categoryName = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("title"); // Category
|
||||
var mainCat = row.QuerySelector("td:nth-of-type(1) > div > a:nth-of-type(1)").GetAttribute("href").Split('?').Last();
|
||||
var qSubCat2 = row.QuerySelector("td:nth-of-type(1) > div > a[href^=\"/browse.php?sub2_cat[]=\"]");
|
||||
|
||||
var cat = mainCat;
|
||||
if (qSubCat2 != null)
|
||||
cat += '&' + qSubCat2.GetAttribute("href").Split('?').Last();
|
||||
|
||||
Output("Category: " + cat + " - " + categoryName);
|
||||
|
||||
// Seeders
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent);
|
||||
Output("Seeders: " + seeders);
|
||||
|
||||
// Leechers
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent);
|
||||
Output("Leechers: " + leechers);
|
||||
|
||||
// Completed
|
||||
var regexObj = new Regex(@"[^\d]");
|
||||
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent); // Seeders
|
||||
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent); // Leechers
|
||||
var regexObj = new Regex(@"[^\d]"); // Completed
|
||||
var completed2 = row.QuerySelector("td:nth-of-type(8)").TextContent;
|
||||
var completed = ParseUtil.CoerceLong(regexObj.Replace(completed2, ""));
|
||||
Output("Completed: " + completed);
|
||||
|
||||
// Files
|
||||
var qFiles = row.QuerySelector("td:nth-of-type(3) > a");
|
||||
var qFiles = row.QuerySelector("td:nth-of-type(3) > a"); // Files
|
||||
var files = qFiles != null ? ParseUtil.CoerceInt(Regex.Match(qFiles.TextContent, @"\d+").Value) : 1;
|
||||
Output("Files: " + files);
|
||||
|
||||
// Size
|
||||
var humanSize = row.QuerySelector("td:nth-of-type(7)").TextContent.ToLowerInvariant();
|
||||
var size = ReleaseInfo.GetBytes(humanSize);
|
||||
Output("Size: " + humanSize + " (" + size + " bytes)");
|
||||
|
||||
// --> Date
|
||||
var humanSize = row.QuerySelector("td:nth-of-type(7)").TextContent.ToLowerInvariant(); // Size
|
||||
var size = ReleaseInfo.GetBytes(humanSize); // Date
|
||||
var dateTimeOrig = row.QuerySelector("td:nth-of-type(5)").TextContent;
|
||||
var dateTime = Regex.Replace(dateTimeOrig, @"<[^>]+>| ", "").Trim();
|
||||
var date = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
|
||||
Output("Released on: " + date);
|
||||
|
||||
// Torrent Details URL
|
||||
var details = new Uri(TorrentDetailsUrl.Replace("{id}", id.ToString()));
|
||||
Output("Details: " + details.AbsoluteUri);
|
||||
|
||||
// Torrent Download URL
|
||||
var passkey = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(2)").GetAttribute("href");
|
||||
var details = new Uri(TorrentDetailsUrl.Replace("{id}", id.ToString())); // Description Link
|
||||
var passkey = row.QuerySelector("td:nth-of-type(2) > a:nth-of-type(2)").GetAttribute("href"); // Download Link
|
||||
var key = Regex.Match(passkey, "(?<=passkey\\=)([a-zA-z0-9]*)");
|
||||
var downloadLink = new Uri(TorrentDownloadUrl.Replace("{id}", id.ToString()).Replace("{passkey}", key.ToString()));
|
||||
Output("Download Link: " + downloadLink.AbsoluteUri);
|
||||
|
||||
// Building release infos
|
||||
var release = new ReleaseInfo
|
||||
@@ -462,7 +379,7 @@ namespace Jackett.Common.Indexers
|
||||
// Building our query
|
||||
url += "?" + searchterm + "&" + parameters.GetQueryString() + "&" + CatQryStr;
|
||||
|
||||
Output("\nBuilded query for \"" + term + "\"... " + url);
|
||||
logger.Info("\nBuilded query for \"" + term + "\"... " + url);
|
||||
|
||||
// Return our search url
|
||||
return url;
|
||||
@@ -473,58 +390,10 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebResult> QueryExec(string request)
|
||||
private async Task<WebResult> QueryExecAsync(string request)
|
||||
{
|
||||
WebResult results;
|
||||
|
||||
// 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<WebResult> QueryCache(string request)
|
||||
{
|
||||
WebResult results;
|
||||
|
||||
// Create Directory if not exist
|
||||
System.IO.Directory.CreateDirectory(Directory);
|
||||
|
||||
// Clean Storage Provider Directory from outdated cached queries
|
||||
CleanCacheStorage();
|
||||
|
||||
// Create fingerprint for request
|
||||
var file = Directory + request.GetHashCode() + ".json";
|
||||
|
||||
// Checking modes states
|
||||
if (File.Exists(file))
|
||||
{
|
||||
// File exist... loading it right now !
|
||||
Output("Loading results from hard drive cache ..." + request.GetHashCode() + ".json");
|
||||
results = JsonConvert.DeserializeObject<WebResult>(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");
|
||||
File.WriteAllText(file, JsonConvert.SerializeObject(results));
|
||||
}
|
||||
results = await QueryTrackerAsync(request);
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -533,91 +402,18 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
/// <param name="request">URL created by Query Builder</param>
|
||||
/// <returns>Results from query</returns>
|
||||
private async Task<WebResult> QueryTracker(string request)
|
||||
private async Task<WebResult> QueryTrackerAsync(string request)
|
||||
{
|
||||
// Cache mode not enabled or cached file didn't exist for our query
|
||||
Output("\nQuerying tracker for results....");
|
||||
logger.Info("\nNorBits - Querying tracker for results....");
|
||||
|
||||
// Request our first page
|
||||
LatencyNow();
|
||||
var results = await RequestWithCookiesAndRetryAsync(request, ConfigData.CookieHeader.Value, RequestType.GET, SearchUrl, null, _emulatedBrowserHeaders);
|
||||
var results = await RequestWithCookiesAndRetryAsync(request, ConfigData.CookieHeader.Value, RequestType.GET, SearchUrl, null);
|
||||
|
||||
// Return results from tracker
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Hard Drive Cache Storage
|
||||
/// </summary>
|
||||
/// <param name="force">Force Provider Folder deletion</param>
|
||||
private void CleanCacheStorage(bool 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
|
||||
{
|
||||
var 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 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)
|
||||
{
|
||||
var random = new Random(DateTime.Now.Millisecond);
|
||||
var 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);
|
||||
}
|
||||
// Generate a random value in our range
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find torrent rows in search pages
|
||||
/// </summary>
|
||||
@@ -634,7 +430,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
// Retrieving ID from link provided
|
||||
var id = ParseUtil.CoerceInt(Regex.Match(link.AbsoluteUri, @"\d+").Value);
|
||||
Output("Torrent Requested ID: " + id);
|
||||
logger.Info("NorBits - Torrent Requested ID: " + id);
|
||||
|
||||
// Building login form data
|
||||
var pairs = new Dictionary<string, string> {
|
||||
@@ -642,67 +438,19 @@ namespace Jackett.Common.Indexers
|
||||
{ "_", string.Empty } // ~~ Strange, blank param...
|
||||
};
|
||||
|
||||
// Add emulated XHR request
|
||||
_emulatedBrowserHeaders.Add("X-Prototype-Version", "1.6.0.3");
|
||||
_emulatedBrowserHeaders.Add("X-Requested-With", "XMLHttpRequest");
|
||||
|
||||
// Get torrent file now
|
||||
Output("Getting torrent file now....");
|
||||
var response = await base.Download(link);
|
||||
|
||||
// Remove our XHR request header
|
||||
_emulatedBrowserHeaders.Remove("X-Prototype-Version");
|
||||
_emulatedBrowserHeaders.Remove("X-Requested-With");
|
||||
|
||||
// Return content
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <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 (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");
|
||||
logger.Info("\nNorBits - Validating Settings ... \n");
|
||||
|
||||
// Check Username Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Username.Value))
|
||||
@@ -711,7 +459,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Validated Setting -- Username (auth) => " + ConfigData.Username.Value);
|
||||
logger.Info("NorBits - Validated Setting -- Username (auth) => " + ConfigData.Username.Value);
|
||||
}
|
||||
|
||||
// Check Password Setting
|
||||
@@ -721,7 +469,7 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Validated Setting -- Password (auth) => " + ConfigData.Password.Value);
|
||||
logger.Info("NorBits - Validated Setting -- Password (auth) => " + ConfigData.Password.Value);
|
||||
}
|
||||
|
||||
// Check Max Page Setting
|
||||
@@ -729,7 +477,7 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
try
|
||||
{
|
||||
Output("Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
logger.Info("NorBits - Validated Setting -- Max Pages => " + Convert.ToInt32(ConfigData.Pages.Value));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -740,121 +488,6 @@ namespace Jackett.Common.Indexers
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Browser simulation must be enabled (otherwhise, this provider will not work due to tracker's security)
|
||||
throw new ExceptionWithConfigData("Browser Simulation must be enabled for this provider to work, please enable it !", ConfigData);
|
||||
}
|
||||
|
||||
// Check Dev Cache Settings
|
||||
if (ConfigData.HardDriveCache.Value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,12 +19,16 @@ namespace Jackett.Common.Indexers
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal class ShizaProject : BaseWebIndexer
|
||||
{
|
||||
public override string[] LegacySiteLinks { get; protected set; } = {
|
||||
"http://shiza-project.com/" // site is forcing https
|
||||
};
|
||||
|
||||
public ShizaProject(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
|
||||
ICacheService cs)
|
||||
: base(id: "ShizaProject",
|
||||
name: "ShizaProject",
|
||||
description: "ShizaProject Tracker is a semi-private russian tracker and release group for anime",
|
||||
link: "http://shiza-project.com/",
|
||||
link: "https://shiza-project.com/",
|
||||
caps: new TorznabCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
|
@@ -102,7 +102,7 @@ namespace Jackett.Common.Indexers
|
||||
PublishDate = publishDate,
|
||||
InfoHash = infoHash,
|
||||
MagnetUri = magnetUri,
|
||||
Size = 0,
|
||||
Size = 512,
|
||||
Seeders = 1,
|
||||
Peers = 2,
|
||||
DownloadVolumeFactor = 0,
|
||||
|
@@ -20,6 +20,11 @@ namespace Jackett.Common.Indexers
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class SubsPlease : BaseWebIndexer
|
||||
{
|
||||
public override string[] AlternativeSiteLinks { get; protected set; } = {
|
||||
"https://subsplease.org/",
|
||||
"https://subsplease.nocensor.space/"
|
||||
};
|
||||
|
||||
private string ApiEndpoint => SiteLink + "/api/?";
|
||||
|
||||
public SubsPlease(IIndexerConfigurationService configService, Utils.Clients.WebClient wc, Logger l, IProtectionService ps, ICacheService cs)
|
||||
|
@@ -92,7 +92,7 @@ namespace Jackett.Common.Indexers
|
||||
var qc = new NameValueCollection
|
||||
{
|
||||
{ "order_by", "s3" },
|
||||
{ "order_way", "desc" },
|
||||
{ "order_way", "DESC" },
|
||||
{ "disablegrouping", "1" }
|
||||
};
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Jackett.Common.Indexers
|
||||
qc.Add("action", "advanced");
|
||||
qc.Add("imdbid", query.ImdbID);
|
||||
}
|
||||
else
|
||||
else if (!string.IsNullOrWhiteSpace(query.GetQueryString()))
|
||||
qc.Add("searchstr", StripSearchString(query.GetQueryString()));
|
||||
|
||||
var searchUrl = BrowseUrl + "?" + qc.GetQueryString();
|
||||
|
@@ -2,21 +2,20 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Jackett.Common.Models;
|
||||
using Jackett.Common.Models.IndexerConfig.Bespoke;
|
||||
using Jackett.Common.Models.IndexerConfig;
|
||||
using Jackett.Common.Services.Interfaces;
|
||||
using Jackett.Common.Utils;
|
||||
using Jackett.Common.Utils.Clients;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using static Jackett.Common.Models.IndexerConfig.ConfigurationData;
|
||||
using WebRequest = Jackett.Common.Utils.Clients.WebRequest;
|
||||
|
||||
namespace Jackett.Common.Indexers
|
||||
@@ -25,25 +24,27 @@ namespace Jackett.Common.Indexers
|
||||
public class Xthor : BaseCachingWebIndexer
|
||||
{
|
||||
private static string ApiEndpoint => "https://api.xthor.tk/";
|
||||
private int MaxPagesHardLimit => 4;
|
||||
private string TorrentDetailsUrl => SiteLink + "details.php?id={id}";
|
||||
private string WebRequestDelay => ((SingleSelectConfigurationItem)configData.GetDynamic("webRequestDelay")).Value;
|
||||
private int MaxPages => Convert.ToInt32(((SingleSelectConfigurationItem)configData.GetDynamic("maxPages")).Value);
|
||||
private bool MaxPagesBypassForTMDB => ((BoolConfigurationItem)configData.GetDynamic("maxPagesBypassForTMDB")).Value;
|
||||
private string MultiReplacement => ((StringConfigurationItem)configData.GetDynamic("multiReplacement")).Value;
|
||||
private bool SubReplacement => ((BoolConfigurationItem)configData.GetDynamic("subReplacement")).Value;
|
||||
private bool EnhancedAnimeSearch => ((BoolConfigurationItem)configData.GetDynamic("enhancedAnimeSearch")).Value;
|
||||
private string SpecificLanguageAccent => ((SingleSelectConfigurationItem)configData.GetDynamic("specificLanguageAccent")).Value;
|
||||
private bool FreeleechOnly => ((BoolConfigurationItem)configData.GetDynamic("freeleechOnly")).Value;
|
||||
|
||||
public override string[] LegacySiteLinks { get; protected set; } = {
|
||||
"https://xthor.bz/",
|
||||
"https://xthor.to"
|
||||
};
|
||||
|
||||
private string TorrentDetailsUrl => SiteLink + "details.php?id={id}";
|
||||
private string ReplaceMulti => ConfigData.ReplaceMulti.Value;
|
||||
private bool EnhancedAnime => ConfigData.EnhancedAnime.Value;
|
||||
private bool DevMode => ConfigData.DevMode.Value;
|
||||
private bool CacheMode => ConfigData.HardDriveCache.Value;
|
||||
private static string Directory => Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name.ToLower(), MethodBase.GetCurrentMethod().DeclaringType?.Name.ToLower());
|
||||
public Dictionary<string, string> EmulatedBrowserHeaders { get; } = new Dictionary<string, string>();
|
||||
private ConfigurationDataXthor ConfigData => (ConfigurationDataXthor)configData;
|
||||
private ConfigurationDataPasskey ConfigData => (ConfigurationDataPasskey)configData;
|
||||
|
||||
public Xthor(IIndexerConfigurationService configService, Utils.Clients.WebClient w, Logger l,
|
||||
IProtectionService ps, ICacheService cs)
|
||||
: base(id: "xthor",
|
||||
name: "Xthor",
|
||||
: base(id: "xthor-api",
|
||||
name: "Xthor API",
|
||||
description: "General French Private Tracker",
|
||||
link: "https://xthor.tk/",
|
||||
caps: new TorznabCapabilities
|
||||
@@ -54,7 +55,7 @@ namespace Jackett.Common.Indexers
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q
|
||||
MovieSearchParam.Q, MovieSearchParam.TmdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
@@ -71,7 +72,8 @@ namespace Jackett.Common.Indexers
|
||||
p: ps,
|
||||
cacheService: cs,
|
||||
downloadBase: "https://xthor.tk/download.php?torrent=",
|
||||
configData: new ConfigurationDataXthor())
|
||||
configData: new ConfigurationDataPasskey()
|
||||
)
|
||||
{
|
||||
Encoding = Encoding.UTF8;
|
||||
Language = "fr-fr";
|
||||
@@ -145,6 +147,106 @@ namespace Jackett.Common.Indexers
|
||||
AddCategoryMapping(21, TorznabCatType.PC, "Logiciels Applis PC");
|
||||
AddCategoryMapping(22, TorznabCatType.PCMac, "Logiciels Applis Mac");
|
||||
AddCategoryMapping(23, TorznabCatType.PCMobileAndroid, "Logiciels Smartphone");
|
||||
|
||||
// Dynamic Configuration
|
||||
ConfigData.AddDynamic("optionsConfigurationWarning", new DisplayInfoConfigurationItem(string.Empty, "<center><b>Available Options</b></center>,<br /><br /> <ul><li><b>Freeleech Only</b>: (<i>Restrictive</i>) If you want to discover only freeleech torrents to not impact your ratio, check the related box. So only torrents marked as freeleech will be returned instead of all.</li><br /><li><b>Specific Language</b>: (<i>Restrictive</i>) You can scope your searches with a specific language / accent.</li></ul>"));
|
||||
|
||||
var ConfigFreeleechOnly = new BoolConfigurationItem("Do you want to discover only freeleech tagged torrents ?");
|
||||
ConfigData.AddDynamic("freeleechOnly", ConfigFreeleechOnly);
|
||||
|
||||
var ConfigSpecificLanguageAccent = new SingleSelectConfigurationItem("Do you want to scope your searches with a specific language ? (Accent)", new Dictionary<string, string>
|
||||
{
|
||||
{"0", "All Voices (default)"},
|
||||
{"1", "Françaises"},
|
||||
{"2", "Quebecoises"},
|
||||
{"47", "Françaises et Québécoises"},
|
||||
{"3", "Anglaises"},
|
||||
{"4", "Japonaises"},
|
||||
{"5", "Espagnoles"},
|
||||
{"6", "Allemandes"},
|
||||
{"7", "Chinoises"},
|
||||
{"8", "Italiennes"},
|
||||
{"9", "Coréennes"},
|
||||
{"10", "Danoises"},
|
||||
{"11", "Russes"},
|
||||
{"12", "Portugaises"},
|
||||
{"13", "Hindi"},
|
||||
{"14", "Hollandaises"},
|
||||
{"15", "Suédoises"},
|
||||
{"16", "Norvégiennes"},
|
||||
{"17", "Thaïlandaises"},
|
||||
{"18", "Hébreu"},
|
||||
{"19", "Persanes"},
|
||||
{"20", "Arabes"},
|
||||
{"21", "Turques"},
|
||||
{"22", "Hongroises"},
|
||||
{"23", "Polonaises"},
|
||||
{"24", "Finnoises"},
|
||||
{"25", "Indonésiennes"},
|
||||
{"26", "Roumaines"},
|
||||
{"27", "Malaisiennes"},
|
||||
{"28", "Estoniennes"},
|
||||
{"29", "Islandaises"},
|
||||
{"30", "Grecques"},
|
||||
{"31", "Serbes"},
|
||||
{"32", "Norvégiennes"},
|
||||
{"33", "Ukrainiennes"},
|
||||
{"34", "Bulgares"},
|
||||
{"35", "Tagalogues"},
|
||||
{"36", "Xhosa"},
|
||||
{"37", "Kurdes"},
|
||||
{"38", "Bengali"},
|
||||
{"39", "Amhariques"},
|
||||
{"40", "Bosniaques"},
|
||||
{"41", "Malayalam"},
|
||||
{"42", "Télougou"},
|
||||
{"43", "Bambara"},
|
||||
{"44", "Catalanes"},
|
||||
{"45", "Tchèques"},
|
||||
{"46", "Afrikaans"}
|
||||
})
|
||||
{ Value = "0" };
|
||||
ConfigData.AddDynamic("specificLanguageAccent", ConfigSpecificLanguageAccent);
|
||||
|
||||
ConfigData.AddDynamic("advancedConfigurationWarning", new DisplayInfoConfigurationItem(string.Empty, "<center><b>Advanced Configuration</b></center>,<br /><br /> <center><b><u>WARNING !</u></b> <i>Be sure to read instructions before editing options bellow, you can <b>drastically reduce performance</b> of queries or have <b>non-accurate results</b>.</i></center><br/><br/><ul><li><b>Delay betwwen Requests</b>: (<i>not recommended</i>) you can increase delay to requests made to the tracker, but a minimum of 2.1s is enforced as there is an anti-spam protection.</li><br /><li><b>Max Pages</b>: (<i>not recommended</i>) you can increase max pages to follow when making a request. But be aware that others apps can consider this indexer not working if jackett take too many times to return results. Another thing is that API is very buggy on tracker side, most of time, results of next pages are same ... as the first page. Even if we deduplicate rows, you will loose performance for the same results. You can check logs to see if an higher pages following is not benefical, you will see an error percentage (duplicates) with recommandations.</li><br /><li><b>Bypass for TMDB</b>: (<i>recommended</i>) this indexer is compatible with TMDB queries (<i>for movies only</i>), so when requesting content with an TMDB ID, we will search directly ID on API instead of name. Results will be more accurate, so you can enable a max pages bypass for this query type. You will be at least limited by the hard limit of 4 pages.</li><br /><li><b>Enhanced Anime</b>: if you have \"Anime\", this will improve queries made to this tracker related to this type when making searches.</li><br /><li><b>Multi Replacement</b>: you can dynamically replace the word \"MULTI\" with another of your choice like \"MULTI.FRENCH\" for better analysis of 3rd party softwares.</li><li><b>Sub Replacement</b>: you can dynamically replace the word \"VOSTFR\" or \"SUBFRENCH\" with the word \"ENGLISH\" for better analysis of 3rd party softwares.</li></ul>"));
|
||||
|
||||
var ConfigWebRequestDelay = new SingleSelectConfigurationItem("Which delay do you want to apply between each requests made to tracker ?", new Dictionary<string, string>
|
||||
{
|
||||
{"2.1", "2.1s (minimum)"},
|
||||
{"2.2", "2.2s"},
|
||||
{"2.3", "2.3s"},
|
||||
{"2.4", "2.4s" },
|
||||
{"2.5", "2.5s"},
|
||||
{"2.6", "2.6s"}
|
||||
})
|
||||
{ Value = "2.1" };
|
||||
ConfigData.AddDynamic("webRequestDelay", ConfigWebRequestDelay);
|
||||
|
||||
var ConfigMaxPages = new SingleSelectConfigurationItem("How many pages do you want to follow ?", new Dictionary<string, string>
|
||||
{
|
||||
{"1", "1 (32 results - default / best perf.)"},
|
||||
{"2", "2 (64 results)"},
|
||||
{"3", "3 (96 results)"},
|
||||
{"4", "4 (128 results - hard limit max)" },
|
||||
})
|
||||
{ Value = "1" };
|
||||
ConfigData.AddDynamic("maxPages", ConfigMaxPages);
|
||||
|
||||
var ConfigMaxPagesBypassForTMDB = new BoolConfigurationItem("Do you want to bypass max pages for TMDB searches ? (Radarr) - Hard limit of 4") { Value = true };
|
||||
ConfigData.AddDynamic("maxPagesBypassForTMDB", ConfigMaxPagesBypassForTMDB);
|
||||
|
||||
var ConfigEnhancedAnimeSearch = new BoolConfigurationItem("Do you want to use enhanced ANIME search ?") { Value = false };
|
||||
ConfigData.AddDynamic("enhancedAnimeSearch", ConfigEnhancedAnimeSearch);
|
||||
|
||||
var ConfigMultiReplacement = new StringConfigurationItem("Do you want to replace \"MULTI\" keyword in release title by another word ?") { Value = "MULTI.FRENCH" };
|
||||
ConfigData.AddDynamic("multiReplacement", ConfigMultiReplacement);
|
||||
|
||||
var ConfigSubReplacement = new BoolConfigurationItem("Do you want to replace \"VOSTFR\" and \"SUBFRENCH\" with \"ENGLISH\" word ?") { Value = false };
|
||||
ConfigData.AddDynamic("subReplacement", ConfigSubReplacement);
|
||||
|
||||
// Api has 1req/2s limit (minimum)
|
||||
webclient.requestDelay = Convert.ToDouble(WebRequestDelay);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -156,10 +258,9 @@ namespace Jackett.Common.Indexers
|
||||
// Warning 1998 is async method with no await calls inside
|
||||
// TODO: Remove pragma by wrapping return in Task.FromResult and removing async
|
||||
|
||||
#pragma warning disable 1998
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
|
||||
#pragma warning restore 1998
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
{
|
||||
// Provider not yet configured
|
||||
IsConfigured = false;
|
||||
@@ -167,19 +268,18 @@ namespace Jackett.Common.Indexers
|
||||
// Retrieve config values set by Jackett's user
|
||||
LoadValuesFromJson(configJson);
|
||||
|
||||
// Check & Validate Config
|
||||
ValidateConfig();
|
||||
logger.Debug("\nXthor - Validating Settings ... \n");
|
||||
|
||||
// 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");
|
||||
// Check Passkey Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.Passkey.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide your passkey for this tracker to be allowed to use API !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Debug("Xthor - Validated Setting -- PassKey (auth) => " + ConfigData.Passkey.Value);
|
||||
}
|
||||
|
||||
// Clean headers
|
||||
EmulatedBrowserHeaders.Clear();
|
||||
|
||||
// Inject headers
|
||||
EmulatedBrowserHeaders.Add("Accept", "application/json-rpc, application/json");
|
||||
EmulatedBrowserHeaders.Add("Content-Type", "application/json-rpc");
|
||||
|
||||
// Tracker is now configured
|
||||
IsConfigured = true;
|
||||
@@ -198,106 +298,145 @@ namespace Jackett.Common.Indexers
|
||||
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
|
||||
{
|
||||
var releases = new List<ReleaseInfo>();
|
||||
var searchTerm = query.GetEpisodeSearchString() + " " + query.SanitizedSearchTerm; // use episode search string first, see issue #1202
|
||||
var searchTerm = query.SanitizedSearchTerm + " " + query.GetEpisodeSearchString();
|
||||
searchTerm = searchTerm.Trim();
|
||||
searchTerm = searchTerm.ToLower();
|
||||
searchTerm = searchTerm.Replace(" ", ".");
|
||||
|
||||
if (EnhancedAnime && query.HasSpecifiedCategories && (query.Categories.Contains(TorznabCatType.TVAnime.ID) || query.Categories.Contains(100032) || query.Categories.Contains(100101) || query.Categories.Contains(100110)))
|
||||
if (EnhancedAnimeSearch && query.HasSpecifiedCategories && (query.Categories.Contains(TorznabCatType.TVAnime.ID) || query.Categories.Contains(100032) || query.Categories.Contains(100101) || query.Categories.Contains(100110)))
|
||||
{
|
||||
var regex = new Regex(" ([0-9]+)");
|
||||
searchTerm = regex.Replace(searchTerm, " E$1");
|
||||
}
|
||||
|
||||
// Check cache first so we don't query the server (if search term used or not in dev mode)
|
||||
if (!DevMode && !string.IsNullOrEmpty(searchTerm))
|
||||
// Multiple page support
|
||||
var nextPage = 1; var followingPages = true;
|
||||
do
|
||||
{
|
||||
lock (cache)
|
||||
|
||||
// Build our query
|
||||
var request = BuildQuery(searchTerm, query, ApiEndpoint, nextPage);
|
||||
|
||||
// Getting results
|
||||
logger.Info("\nXthor - Querying API page " + nextPage);
|
||||
var results = await QueryTrackerAsync(request);
|
||||
|
||||
// Torrents Result Count
|
||||
var torrentsCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Remove old cache items
|
||||
CleanCache();
|
||||
// Deserialize our Json Response
|
||||
var xthorResponse = JsonConvert.DeserializeObject<XthorResponse>(results);
|
||||
|
||||
// Search in cache
|
||||
var cachedResult = cache.FirstOrDefault(i => i.Query == searchTerm);
|
||||
if (cachedResult != null)
|
||||
return cachedResult.Results.Select(s => (ReleaseInfo)s.Clone()).ToArray();
|
||||
}
|
||||
}
|
||||
// Check Tracker's State
|
||||
CheckApiState(xthorResponse.Error);
|
||||
|
||||
// Build our query
|
||||
var request = BuildQuery(searchTerm, query, ApiEndpoint);
|
||||
|
||||
// Getting results & Store content
|
||||
var results = await QueryExec(request);
|
||||
|
||||
try
|
||||
{
|
||||
// Deserialize our Json Response
|
||||
var xthorResponse = JsonConvert.DeserializeObject<XthorResponse>(results);
|
||||
|
||||
// Check Tracker's State
|
||||
CheckApiState(xthorResponse.error);
|
||||
|
||||
// If contains torrents
|
||||
if (xthorResponse.torrents != null)
|
||||
{
|
||||
// Adding each torrent row to releases
|
||||
// Exclude hidden torrents (category 106, example => search 'yoda' in the API) #10407
|
||||
releases.AddRange(xthorResponse.torrents
|
||||
.Where(torrent => torrent.category != 106).Select(torrent =>
|
||||
// If contains torrents
|
||||
if (xthorResponse.Torrents != null)
|
||||
{
|
||||
//issue #3847 replace multi keyword
|
||||
if (!string.IsNullOrEmpty(ReplaceMulti))
|
||||
{
|
||||
var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])");
|
||||
torrent.name = regex.Replace(torrent.name, "$1" + ReplaceMulti + "$2");
|
||||
}
|
||||
// Store torrents rows count result
|
||||
torrentsCount = xthorResponse.Torrents.Count();
|
||||
logger.Info("\nXthor - Found " + torrentsCount + " torrents on current page.");
|
||||
|
||||
// issue #8759 replace vostfr and subfrench with English
|
||||
if (ConfigData.Vostfr.Value) torrent.name = torrent.name.Replace("VOSTFR","ENGLISH").Replace("SUBFRENCH","ENGLISH");
|
||||
// Adding each torrent row to releases
|
||||
// Exclude hidden torrents (category 106, example => search 'yoda' in the API) #10407
|
||||
releases.AddRange(xthorResponse.Torrents
|
||||
.Where(torrent => torrent.Category != 106).Select(torrent =>
|
||||
{
|
||||
//issue #3847 replace multi keyword
|
||||
if (!string.IsNullOrEmpty(MultiReplacement))
|
||||
{
|
||||
var regex = new Regex("(?i)([\\.\\- ])MULTI([\\.\\- ])");
|
||||
torrent.Name = regex.Replace(torrent.Name, "$1" + MultiReplacement + "$2");
|
||||
}
|
||||
|
||||
var publishDate = DateTimeUtil.UnixTimestampToDateTime(torrent.added);
|
||||
//TODO replace with download link?
|
||||
var guid = new Uri(TorrentDetailsUrl.Replace("{id}", torrent.id.ToString()));
|
||||
var details = new Uri(TorrentDetailsUrl.Replace("{id}", torrent.id.ToString()));
|
||||
var link = new Uri(torrent.download_link);
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
// Mapping data
|
||||
Category = MapTrackerCatToNewznab(torrent.category.ToString()),
|
||||
Title = torrent.name,
|
||||
Seeders = torrent.seeders,
|
||||
Peers = torrent.seeders + torrent.leechers,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 345600,
|
||||
PublishDate = publishDate,
|
||||
Size = torrent.size,
|
||||
Grabs = torrent.times_completed,
|
||||
Files = torrent.numfiles,
|
||||
UploadVolumeFactor = 1,
|
||||
DownloadVolumeFactor = (torrent.freeleech == 1 ? 0 : 1),
|
||||
Guid = guid,
|
||||
Details = details,
|
||||
Link = link,
|
||||
TMDb = torrent.tmdb_id
|
||||
};
|
||||
// issue #8759 replace vostfr and subfrench with English
|
||||
if (SubReplacement)
|
||||
torrent.Name = torrent.Name.Replace("VOSTFR", "ENGLISH").Replace("SUBFRENCH", "ENGLISH");
|
||||
|
||||
//TODO make consistent with other trackers
|
||||
if (DevMode)
|
||||
{
|
||||
Output(release.ToString());
|
||||
}
|
||||
var publishDate = DateTimeUtil.UnixTimestampToDateTime(torrent.Added);
|
||||
//TODO replace with download link?
|
||||
var guid = new Uri(TorrentDetailsUrl.Replace("{id}", torrent.Id.ToString()));
|
||||
var details = new Uri(TorrentDetailsUrl.Replace("{id}", torrent.Id.ToString()));
|
||||
var link = new Uri(torrent.Download_link);
|
||||
var release = new ReleaseInfo
|
||||
{
|
||||
// Mapping data
|
||||
Category = MapTrackerCatToNewznab(torrent.Category.ToString()),
|
||||
Title = torrent.Name,
|
||||
Seeders = torrent.Seeders,
|
||||
Peers = torrent.Seeders + torrent.Leechers,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 345600,
|
||||
PublishDate = publishDate,
|
||||
Size = torrent.Size,
|
||||
Grabs = torrent.Times_completed,
|
||||
Files = torrent.Numfiles,
|
||||
UploadVolumeFactor = 1,
|
||||
DownloadVolumeFactor = (torrent.Freeleech == 1 ? 0 : 1),
|
||||
Guid = guid,
|
||||
Details = details,
|
||||
Link = link,
|
||||
TMDb = torrent.Tmdb_id
|
||||
};
|
||||
|
||||
return release;
|
||||
}));
|
||||
return release;
|
||||
}));
|
||||
nextPage++;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("\nXthor - No results found on page " + nextPage + ", stopping follow of next page.");
|
||||
// No results or no more results available
|
||||
followingPages = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError("Unable to parse result \n" + ex.StackTrace, ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnParseError("Unable to parse result \n" + ex.StackTrace, ex);
|
||||
}
|
||||
|
||||
// Stop ?
|
||||
if(query.IsTmdbQuery && MaxPagesBypassForTMDB)
|
||||
{
|
||||
if(nextPage > MaxPagesHardLimit)
|
||||
{
|
||||
logger.Info("\nXthor - Stopping follow of next page " + nextPage + " due to page hard limit reached.");
|
||||
break;
|
||||
}
|
||||
logger.Info("\nXthor - Continue to next page " + nextPage + " due to TMDB request and activated max page bypass for this type of query. Max page hard limit: 4.");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(torrentsCount < 32)
|
||||
{
|
||||
logger.Info("\nXthor - Stopping follow of next page " + nextPage + " due max available results reached.");
|
||||
break;
|
||||
} else if(nextPage > MaxPages)
|
||||
{
|
||||
logger.Info("\nXthor - Stopping follow of next page " + nextPage + " due to page limit reached.");
|
||||
break;
|
||||
} else if (query.IsTest)
|
||||
{
|
||||
logger.Info("\nXthor - Stopping follow of next page " + nextPage + " due to index test query.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while (followingPages);
|
||||
|
||||
// Check if there is duplicate and return unique rows - Xthor API can be very buggy !
|
||||
var uniqReleases = releases.GroupBy(x => x.Guid).Select(x => x.First()).ToList();
|
||||
var errorPercentage = 1 - ((double) uniqReleases.Count() / releases.Count());
|
||||
if(errorPercentage >= 0.25)
|
||||
{
|
||||
logger.Warn("\nXthor - High percentage error detected: " + string.Format("{0:0.0%}", errorPercentage) + "\nWe strongly recommend that you lower max page to 1, as there is no benefit to grab additionnals.\nTracker API sent us duplicated pages with same results, even if we deduplicate returned rows, please consider to lower as it's unnecessary and increase time used for query for the same result.");
|
||||
}
|
||||
// Return found releases
|
||||
return releases;
|
||||
return uniqReleases;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -305,9 +444,9 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
public class XthorResponse
|
||||
{
|
||||
public XthorError error { get; set; }
|
||||
public XthorUser user { get; set; }
|
||||
public List<XthorTorrent> torrents { get; set; }
|
||||
public XthorError Error { get; set; }
|
||||
public XthorUser User { get; set; }
|
||||
public List<XthorTorrent> Torrents { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,8 +454,8 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
public class XthorError
|
||||
{
|
||||
public int code { get; set; }
|
||||
public string descr { get; set; }
|
||||
public int Code { get; set; }
|
||||
public string Descr { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -324,14 +463,14 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
public class XthorUser
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string username { get; set; }
|
||||
public long uploaded { get; set; }
|
||||
public long downloaded { get; set; }
|
||||
public int uclass { get; set; } // Class is a reserved keyword.
|
||||
public decimal bonus_point { get; set; }
|
||||
public int hits_and_run { get; set; }
|
||||
public string avatar_url { get; set; }
|
||||
public int Id { get; set; }
|
||||
public string Username { get; set; }
|
||||
public long Uploaded { get; set; }
|
||||
public long Downloaded { get; set; }
|
||||
public int Uclass { get; set; } // Class is a reserved keyword.
|
||||
public decimal Bonus_point { get; set; }
|
||||
public int Hits_and_run { get; set; }
|
||||
public string Avatar_url { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -339,21 +478,21 @@ namespace Jackett.Common.Indexers
|
||||
/// </summary>
|
||||
public class XthorTorrent
|
||||
{
|
||||
public int id { get; set; }
|
||||
public int category { get; set; }
|
||||
public int seeders { get; set; }
|
||||
public int leechers { get; set; }
|
||||
public string name { get; set; }
|
||||
public int times_completed { get; set; }
|
||||
public long size { get; set; }
|
||||
public int added { get; set; }
|
||||
public int freeleech { get; set; }
|
||||
public int numfiles { get; set; }
|
||||
public string release_group { get; set; }
|
||||
public string download_link { get; set; }
|
||||
public int tmdb_id { get; set; }
|
||||
public int Id { get; set; }
|
||||
public int Category { get; set; }
|
||||
public int Seeders { get; set; }
|
||||
public int Leechers { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Times_completed { get; set; }
|
||||
public long Size { get; set; }
|
||||
public int Added { get; set; }
|
||||
public int Freeleech { get; set; }
|
||||
public int Numfiles { get; set; }
|
||||
public string Release_group { get; set; }
|
||||
public string Download_link { get; set; }
|
||||
public int Tmdb_id { get; set; }
|
||||
|
||||
public override string ToString() => string.Format("[XthorTorrent: id={0}, category={1}, seeders={2}, leechers={3}, name={4}, times_completed={5}, size={6}, added={7}, freeleech={8}, numfiles={9}, release_group={10}, download_link={11}, tmdb_id={12}]", id, category, seeders, leechers, name, times_completed, size, added, freeleech, numfiles, release_group, download_link, tmdb_id);
|
||||
public override string ToString() => string.Format("[XthorTorrent: id={0}, category={1}, seeders={2}, leechers={3}, name={4}, times_completed={5}, size={6}, added={7}, freeleech={8}, numfiles={9}, release_group={10}, download_link={11}, tmdb_id={12}]", Id, Category, Seeders, Leechers, Name, Times_completed, Size, Added, Freeleech, Numfiles, Release_group, Download_link, Tmdb_id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -363,26 +502,33 @@ namespace Jackett.Common.Indexers
|
||||
/// <param name="query">Torznab Query for categories mapping</param>
|
||||
/// <param name="url">Search url for provider</param>
|
||||
/// <returns>URL to query for parsing and processing results</returns>
|
||||
private string BuildQuery(string term, TorznabQuery query, string url)
|
||||
private string BuildQuery(string term, TorznabQuery query, string url, int page = 1)
|
||||
{
|
||||
var parameters = new NameValueCollection();
|
||||
var categoriesList = MapTorznabCapsToTrackers(query);
|
||||
|
||||
// Passkey
|
||||
parameters.Add("passkey", ConfigData.PassKey.Value);
|
||||
parameters.Add("passkey", ConfigData.Passkey.Value);
|
||||
|
||||
// If search term provided
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
if (query.IsTmdbQuery)
|
||||
{
|
||||
// Add search term
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
parameters.Add("search", WebUtility.UrlEncode(term));
|
||||
logger.Info("\nXthor - Search requested for movie with TMDB ID n°" + query.TmdbID.ToString());
|
||||
parameters.Add("tmdbid", query.TmdbID.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Add("search", string.Empty);
|
||||
// Showing all torrents (just for output function)
|
||||
term = "all";
|
||||
if (!string.IsNullOrWhiteSpace(term))
|
||||
{
|
||||
// Add search term
|
||||
logger.Info("\nXthor - Search requested for movie with title \"" + term + "\"");
|
||||
parameters.Add("search", WebUtility.UrlEncode(term));
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("\nXthor - Global search requested without term");
|
||||
parameters.Add("search", string.Empty);
|
||||
// Showing all torrents
|
||||
}
|
||||
}
|
||||
|
||||
// Loop on Categories needed
|
||||
@@ -392,121 +538,48 @@ namespace Jackett.Common.Indexers
|
||||
}
|
||||
|
||||
// If Only Freeleech Enabled
|
||||
if (ConfigData.Freeleech.Value)
|
||||
if (FreeleechOnly)
|
||||
{
|
||||
parameters.Add("freeleech", "1");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ConfigData.Accent.Value))
|
||||
// If Specific Language Accent Requested
|
||||
if (!string.IsNullOrEmpty(SpecificLanguageAccent) && SpecificLanguageAccent != "0")
|
||||
{
|
||||
parameters.Add("accent", ConfigData.Accent.Value);
|
||||
parameters.Add("accent", SpecificLanguageAccent);
|
||||
}
|
||||
|
||||
// Pages handling
|
||||
if (page > 1 && !query.IsTest)
|
||||
{
|
||||
parameters.Add("page", page.ToString());
|
||||
}
|
||||
|
||||
// Building our query -- Cannot use GetQueryString due to UrlEncode (generating wrong category param)
|
||||
url += "?" + string.Join("&", parameters.AllKeys.Select(a => a + "=" + parameters[a]));
|
||||
|
||||
Output("\nBuilded query for \"" + term + "\"... " + url);
|
||||
logger.Info("\nXthor - Builded query: " + 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<string> QueryExec(string request)
|
||||
{
|
||||
string results;
|
||||
|
||||
// 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<string> QueryCache(string request)
|
||||
{
|
||||
string results;
|
||||
|
||||
// Create Directory if not exist
|
||||
System.IO.Directory.CreateDirectory(Directory);
|
||||
|
||||
// Clean Storage Provider Directory from outdated cached queries
|
||||
CleanCacheStorage();
|
||||
|
||||
// File Name
|
||||
var fileName = StringUtil.HashSHA1(request) + ".json";
|
||||
|
||||
// Create fingerprint for request
|
||||
var file = Path.Combine(Directory, fileName);
|
||||
|
||||
// Checking modes states
|
||||
if (File.Exists(file))
|
||||
{
|
||||
// File exist... loading it right now !
|
||||
Output("Loading results from hard drive cache ..." + fileName);
|
||||
try
|
||||
{
|
||||
using (var fileReader = File.OpenText(file))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
results = (string)serializer.Deserialize(fileReader, typeof(string));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Output("Error loading cached results ! " + e.Message, "error");
|
||||
results = null;
|
||||
}
|
||||
}
|
||||
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 ..." + fileName);
|
||||
using (var fileWriter = File.CreateText(file))
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
serializer.Serialize(fileWriter, 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<string> QueryTracker(string request)
|
||||
private async Task<string> QueryTrackerAsync(string request)
|
||||
{
|
||||
// Cache mode not enabled or cached file didn't exist for our query
|
||||
Output("\nQuerying tracker for results....");
|
||||
logger.Debug("\nQuerying tracker for results....");
|
||||
|
||||
// Build WebRequest for index
|
||||
var myIndexRequest = new WebRequest
|
||||
{
|
||||
Type = RequestType.GET,
|
||||
Url = request,
|
||||
Encoding = Encoding,
|
||||
Headers = EmulatedBrowserHeaders
|
||||
Encoding = Encoding
|
||||
};
|
||||
|
||||
// Request our first page
|
||||
@@ -525,188 +598,40 @@ namespace Jackett.Common.Indexers
|
||||
private void CheckApiState(XthorError state)
|
||||
{
|
||||
// Switch on state
|
||||
switch (state.code)
|
||||
switch (state.Code)
|
||||
{
|
||||
case 0:
|
||||
// Everything OK
|
||||
Output("\nAPI State : Everything OK ... -> " + state.descr);
|
||||
logger.Debug("\nXthor - API State : Everything OK ... -> " + state.Descr);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Passkey not found
|
||||
Output("\nAPI State : Error, Passkey not found in tracker's database, aborting... -> " + state.descr);
|
||||
logger.Error("\nXthor - API State : Error, Passkey not found in tracker's database, aborting... -> " + state.Descr);
|
||||
throw new Exception("Passkey not found in tracker's database");
|
||||
case 2:
|
||||
// No results
|
||||
Output("\nAPI State : No results for query ... -> " + state.descr);
|
||||
logger.Info("\nXthor - API State : No results for query ... -> " + state.Descr);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Power Saver
|
||||
Output("\nAPI State : Power Saver mode, only cached query with no parameters available ... -> " + state.descr);
|
||||
logger.Warn("\nXthor - API State : Power Saver mode, only cached query with no parameters available ... -> " + state.Descr);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// DDOS Attack, API disabled
|
||||
Output("\nAPI State : Tracker is under DDOS attack, API disabled, aborting ... -> " + state.descr);
|
||||
logger.Error("\nXthor - API State : Tracker is under DDOS attack, API disabled, aborting ... -> " + state.Descr);
|
||||
throw new Exception("Tracker is under DDOS attack, API disabled");
|
||||
case 8:
|
||||
// AntiSpam Protection
|
||||
logger.Warn("\nXthor - API State : Triggered AntiSpam Protection -> " + state.Descr);
|
||||
throw new Exception("Triggered AntiSpam Protection, please delay your requests !");
|
||||
default:
|
||||
// Unknown state
|
||||
Output("\nAPI State : Unknown state, aborting querying ... -> " + state.descr);
|
||||
logger.Error("\nXthor - API State : Unknown state, aborting querying ... -> " + state.Descr);
|
||||
throw new Exception("Unknown state, aborting querying");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean Hard Drive Cache Storage
|
||||
/// </summary>
|
||||
/// <param name="force">Force Provider Folder deletion</param>
|
||||
private void CleanCacheStorage(bool 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
|
||||
{
|
||||
var 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 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>
|
||||
/// 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 (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 Passkey Setting
|
||||
if (string.IsNullOrEmpty(ConfigData.PassKey.Value))
|
||||
{
|
||||
throw new ExceptionWithConfigData("You must provide your passkey for this tracker to be allowed to use API !", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Validated Setting -- PassKey (auth) => " + ConfigData.PassKey.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ConfigData.Accent.Value) && !string.Equals(ConfigData.Accent.Value, "1") && !string.Equals(ConfigData.Accent.Value, "2"))
|
||||
{
|
||||
throw new ExceptionWithConfigData("Only '1' or '2' are available in the Accent parameter.", ConfigData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Validated Setting -- Accent (audio) => " + ConfigData.Accent.Value);
|
||||
}
|
||||
// Check Dev Cache Settings
|
||||
if (ConfigData.HardDriveCache.Value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user