Compare commits

...

92 Commits

Author SHA1 Message Date
Garfield69
384b7bb3e4 tlfbits: engine tag 2023-02-04 16:38:17 +13:00
Garfield69
8ce9aeaae9 teamctgame: add posters
update engine tag
2023-02-04 12:25:14 +13:00
Garfield69
8c9e327ae6 spidertk: update posters
update engine tag
2023-02-04 12:19:09 +13:00
Garfield69
30854838c1 hdmayi: lint fix 3dc9b6c532 2023-02-04 11:49:56 +13:00
Garfield69
3dc9b6c532 add hdmayi a Chinese private site. resolves #13900 2023-02-04 11:42:12 +13:00
Garfield69
aa484e4618 pterclub: fix posters 2023-02-04 10:43:47 +13:00
Garfield69
cd0ca16d9b ptchina: add posters 2023-02-04 10:43:30 +13:00
Garfield69
871b857f48 ourbits: update engine tag 2023-02-04 10:30:21 +13:00
Garfield69
d74aa2317a oshenpt: add posters
update engine tag
2023-02-04 10:25:21 +13:00
Garfield69
44adbb1033 ihdbits: add posters
update engine tag
2023-02-04 10:02:11 +13:00
Garfield69
d2b0d7b21e oldtoonsworld: add posters
update engine tag
2023-02-04 10:01:51 +13:00
Garfield69
34eb822137 sharkpt: no MR atm. fix a0afe0aaa1 2023-02-04 10:01:32 +13:00
Garfield69
a0afe0aaa1 add sharkpt a Chinese private site. resolves #13904 2023-02-04 07:50:45 +13:00
Bogdan
14bcfcc018 rutracker/toloka: improve title cleaning (#13944) 2023-02-04 06:57:53 +13:00
Garfield69
470b18d664 hhanclub: add posters
update engine tag
2023-02-03 22:11:57 +13:00
Garfield69
3b7110880f hdtime: add posters 2023-02-03 22:07:11 +13:00
Garfield69
74fdceba96 hdhome: update engine tag 2023-02-03 22:06:49 +13:00
Garfield69
6fca20b776 hdchina: update engine tag 2023-02-03 21:59:27 +13:00
Garfield69
733b4c854b carpt: add posters
update engine tag
2023-02-03 21:43:47 +13:00
Garfield69
b3c7ce5818 2xfree: update poster selector 2023-02-03 21:35:05 +13:00
Garfield69
cd582ced27 1ptbar: add posters
update engine tag
2023-02-03 21:29:55 +13:00
Garfield69
8788ce1e93 3changtrai: update engine tag 2023-02-03 20:45:34 +13:00
Garfield69
eb3a9ab300 sugoimusic: update engine tag 2023-02-03 20:40:36 +13:00
Garfield69
690265d45e pthome: update engine tag 2023-02-03 19:58:35 +13:00
Garfield69
435b7e84e4 hdatmos: add 2fa
update engine tag
2023-02-03 19:58:23 +13:00
Garfield69
489e0c895a haitang: update engine tag 2023-02-03 19:58:02 +13:00
Garfield69
7e819a2b47 hdfans: add posters
update engine tag
2023-02-03 19:18:10 +13:00
Bogdan
5d7ee40a04 nebulanceapi: improve search query and release info data (#13940) 2023-02-03 17:39:01 +13:00
Garfield69
a2e5fc88cb speedtorrentreloaded: add new cat 2023-02-03 16:24:07 +13:00
Garfield69
3b7962210a torrent9clone: add new cat 2023-02-03 15:31:57 +13:00
Garfield69
771519385c torrent911: add new cat 2023-02-03 15:31:45 +13:00
Garfield69
8171a7986a torrentqq: bump domain 2023-02-03 15:31:32 +13:00
ilike2burnthing
38a3314f6f pignetwork: fix posters 2023-02-02 23:55:54 +00:00
ilike2burnthing
d756ff0ccf nicept: add private chinese 3x tracker. resolves #9075 (#13968) 2023-02-02 23:39:51 +00:00
Garfield69
5aae699e91 icc2022: fix posters 2023-02-03 12:15:54 +13:00
Garfield69
62aa75b7ad hdbits: removed. use hdbits (api) instead. 2023-02-03 08:38:49 +13:00
Bogdan
e8875d38fc xspeeds: category filtering if single, prevent possible error in login, add sorting (#13966) 2023-02-03 07:10:43 +13:00
Bogdan
22af3a09a2 bitspyder: fix category filtering and improve term search (#13965) 2023-02-03 06:44:12 +13:00
Bogdan
749167ba4a trancetraffic: add fl only, check if torrent is FL and avoid details redirect (#13963) 2023-02-02 18:56:44 +13:00
Bogdan
872c8674bf bitsexy: remove unused login inputs (#13962) 2023-02-02 18:56:04 +13:00
Bogdan
aac8469ffc immortalseed: don't return the whole page as error (#13961) 2023-02-02 18:08:38 +13:00
Garfield69
f8a9c57656 acervos: fix 0d2621b24d
the genre is 6.5.0 and we're still at 6.1.0
2023-02-02 16:58:33 +13:00
Garfield69
0d2621b24d acervos: update cats, add genre
add book and music search
update MST
2023-02-02 13:15:39 +13:00
Bogdan
231352dad5 speedcd: add freeleech only, exclude archives and fix TZ (#13959) 2023-02-02 12:39:54 +13:00
Bogdan
aef0802c21 libble: add 2FA support, improve searching and parsing (#13960) 2023-02-02 12:29:46 +13:00
Bogdan
ef9a4fbaa6 cloudtorrents: add fake user-agent to bypass blocking and fix TZ (#13958) 2023-02-01 11:15:27 +00:00
Bogdan
aad95a64ae pretome: refactor parsing and login error message (#13957) 2023-02-01 21:18:13 +13:00
Bogdan
2ca375c33f pornolab: refactor parsing (#13956) 2023-02-01 21:11:00 +13:00
Bogdan
0ba4d305b0 norbits: refactor parsing (#13955) 2023-02-01 21:06:23 +13:00
Bogdan
40fcb1e43b funfile: refactor parsing and multi-category filtering (#13954) 2023-02-01 21:00:26 +13:00
Garfield69
24d4198e59 partis: handle internal server error
when partis is offline  we currently get this obscure error:
Exception (partis): Cannot perform runtime binding on a null reference
now we get:
Exception (partis): Partis is offline, returning an Internal server error
2023-02-01 17:50:47 +13:00
Garfield69
d529b340ea torrentsir: bump domain 2023-02-01 17:25:09 +13:00
Garfield69
94b7f5434e add vtorrent a Polish semi-private site. resolves #13952 2023-02-01 15:18:25 +13:00
Garfield69
e45e8a9e4f mejortorrent: strip SxxExx from title for query. resolves #12559 2023-02-01 12:57:43 +13:00
Garfield69
d1f078b36b devil-torrents: fix size for kB 2023-02-01 09:23:18 +13:00
Garfield69
8cb43e3bdd add electro-torrent a Polish semi-private site. resolves #13951 2023-02-01 09:22:48 +13:00
Garfield69
c886faf7df add devil-torrents a semi-private site. resolves #13950 2023-01-31 22:01:37 +13:00
ilike2burnthing
885da5d30f rutracker: update cats 2023-01-31 03:53:37 +00:00
Garfield69
c61526182a lastfiles: adjustements for new layout
back to regular category selector
new title and details link
new poster link
2023-01-31 15:36:15 +13:00
Garfield69
a950ee0071 dontorrent: new domain 2023-01-31 15:20:16 +13:00
Garfield69
0d45b29a8e korsar: use new cat selector. resolves #13953 2023-01-31 12:33:33 +13:00
ilike2burnthing
c1fc63b8c6 korsar: add useragent. resolves #13949 2023-01-30 18:13:56 +00:00
ilike2burnthing
df46540efc filelist: add main link to alternativesitelinks #13946 2023-01-30 02:18:18 +00:00
Bogdan
40acf3c4a7 filelist: add alternative link and skip non-fl results when freeleech only is set (#13946) 2023-01-30 02:11:39 +00:00
Bogdan
329c17ab25 filelist: add freeleech setting (#13945) 2023-01-30 01:23:40 +00:00
ilike2burnthing
c479596a49 torrentgalaxy: add exclude ads setting, append title
https://github.com/Prowlarr/Prowlarr/issues/1369
2023-01-29 23:42:42 +00:00
Garfield69
993d5f2045 unleashthecartoons: add cat selection and poster 2023-01-30 07:15:46 +13:00
Garfield69
5b7deb8250 2xfree: fix posters. #13943 2023-01-29 22:43:10 +13:00
Garfield69
fba7b11697 add 2xfree a Chinese private site. resolves #13943 2023-01-29 22:14:00 +13:00
ilike2burnthing
44caa63a2e lastfiles: remove old sitelinks 2023-01-29 08:30:19 +00:00
Bogdan
cf6f67d7cc assorted: use GetArgumentFromQueryString and other minor fixes (#13941) 2023-01-29 20:17:48 +13:00
ilike2burnthing
137e112964 Revert "ci/cd: fix .net sdk to 6.0.403 #13857" (#13932) 2023-01-29 01:08:15 +00:00
ilike2burnthing
3ee74aa52a codeql: exclude IndexerConfig folder 2023-01-29 00:49:48 +00:00
Bogdan
7fb648d786 myanonamouse: add selectable search type, show max 5 authors (#13938) 2023-01-29 00:44:21 +00:00
Garfield69
19a556cd8f les-cinephiles: update some cats 2023-01-29 10:13:35 +13:00
Bogdan
33be5ec331 webui: add align center/right to table results, add title for publish date (#13939) 2023-01-28 15:14:55 +13:00
Garfield69
12bd05422a pornolab: ident forum offline msg 2023-01-28 15:04:12 +13:00
Garfield69
691a8c3757 add icc2022 a Chinese private site. resolves #13898 2023-01-27 18:46:45 +13:00
Garfield69
a63c8b012c torrentqq: bump domain 2023-01-27 08:13:55 +13:00
Garfield69
abe345a803 rutracker: ident other server error. #13935 2023-01-27 07:03:28 +13:00
Garfield69
ce583219a9 rutracker: ident other server error. #13935 2023-01-27 06:52:51 +13:00
Bogdan
ab76dde943 unleashthecartoons: add private site (#13933)
Co-authored-by: ilike2burnthing <59480337+ilike2burnthing@users.noreply.github.com>
2023-01-26 12:48:41 +00:00
Garfield69
c7a5d8c9a5 dontorrent: add requestdelay 2.1s to avoid too many requests limiter. resolves #6214 2023-01-26 16:14:02 +13:00
Garfield69
2b66e79a39 torrentwhiz: bump domain 2023-01-26 15:11:56 +13:00
Garfield69
6d5f8dac65 torrentview: bump domain 2023-01-26 15:11:45 +13:00
Garfield69
b0c9419345 TDC: update MR and MST 2023-01-26 13:49:21 +13:00
Garfield69
1dd08bd63c add thedarkcommunity, a private site. resolves #13923 resolves #13534 2023-01-26 13:31:16 +13:00
Garfield69
c3f62a1ac2 torrent-turk: drop windlard. resolves #13931
add andmatch
2023-01-26 07:14:15 +13:00
Garfield69
655ab08d57 lastfiles: add audio genre back. fix 2a593a195c 2023-01-26 07:04:48 +13:00
ilike2burnthing
2a593a195c lastfiles: update genre selector and filter 2023-01-25 14:33:01 +00:00
Garfield69
e7cd1a8e68 lastfiles: lint 2023-01-25 22:32:09 +13:00
Garfield69
21a6ce12c8 lastfiles: new search and selectors
- reorder cats to match pulldown for future ease spotting cat changes
- disable sort due to duplicate type keyword on search parms
- while there is now an imdbid/tmdbid search option it  cannot be used by current yaml implementation due to path cat  filtering
- new row selectors
2023-01-25 22:26:59 +13:00
80 changed files with 3164 additions and 1305 deletions

View File

@@ -18,6 +18,7 @@ on:
- src/**/*.cs
- src/**/*.js
- '!src/Jackett.Common/Indexers/**'
- '!src/Jackett.Common/Models/IndexerConfig/**'
- '!src/Jackett.IntegrationTests/**'
- '!src/Jackett.Test/**'
pull_request:
@@ -27,6 +28,7 @@ on:
- src/**/*.cs
- src/**/*.js
- '!src/Jackett.Common/Indexers/**'
- '!src/Jackett.Common/Models/IndexerConfig/**'
- '!src/Jackett.IntegrationTests/**'
- '!src/Jackett.Test/**'
schedule:

View File

@@ -177,10 +177,12 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* BookTracker
* BootyTape
* Catorrent
* Devil-Torrents
* Darmowe torrenty
* Deildu
* DimeADozen (EzTorrent)
* DXP (Deaf Experts)
* Electro-Torrent
* EniaHD
* Erai-Raws
* ExKinoRay
@@ -222,6 +224,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* Torrents-Local
* TribalMixes
* Union Fansub
* vTorrent
* xTorrenty
* YggTorrent (YGG)
* ZOMB
@@ -233,9 +236,9 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* 0day.kiev
* 1ptbar
* 2 Fast 4 You [![(invite needed)][inviteneeded]](#)
* 2xFree
* 3ChangTrai (3CT) [![(invite needed)][inviteneeded]](#)
* 3D Torrents (3DT)
* 3Evils
* 4thD (4th Dimension)
* 52PT
* Abnormal
@@ -376,6 +379,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* HDCity [![(invite needed)][inviteneeded]](#)
* HDFans
* HDHome (HDBigger) [![(invite needed)][inviteneeded]](#)
* HDMaYi
* HDMonkey
* HDRoute [![(invite needed)][inviteneeded]](#)
* HDSky [![(invite needed)][inviteneeded]](#)
@@ -389,6 +393,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* HHanClub
* HQMusic
* House of Devil
* ICC2022
* iHDBits
* ImmortalSeed (iS)
* Immortuos
@@ -439,6 +444,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* MySpleen [![(invite needed)][inviteneeded]](#)
* NCore
* Nebulance (NBL) (TransmiTheNet)
* NicePT
* NorBits
* Old Toons World
* OpenCD [![(invite needed)][inviteneeded]](#)
@@ -491,6 +497,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* Secret Cinema
* SeedFile
* Shareisland
* SharkPT
* Shazbat [![(invite needed)][inviteneeded]](#)
* SiamBIT
* SkipTheCommercials
@@ -528,6 +535,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* The Vault [![(invite needed)][inviteneeded]](#)
* The-Crazy-Ones
* The-New-Fun
* TheDarkCommunity (TDC)
* TheEmpire (TE)
* TheLeachZone (TLZ)
* TheScenePlace (TSP)
@@ -566,6 +574,7 @@ A third-party Golang SDK for Jackett is available from [webtor-io/go-jackett](ht
* U2 (U2分享園@動漫花園) [![(invite needed)][inviteneeded]](#)
* UHDBits
* UnionGang
* UnleashTheCartoons
* UnlimitZ
* White Angel
* wOOt [![(invite needed)][inviteneeded]](#)

View File

@@ -7,7 +7,7 @@ variables:
jackettVersion: $(majorVersion).$(minorVersion).$(patchVersion)
buildConfiguration: Release
netCoreFramework: net6.0
netCoreSdkVersion: 6.0.403 # #13806 & dotnet/runtime#79796
netCoreSdkVersion: 6.0.x
# system.debug: true
trigger:

View File

@@ -570,39 +570,39 @@
<table id="jackett-search-results-datatable" class="dataTable compact cell-border hover stripe">
<thead>
<tr>
<th>Published</th>
<th>Published</th>
<th>Tracker</th>
<th class="text-center">Published</th>
<th class="text-center">Published</th>
<th class="text-center">Tracker</th>
<th>Name</th>
<th>Size</th>
<th>Size</th>
<th title="Files">F</th>
<th>Category</th>
<th title="Grabs">G</th>
<th title="Seeders">S</th>
<th title="Leechers">L</th>
<th title="DownloadVolumeFactor" class="fit">DLF</th>
<th title="UploadVolumeFactor" class="fit">ULF</th>
<th title="Download">DL</th>
<th class="text-center">Size</th>
<th class="text-center">Size</th>
<th class="text-center" title="Files">F</th>
<th class="text-center">Category</th>
<th class="text-center" title="Grabs">G</th>
<th class="text-center" title="Seeders">S</th>
<th class="text-center" title="Leechers">L</th>
<th class="text-center fit" title="DownloadVolumeFactor">DLF</th>
<th class="text-center fit" title="UploadVolumeFactor">ULF</th>
<th class="text-center" title="Download">DL</th>
</tr>
</thead>
<tbody>
{{#each Results}}
<tr class="jackett-search-results-row" data-imdb="{{Imdb}}" data-tmdb="{{TMDb}}" data-tvdb="{{TVDBId}}" data-tvmaze="{{TVMazeId}}" data-trakt="{{TraktId}}" data-douban="{{DoubanId}}" data-poster="{{Poster}}" data-description="{{Description}}">
<td>{{PublishDate}}</td>
<td>{{jacketTimespan PublishDate}}</td>
<td>{{Tracker}}</td>
<td class="text-center">{{PublishDate}}</td>
<td class="text-center" title="{{dateFormat PublishDate format="YYYY-MM-DD HH:mm:ss Z"}}">{{jacketTimespan PublishDate}}</td>
<td class="text-center">{{Tracker}}</td>
<td class="Title"><a href="{{Details}}" target="_blank">{{Title}}</a> <span class="release-labels"></span></td>
<td>{{Size}}</td>
<td class="fit">{{jacketSize Size}}</td>
<td>{{Files}}</td>
<td class="Cat">{{CategoryDesc}}</td>
<td>{{Grabs}}</td>
<td>{{Seeders}}</td>
<td>{{Peers}}</td>
<td class="DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
<td class="UploadVolumeFactor">{{UploadVolumeFactor}}</td>
<td class="downloadcolumn">
<td class="text-right">{{Size}}</td>
<td class="text-right fit">{{jacketSize Size}}</td>
<td class="text-center">{{Files}}</td>
<td class="text-center Cat">{{CategoryDesc}}</td>
<td class="text-center">{{Grabs}}</td>
<td class="text-center">{{Seeders}}</td>
<td class="text-center">{{Peers}}</td>
<td class="text-center DownloadVolumeFactor">{{DownloadVolumeFactor}}</td>
<td class="text-center UploadVolumeFactor">{{UploadVolumeFactor}}</td>
<td class="text-center downloadcolumn">
{{#if Link}}
<a class="downloadlink" title="Download locally" href="{{Link}}"><i class="fa fa-download"></i></a>
{{/if}}

View File

@@ -110,6 +110,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
selector: a[href*="imdb.com/title/tt"]
attribute: href
@@ -159,4 +162,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP Standard v1.5 Beta 5
# NexusPHP v1.7.32 2022-12-05

View File

@@ -0,0 +1,217 @@
---
id: 2xfree
name: 2xFree
description: "2xFree is a CHINESE Private Torrent Tracker for HD MOVIES / TV / GENERAL"
language: zh-CN
type: private
encoding: UTF-8
requestDelay: 2
links:
- https://pt.2xfree.org/
caps:
categorymappings:
- {id: 401, cat: Movies, desc: "Movies(电影)", default: true}
- {id: 402, cat: TV, desc: "TV Series(电视剧)", default: true}
- {id: 403, cat: TV, desc: "TV Shows(综艺)", default: true}
- {id: 404, cat: TV/Documentary, desc: "Documentaries(纪录片)", default: true}
- {id: 405, cat: TV/Anime, desc: "Animations(动画)", default: true}
- {id: 406, cat: Audio/Video, desc: "Music Videos(MV)", default: true}
- {id: 407, cat: TV/Sport, desc: "Sports(体育运动)", default: true}
- {id: 408, cat: Audio, desc: "HQ Audio(音乐)", default: true}
- {id: 409, cat: Other, desc: "Misc(其他)", default: true}
- {id: 410, cat: Books, desc: "eBook(电子书)", default: true}
- {id: 411, cat: PC/Games, desc: "PCGame(游戏)", default: true}
- {id: 413, cat: Audio/Video, desc: "AV(无码)", default: true}
- {id: 414, cat: Audio/Video, desc: "AV(有码)", default: true}
- {id: 420, cat: XXX, desc: "HAnime(H动画)", default: true}
- {id: 421, cat: XXX, desc: "HComic(H漫画)", default: true}
- {id: 422, cat: XXX, desc: "HGame(H游戏)", default: true}
- {id: 423, cat: XXX, desc: "IV(写真影片)", default: true}
- {id: 424, cat: XXX, desc: "IV(写真图集)", default: true}
# special
- {id: 526, cat: Movies, desc: "VRMovies(3D/VR电影)", default: true}
- {id: 527, cat: TV, desc: "VRSeries(3D/VR剧集)", default: true}
- {id: 528, cat: Audio/Video, desc: "AV(VR无码)", default: true}
- {id: 529, cat: Audio/Video, desc: "AV(VR有码)", default: true}
- {id: 530, cat: Console, desc: "VRGame(VR一体机游戏)", default: true}
- {id: 531, cat: PC/Games, desc: "PCVRGame(PCVR游戏)", default: true}
- {id: 532, cat: Other, desc: "VRTools(VR工具软件)", default: true}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, doubanid]
movie-search: [q, imdbid, doubanid]
music-search: [q]
book-search: [q]
settings:
- name: username
type: text
label: Username
- name: password
type: password
label: Password
- name: 2facode
type: text
label: 2FA code
- name: info_2fa
type: info
label: "About 2FA code"
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the ICC2022 Web Site. Otherwise just leave it empty."
- name: freeleech
type: checkbox
label: Search freeleech only
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"]
captcha:
type: image
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
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:
- path: torrents.php
categories: [401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 413, 420, 421, 422, 423, 424]
- path: special.php
categories: [526, 527, 528, 529, 530, 531, 532]
inputs:
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{ else }}{{ end }}{{ if or .Query.IMDBID .Query.DoubanID }} {{ else }}{{ .Keywords }}{{ end }}{{ if .Query.DoubanID }}{{ .Query.DoubanID }}{{ else }}{{ 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 (4 does not appear to work)
search_area: "{{ if or .Query.IMDBID .Query.DoubanID }}1{{ 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(a[href^="details.php?id="])
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title_default:
selector: a[href^="details.php?id="]
title_optional:
optional: true
selector: a[title][href^="details.php?id="]
attribute: title
title:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
attribute: href
doubanid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="movie.douban.com/subject/"]
attribute: href
date_elapsed:
# time type: time elapsed (default)
selector: td.rowfollow:nth-child(4) > 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_added:
# time added
selector: td.rowfollow:nth-child(4):not(:has(span))
optional: true
filters:
- name: append
args: " +08:00" # CST
- name: dateparse
args: "2006-01-0215:04:05 -07:00"
date:
text: "{{ if or .Result.date_elapsed .Result.date_added }}{{ or .Result.date_elapsed .Result.date_added }}{{ else }}now{{ end }}"
size:
selector: td.rowfollow:nth-child(5)
seeders:
selector: td.rowfollow:nth-child(6)
leechers:
selector: td.rowfollow:nth-child(7)
grabs:
selector: td.rowfollow:nth-child(8)
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
minimumratio:
text: 1.0
minimumseedtime:
# 1 day (as seconds = 24 x 60 x 60)
text: 86400
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.8.0 2023-01-26

View File

@@ -177,4 +177,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP Standard v1.5 Beta 4
# NexusPHP v3.0 2020-04-30

View File

@@ -1,181 +0,0 @@
---
id: 3evils
name: 3Evils
description: "3Evils is a Private Torrent Tracker for MOVIES / TV / GENERAL"
language: en-US
type: private
encoding: UTF-8
links:
- https://3evils.net/
caps:
categorymappings:
- {id: 1, cat: PC, desc: "Apps"}
- {id: 24, cat: Books, desc: "Books/Magazines"}
- {id: 12, cat: TV/Anime, desc: "Movies/Anime"}
- {id: 31, cat: Movies, desc: "Movies/FooKaS RG"}
- {id: 84, cat: Movies, desc: "Movies/Kids"}
- {id: 55, cat: Movies/WEB-DL, desc: "Movies/WEB-DL"}
- {id: 70, cat: Movies/WEB-DL, desc: "Movies/WEBRip"}
- {id: 71, cat: Movies/HD, desc: "Movies/x265"}
- {id: 64, cat: Movies/SD, desc: "Movies/XViD"}
- {id: 26, cat: Audio/Lossless, desc: "Music/FLAC"}
- {id: 10, cat: Audio/MP3, desc: "Music/MP3"}
- {id: 63, cat: Audio/Video, desc: "Music/Videos"}
- {id: 29, cat: Movies, desc: "Movies/Packs"}
- {id: 19, cat: Audio, desc: "Music/Packs"}
- {id: 61, cat: TV/Anime, desc: "TV/Anime"}
- {id: 85, cat: TV, desc: "TV/Kids"}
- {id: 86, cat: TV, desc: "TV/Packs"}
- {id: 82, cat: TV/Sport, desc: "TV/Sports"}
- {id: 36, cat: TV/HD, desc: "TV/x265"}
- {id: 45, cat: TV/SD, desc: "TV/XViD"}
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: 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. The default is <i>15</i>.
login:
path: takelogin.php
method: post
inputs:
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
use_ssl: 1
perm_ssl: ""
error:
- selector: td.embedded:contains("Login failed!")
test:
path: /
selector: a[href*="/logout.php?hash_please="]
search:
paths:
# https://www.3evils.net/browse.php?c16=1&c17=1&c3=1&c4=1&search=&searchin=title&incldead=0&only_free=1
- path: browse.php
inputs:
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
search: "{{ .Keywords }}"
# title, descr, genre, all
searchin: title
# 0 active, 1 incldead, 2 onlydead
incldead: 1
only_free: "{{ if .Config.freeleech }}1{{ else }}{{ end }}"
sort: "{{ .Config.sort }}"
type: "{{ .Config.type }}"
keywordsfilters:
- name: re_replace
args: ["(\\w+)", "+$1"] # prepend + to each word
rows:
selector: table.table-bordered tbody tr:has(a[href^="download.php?torrent="])
fields:
category:
selector: a[href^="browse.php?cat="]
attribute: href
filters:
- name: querystring
args: cat
title:
selector: a[href^="details.php?id="]
attribute: onmouseover
filters:
- name: regexp
args: "Tip\\('<b>(.*?)</b>"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?torrent="]
attribute: href
poster:
selector: a[href^="details.php?id="]
attribute: onmouseover
filters:
# onmouseover="Tip('<b>blahblah /><img src=\'img.php/tvmaze/80.jpg\' blahblah />');"
# <img src=\'./pic/noposter.png\'
- name: regexp
args: "src=\\\\'(.+?)\\\\'"
- name: replace
args: ["./pic/noposter.png", ""]
files:
selector: td:nth-last-child(9)
# 2 flavours of dates
date_day:
# Today<br> 10:20 AM
# Yesterday<br> 08:03 PM
selector: td:nth-last-child(7):contains("day")
# auto adjusted by site account profile
optional: true
date_year:
# Feb 14 2019<br> 10:20 AM
selector: td:nth-last-child(7):not(:contains("day"))
# auto adjusted by site account profile
optional: true
filters:
- name: dateparse
args: "Jan 2 2006 03:04 PM"
date:
text: "{{ if or .Result.date_day .Result.date_year }}{{ or .Result.date_day .Result.date_year }}{{ else }}now{{ end }}"
size:
selector: td:nth-last-child(6)
grabs:
selector: td:nth-last-child(5)
filters:
- name: regexp
args: (\d+)
seeders:
selector: td:nth-last-child(4)
leechers:
selector: td:nth-last-child(3)
downloadvolumefactor:
case:
"a.info:contains(\"[FREE]\")": 0
"*": 1
uploadvolumefactor:
case:
"*": 1
minimumratio:
text: 1.0
minimumseedtime:
# 2 days (as seconds = 2 x 24 x 60 x 60)
text: 172800
# U-232 V5 (customised)

View File

@@ -11,17 +11,22 @@ links:
caps:
categorymappings:
- {id: 1, cat: Movies, desc: "Filmes"}
- {id: 2, cat: TV, desc: "TV Séries"}
- {id: 3, cat: Other, desc: "Cursos"}
- {id: 4, cat: Console, desc: "Jogos"}
- {id: 5, cat: PC, desc: "Aplicativos"}
- {id: 6, cat: TV/Anime, desc: "Animes"}
- {id: 7, cat: Other, desc: "Materiais de Apoio"}
- {id: 2, cat: TV, desc: "Séries"}
- {id: 8, cat: TV/Anime, desc: "Animes"}
- {id: 5, cat: Other, desc: "Cursos"}
- {id: 6, cat: Other, desc: "Materiais de Apoio"}
- {id: 7, cat: Books, desc: "eBooks / Revistas / Apostilas"}
- {id: 9, cat: Audio/Video, desc: "Shows"}
- {id: 10, cat: PC, desc: "Programas"}
- {id: 11, cat: TV/Documentary, desc: "Documentários"}
- {id: 4, cat: Console, desc: "Games"}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, tvdbid, tmdbid]
movie-search: [q, imdbid, tmdbid]
book-search: [q]
music-search: [q]
settings:
- name: apikey
@@ -169,6 +174,6 @@ search:
# minimumratio:
# text: 0.4
minimumseedtime:
# 5 days (as seconds = 5 x 24 x 60 x 60)
text: 432000
# 7 days (as seconds = 7 x 24 x 60 x 60)
text: 604800
# json UNIT3D 6.1.0

View File

@@ -99,9 +99,6 @@ login:
inputs:
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
use_ssl: 1
perm_ssl: ""
returnto: "/"
error:
- selector: table.main:contains("failed")
test:

View File

@@ -93,6 +93,7 @@ search:
paths:
- path: browse.php
inputs:
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
search: "{{ .Keywords }}"
# 0 active, 1 incldead, 2 onlydead
incldead: 1
@@ -101,10 +102,15 @@ search:
sort: "{{ .Config.sort }}"
type: "{{ .Config.type }}"
keywordsfilters:
- name: re_replace
args: ["\\s+", " "] # More than 1 space to 1 space
- name: re_replace
args: ["(\\w+)", "+$1"] # prepend + to each word
- name: trim
rows:
selector: table > tbody > tr[class]
filters:
- name: andmatch
fields:
# there are two styles, we support both

View File

@@ -127,6 +127,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
@@ -186,4 +189,4 @@ search:
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.16
# NexusPHP v1.7.31 2022-11-14

View File

@@ -35,6 +35,10 @@ download:
attribute: href
search:
headers:
# site blocks automation User-Agents, so slightly alter it here (e.g. Safari/537.37 > Safari/537.36)
User-Agent: ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"]
paths:
- path: "{{ if .Keywords }}search?query={{ .Keywords }}{{ else }}latest{{ end }}"
@@ -65,7 +69,7 @@ search:
attribute: title
filters:
- name: append
args: " -09:00" # CUS
args: " +00:00" # GMT
- name: dateparse
args: "02 Jan, 2006 15:04 -07:00"
size:

View File

@@ -0,0 +1,169 @@
---
id: devil-torrents
name: Devil-Torrents
description: "Devil-Torrents is a POLISH Semi-Private Torrent Tracker for MOVIES / TV / GENERAL"
language: pl-PL
type: semi-private
encoding: UTF-8
links:
- https://devil-torrents.pl/
caps:
categorymappings:
- {id: 1, cat: Movies/SD, desc: "Filmy XviD/DivX"}
- {id: 748, cat: Movies/UHD, desc: "Filmy 4K"}
- {id: 4, cat: Movies/BluRay, desc: "Filmy Blu-Ray/HD"}
- {id: 642, cat: Movies/HD, desc: "Filmy x264/h264"}
- {id: 596, cat: Movies/HD, desc: "Filmy x265/h265"}
- {id: 3, cat: Movies/DVD, desc: "Filmy DVD"}
- {id: 5, cat: Movies/3D, desc: "Filmy 3D"}
- {id: 2, cat: Movies/HD, desc: "Filmy RMVB"}
- {id: 362, cat: Movies/SD, desc: "Filmy IVO"}
- {id: 7, cat: TV, desc: "TV/Seriale"}
- {id: 702, cat: Movies/SD, desc: "TS/CAM"}
- {id: 8, cat: XXX, desc: "Erotyka"}
- {id: 10, cat: Audio, desc: "Muzyka"}
- {id: 11, cat: PC, desc: "Programy"}
- {id: 12, cat: PC/Mobile-Other, desc: "GSM/PDA"}
- {id: 13, cat: Console, desc: "Konsole"}
- {id: 14, cat: PC/Games, desc: "Gry PC"}
- {id: 15, cat: Movies, desc: "Dla Dzieci"}
- {id: 16, cat: Books, desc: "Książki"}
- {id: 525, cat: PC/Mac, desc: "Mac"}
- {id: 18, cat: PC, desc: "Linux"}
- {id: 19, cat: TV/Sport, desc: "Sport"}
- {id: 699, cat: TV/Anime, desc: "Anime"}
- {id: 21, cat: Other, desc: "Inne"}
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: multilang
type: checkbox
label: Replace MULTI by another language in release name
default: false
- name: multilanguage
type: select
label: Replace MULTI by this language
default: POLISH
options:
POLISH: POLISH
MULTI.POLISH: MULTI.POLISH
login:
path: logowanie
method: form
form: form[action="takelogin.php"]
captcha:
type: image
selector: img[src^="img.php?size=3"]
input: vImageCodP
inputs:
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
returnto: ""
error:
- selector: div#center-side:contains("Logowanie nie")
test:
path: /
selector: a[href$="/logout.php"]
search:
# https://devil-torrents.pl/szukaj.php?search=%&typ=torrent&c4=1&c748=1
paths:
- path: szukaj.php
inputs:
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
typ: torrent
search: "{{ if .Keywords }}{{ .Keywords }}{{ else }}%{{ end }}"
rows:
selector: table.test5 > tbody > tr > td > div[id]:has(a[href^="download/"])
fields:
category:
selector: div#kategoria-gatunek-1
case:
"div#kategoria-gatunek-1:contains(\"Filmy XviD/DivX\")": 1
"div#kategoria-gatunek-1:contains(\"Filmy 4K\")": 748
"div#kategoria-gatunek-1:contains(\"Filmy Blu-Ray/HD\")": 4
"div#kategoria-gatunek-1:contains(\"Filmy x264/h264\")": 642
"div#kategoria-gatunek-1:contains(\"Filmy x265/h265\")": 596
"div#kategoria-gatunek-1:contains(\"Filmy DVD\")": 3
"div#kategoria-gatunek-1:contains(\"Filmy 3D\")": 5
"div#kategoria-gatunek-1:contains(\"Filmy RMVB\")": 2
"div#kategoria-gatunek-1:contains(\"Filmy IVO\")": 362
"div#kategoria-gatunek-1:contains(\"TV/Seriale\")": 7
"div#kategoria-gatunek-1:contains(\"TS/CAM\")": 702
"div#kategoria-gatunek-1:contains(\"Erotyka\")": 8
"div#kategoria-gatunek-1:contains(\"Muzyka\")": 10
"div#kategoria-gatunek-1:contains(\"Programy\")": 11
"div#kategoria-gatunek-1:contains(\"GSM/PDA\")": 12
"div#kategoria-gatunek-1:contains(\"Konsole\")": 13
"div#kategoria-gatunek-1:contains(\"Gry PC\")": 14
"div#kategoria-gatunek-1:contains(\"Dla Dzieci\")": 15
"div#kategoria-gatunek-1:contains(\"Książki\")": 16
"div#kategoria-gatunek-1:contains(\"Mac\")": 525
"div#kategoria-gatunek-1:contains(\"Linux\")": 18
"div#kategoria-gatunek-1:contains(\"Sport\")": 19
"div#kategoria-gatunek-1:contains(\"Anime\")": 699
"div#kategoria-gatunek-1:contains(\"Inne\")": 21
title_phase1:
selector: a[href^="/torrent/"]
attribute: title
title_multilang:
selector: a[href^="/torrent/"]
attribute: title
filters:
- name: re_replace
args: ["(?i)(\\bmulti\\b)", "{{ .Config.multilanguage }}"]
title:
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
details:
selector: a[href^="/torrent/"]
attribute: href
download:
selector: a[href^="download/"]
attribute: href
poster:
selector: img.browse_poster
attribute: src
date:
selector: td.descr3
filters:
- name: regexp
args: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})
- name: append
args: " +01:00" # CET
- name: dateparse
args: "2006-01-02 15:04:05 -07:00"
size:
selector: td.descr3
filters:
- name: regexp
args: (\d{1,4}\.\d{2}\s+?[T|G|M|k]B)
seeders:
selector: font[color="green"] > b, font[color="red"] > b
leechers:
selector: font[color="green"]:nth-of-type(2) > b, font[color="red"]:nth-of-type(2) > b
grabs:
selector: td.descr3
filters:
- name: regexp
args: (\d{1,4}) razy
downloadvolumefactor:
text: 0
uploadvolumefactor:
text: 1
# engine n/a

View File

@@ -0,0 +1,183 @@
---
id: electro-torrent
name: Electro-Torrent
description: "Electro-Torrent is a POLISH Semi-Private Torrent Tracker for MOVIES / TV / GENERAL"
language: pl-PL
type: semi-private
encoding: UTF-8
links:
- http://electro-torrent.pl/
caps:
categorymappings:
- {id: 1, cat: Movies/SD, desc: "Filmy XviD/DivX"}
- {id: 769, cat: Movies/HD, desc: "Filmy 1080p AVC"}
- {id: 770, cat: Movies/HD, desc: "Filmy x264/1080p"}
- {id: 4, cat: Movies/HD, desc: "Filmy x264/720p"}
- {id: 642, cat: Movies/HD, desc: "Filmy x264/h264"}
- {id: 723, cat: Movies/UHD, desc: "Filmy 4K UHD"}
- {id: 1160, cat: Movies/UHD, desc: "Filmy x265/2160p"}
- {id: 1116, cat: Movies/HD, desc: "Filmy x265/1080p"}
- {id: 1204, cat: Movies/HD, desc: "Filmy x265/720p"}
- {id: 596, cat: Movies/HD, desc: "Filmy x265/h265"}
- {id: 1072, cat: Movies, desc: "Filmy - WAREZY"}
- {id: 3, cat: Movies/DVD, desc: "Filmy DVD"}
- {id: 5, cat: Movies/3D, desc: "Filmy 3D"}
- {id: 362, cat: Movies/SD, desc: "Filmy IVO"}
- {id: 696, cat: Movies/SD, desc: "Filmy TS/CAM"}
- {id: 7, cat: TV, desc: "TV/Seriale"}
- {id: 8, cat: XXX, desc: "Erotyka"}
- {id: 10, cat: Audio, desc: "Muzyka"}
- {id: 11, cat: PC, desc: "Programy"}
- {id: 12, cat: PC/Mobile-Other, desc: "GSM/PDA"}
- {id: 13, cat: Console, desc: "Konsole"}
- {id: 14, cat: PC/Games, desc: "Gry PC"}
- {id: 1045, cat: PC/Games, desc: "Gry PC - Warezy"}
- {id: 15, cat: Movies, desc: "Dla Dzieci"}
- {id: 16, cat: Books, desc: "Książki"}
- {id: 525, cat: PC/Mac, desc: "Mac"}
- {id: 18, cat: PC, desc: "Linux"}
- {id: 19, cat: TV/Sport, desc: "Sport"}
- {id: 907, cat: TV/Anime, desc: "Anime"}
- {id: 21, cat: Other, desc: "Inne"}
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: multilang
type: checkbox
label: Replace MULTI by another language in release name
default: false
- name: multilanguage
type: select
label: Replace MULTI by this language
default: POLISH
options:
POLISH: POLISH
MULTI.POLISH: MULTI.POLISH
login:
path: logowanie
method: form
form: form[action="takelogin.php"]
captcha:
type: image
selector: img[src^="img.php?size=3"]
input: vImageCodP
inputs:
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
returnto: ""
error:
- selector: div#center-side:contains("Logowanie nie")
test:
path: /
selector: a[href$="/logout.php"]
search:
# http://electro-torrent.pl/szukaj.php?search=&typ=torrent&cat=0
paths:
- path: szukaj.php
inputs:
# does not support multi cat selection, defaulting to all
# $raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
cat: 0
typ: torrent
search: "{{ .Keywords }}"
rows:
selector: table.test5 > tbody > tr > td > div[id]:has(a[href*="/download/"])
fields:
category:
selector: div#kategoria-gatunek-1
case:
"div#kategoria-gatunek-1:contains(\"Filmy XviD/DivX\")": 1
"div#kategoria-gatunek-1:contains(\"Filmy 1080p AVC\")": 769
"div#kategoria-gatunek-1:contains(\"Filmy x264/1080p\")": 770
"div#kategoria-gatunek-1:contains(\"Filmy x264/720p\")": 4
"div#kategoria-gatunek-1:contains(\"Filmy x264/h264\")": 642
"div#kategoria-gatunek-1:contains(\"Filmy 4K UHD\")": 723
"div#kategoria-gatunek-1:contains(\"Filmy x265/2160p\")": 1160
"div#kategoria-gatunek-1:contains(\"Filmy x265/1080p\")": 1116
"div#kategoria-gatunek-1:contains(\"Filmy x265/720p\")": 1204
"div#kategoria-gatunek-1:contains(\"Filmy x265/h265\")": 596
"div#kategoria-gatunek-1:contains(\"Filmy - WAREZY\")": 1072
"div#kategoria-gatunek-1:contains(\"Filmy DVD\")": 3
"div#kategoria-gatunek-1:contains(\"Filmy 3D\")": 5
"div#kategoria-gatunek-1:contains(\"Filmy IVO\")": 362
"div#kategoria-gatunek-1:contains(\"Filmy TS/CAM\")": 696
"div#kategoria-gatunek-1:contains(\"TV/Seriale\")": 7
"div#kategoria-gatunek-1:contains(\"Erotyka\")": 8
"div#kategoria-gatunek-1:contains(\"Muzyka\")": 10
"div#kategoria-gatunek-1:contains(\"Programy\")": 11
"div#kategoria-gatunek-1:contains(\"GSM/PDA\")": 12
"div#kategoria-gatunek-1:contains(\"Konsole\")": 13
"div#kategoria-gatunek-1:contains(\"Gry PC - Warezy\")": 1045
"div#kategoria-gatunek-1:contains(\"Gry PC\")": 14
"div#kategoria-gatunek-1:contains(\"Dla Dzieci\")": 15
"div#kategoria-gatunek-1:contains(\"Książki\")": 16
"div#kategoria-gatunek-1:contains(\"Mac\")": 525
"div#kategoria-gatunek-1:contains(\"Linux\")": 18
"div#kategoria-gatunek-1:contains(\"Sport\")": 19
"div#kategoria-gatunek-1:contains(\"Anime\")": 907
"div#kategoria-gatunek-1:contains(\"Inne\")": 21
title_phase1:
selector: a[href^="/torrent/"]
attribute: title
title_multilang:
selector: a[href^="/torrent/"]
attribute: title
filters:
- name: re_replace
args: ["(?i)(\\bmulti\\b)", "{{ .Config.multilanguage }}"]
title:
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
details:
selector: a[href^="/torrent/"]
attribute: href
download:
selector: a[href*="/download/"]
attribute: href
poster:
selector: img.browse_poster
attribute: src
date:
selector: td.descr3
filters:
- name: regexp
args: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})
- name: append
args: " +01:00" # CET
- name: dateparse
args: "2006-01-02 15:04:05 -07:00"
size:
selector: td.descr3
filters:
- name: regexp
args: (\d{1,4}\.\d{2}\s+?[T|G|M|k]B)
seeders:
selector: font[color="green"] > b, font[color="red"] > b
leechers:
selector: font[color="green"]:nth-of-type(2) > b, font[color="red"]:nth-of-type(2) > b
grabs:
selector: td.descr3
filters:
- name: regexp
args: (\d{1,4}) razy
downloadvolumefactor:
text: 0
uploadvolumefactor:
text: 1
# engine n/a

View File

@@ -170,4 +170,4 @@ search:
minimumseedtime:
# 1 day (as seconds = 24 x 60 x 60)
text: 86400
# NexusPHP v1.0
# NexusPHP v1.1 2021-10-15

View File

@@ -32,6 +32,13 @@ settings:
- name: password
type: password
label: Password
- name: 2facode
type: text
label: 2FA code
- name: info_2fa
type: info
label: "About 2FA code"
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the HDAtmos Web Site. Otherwise just leave it empty."
- name: freeleech
type: checkbox
label: Search freeleech only
@@ -66,12 +73,14 @@ login:
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
logout: ""
securelogin: ""
ssl: yes
trackerssl: ""
trackerssl: yes
error:
- selector: td.embedded:has(h2:contains("失败"))
message:
@@ -172,4 +181,4 @@ search:
remove: a, img
minimumratio:
text: 0.81
# NexusPHP Standard v1.5 Beta 4
# NexusPHP 1.7.31 2022-11-14

View File

@@ -1,123 +0,0 @@
---
id: hdbits
name: HDBits
description: "Best HD Tracker"
language: en-US
type: private
encoding: UTF-8
links:
- https://hdbits.org/
caps:
categorymappings:
- {id: 1, cat: "Movies", desc: "Movie"}
- {id: 2, cat: "TV", desc: "TV"}
- {id: 3, cat: "TV/Documentary", desc: "Documentary"}
- {id: 4, cat: "Audio", desc: "Music"}
- {id: 5, cat: "TV/Sport", desc: "Sport"}
- {id: 6, cat: "Audio", desc: "Audio Track"}
- {id: 7, cat: "XXX", desc: "XXX"}
- {id: 8, cat: "Other", desc: "Misc/Demo"}
modes:
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: 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: login
method: form
form: form
inputs:
uname: "{{ .Config.username }}"
password: "{{ .Config.password }}"
error:
- selector: table.main:contains("Login Failed!")
test:
path: my.php
search:
paths:
- path: browse.php
inputs:
$raw: "{{ range .Categories }}filter_cat[{{.}}]=1&{{end}}"
search: "{{ .Keywords }}"
descriptions: 0
imdbgt: 0
imdblt: 10
imdb: "{{ .Query.IMDBID }}"
sort: "{{ .Config.sort }}"
d: "{{ .Config.type }}"
rows:
selector: "table#torrent-list > tbody > tr:has(a[href^=\"/details.php?id=\"]){{ if .Config.freeleech }}:has(a[title=\"100% FL: no download is counted.\"]){{ else }}{{ end }}"
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title:
selector: td:nth-child(3) a
download:
selector: a[href^="/download.php"]
attribute: href
details:
selector: a[href^="/details.php?id="]
attribute: href
grabs:
selector: td:nth-child(7) a
size:
selector: td:nth-child(6)
seeders:
selector: td:nth-child(8)
leechers:
selector: td:nth-child(9)
date:
selector: td:nth-child(5)
filters:
- name: append
args: " ago"
imdbid:
selector: a[href*="imdb.com/title/tt"]
attribute: href
downloadvolumefactor:
case:
"a[title=\"25% Free Leech: only 75% of the download is counted.\"]": 0.25
"a[title=\"50% Free Leech: only half the download is counted.\"]": 0.5
"a[title=\"100% FL: no download is counted.\"]": 0
"*": 1
uploadvolumefactor:
text: 1
# engine tbd

View File

@@ -145,4 +145,4 @@ search:
img.pro_free2up: 2
img.pro_2up: 2
"*": 1
# NexusPHP 146364c08c (Customised)
# NexusPHP 2c858e7 (Customised)

View File

@@ -82,6 +82,7 @@ login:
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
@@ -138,6 +139,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
selector: a[href*="imdb.com/title/tt"]
attribute: href
@@ -189,4 +193,4 @@ search:
remove: a, img
minimumratio:
text: 0.81
# NexusPHP Standard v1.7.6
# NexusPHP v1.8.0 2023-01-20

View File

@@ -170,4 +170,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP Standard v1.5 Beta 4
# NexusPHP v2.0 2014-11-24

View File

@@ -0,0 +1,173 @@
---
id: hdmayi
name: HDMaYi
description: "HDMaYi (小蚂蚁PT站) is a CHINESE Private Torrent Tracker for HD MOVIES / TV / GENERAL"
language: zh-CN
type: private
encoding: UTF-8
links:
- http://hdmayi.com/ # does not support https properly
caps:
categorymappings:
- {id: 401, cat: Movies, desc: "Movies/电影"}
- {id: 404, cat: TV/Documentary, desc: "Documentaries/纪录片"}
- {id: 405, cat: TV/Anime, desc: "Animations/动漫"}
- {id: 402, cat: TV, desc: "TV Series/电视剧"}
- {id: 403, cat: TV, desc: "TV Shows/综艺"}
- {id: 406, cat: Audio/Video, desc: "MusicVideo/MV"}
- {id: 407, cat: TV/Sport, desc: "Sports/体育"}
- {id: 409, cat: Other, desc: "Misc/其他"}
- {id: 408, cat: Audio, desc: "Music/音乐"}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, doubanid]
movie-search: [q, imdbid, doubanid]
music-search: [q]
settings:
- name: cookie
type: text
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
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:
method: cookie
inputs:
cookie: "{{ .Config.cookie }}"
test:
path: index.php
selector: a[href="logout.php"]
search:
paths:
- path: torrents.php
inputs:
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{ else }}{{ end }}{{ if or .Query.IMDBID .Query.DoubanID }} {{ else }}{{ .Keywords }}{{ end }}{{ if .Query.DoubanID }}{{ .Query.DoubanID }}{{ else }}{{ 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 (4 does not appear to work)
search_area: "{{ if or .Query.IMDBID .Query.DoubanID }}1{{ 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(a[href^="details.php?id="])
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title_default:
selector: a[href^="details.php?id="]
title_optional:
optional: true
selector: a[title][href^="details.php?id="]
attribute: title
title:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
attribute: href
doubanid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="movie.douban.com/subject/"]
attribute: href
date_elapsed:
# time type: time elapsed (default)
selector: td.rowfollow:nth-child(4) > 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_added:
# time added
selector: td.rowfollow:nth-child(4):not(:has(span))
optional: true
filters:
- name: append
args: " +08:00" # CST
- name: dateparse
args: "2006-01-0215:04:05 -07:00"
date:
text: "{{ if or .Result.date_elapsed .Result.date_added }}{{ or .Result.date_elapsed .Result.date_added }}{{ else }}now{{ end }}"
size:
selector: td.rowfollow:nth-child(5)
seeders:
selector: td.rowfollow:nth-child(6)
leechers:
selector: td.rowfollow:nth-child(7)
grabs:
selector: td.rowfollow:nth-child(8)
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
minimumratio:
text: 1.0
minimumseedtime:
# 1 day (as seconds = 24 x 60 x 60)
text: 86400
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.29 2022-10-12

View File

@@ -130,6 +130,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]

View File

@@ -130,6 +130,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
@@ -189,4 +192,4 @@ search:
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.24 2022-08-30
# NexusPHP v1.7.30 2022-11-05

View File

@@ -0,0 +1,203 @@
---
id: icc2022
name: ICC2022
description: "ICC2022 (冰淇淋) is a CHINESE Private Torrent Tracker for HD MOVIES / TV / GENERAL"
language: zh-CN
type: private
encoding: UTF-8
requestDelay: 2
links:
- https://www.icc2022.com/
caps:
categorymappings:
- {id: 401, cat: Movies, desc: "Movies/电影", default: true}
- {id: 404, cat: TV/Documentary, desc: "Documentaries/纪录片", default: true}
- {id: 405, cat: TV/Anime, desc: "Animations/动漫", default: true}
- {id: 402, cat: TV, desc: "TV Series/电视剧", default: true}
- {id: 403, cat: TV, desc: "TV Shows/综艺", default: true}
- {id: 406, cat: Audio/Video, desc: "MusicVideo/MV", default: true}
- {id: 407, cat: TV/Sport, desc: "Sports/体育", default: true}
- {id: 409, cat: Other, desc: "Misc/其他", default: true}
- {id: 408, cat: Audio, desc: "Music/音乐", default: true}
# special
- {id: 410, cat: Movies, desc: "Video/视频资料", default: true}
- {id: 411, cat: Audio, desc: "Audio/音频资料", default: true}
- {id: 412, cat: Other, desc: "Other/其他资料", default: true}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, doubanid]
movie-search: [q, imdbid, doubanid]
music-search: [q]
settings:
- name: username
type: text
label: Username
- name: password
type: password
label: Password
- name: 2facode
type: text
label: 2FA code
- name: info_2fa
type: info
label: "About 2FA code"
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the ICC2022 Web Site. Otherwise just leave it empty."
- name: freeleech
type: checkbox
label: Search freeleech only
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"]
captcha:
type: image
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
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:
- path: torrents.php
categories: [401, 402, 403, 404, 405, 406, 407, 408, 409]
- path: special.php
categories: [410, 411, 412]
inputs:
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{ else }}{{ end }}{{ if or .Query.IMDBID .Query.DoubanID }} {{ else }}{{ .Keywords }}{{ end }}{{ if .Query.DoubanID }}{{ .Query.DoubanID }}{{ else }}{{ 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 (4 does not appear to work)
search_area: "{{ if or .Query.IMDBID .Query.DoubanID }}1{{ 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(a[href^="details.php?id="])
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title_default:
selector: a[href^="details.php?id="]
title_optional:
optional: true
selector: a[title][href^="details.php?id="]
attribute: title
title:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
attribute: href
doubanid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="movie.douban.com/subject/"]
attribute: href
date_elapsed:
# time type: time elapsed (default)
selector: td.rowfollow:nth-child(4) > 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_added:
# time added
selector: td.rowfollow:nth-child(4):not(:has(span))
optional: true
filters:
- name: append
args: " +08:00" # CST
- name: dateparse
args: "2006-01-0215:04:05 -07:00"
date:
text: "{{ if or .Result.date_elapsed .Result.date_added }}{{ or .Result.date_elapsed .Result.date_added }}{{ else }}now{{ end }}"
size:
selector: td.rowfollow:nth-child(5)
seeders:
selector: td.rowfollow:nth-child(6)
leechers:
selector: td.rowfollow:nth-child(7)
grabs:
selector: td.rowfollow:nth-child(8)
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
minimumratio:
text: 1.0
minimumseedtime:
# 1 day (as seconds = 24 x 60 x 60)
text: 86400
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.29 2022-10-13

View File

@@ -130,6 +130,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
@@ -189,4 +192,4 @@ search:
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP 1.7.29 2022-10-13
# NexusPHP v1.7.30 2022-11-05

View File

@@ -439,6 +439,13 @@ settings:
type: info
label: How to get the Cookie
default: "<ol><li><a href=\"http://filbi1976.org/ucp.php?mode=login\" target =_blank>Login</a> 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: useragent
type: text
label: User-Agent
- name: info_useragent
type: info
label: How to get the User-Agent
default: "<ol><li>From the same place you fetched the cookie,<li>Find <b>'user-agent:'</b> in the <b>Request Headers</b> section<li><b>Select</b> and <b>Copy</b> the whole user-agent string <i>(everything after 'user-agent: ')</i> and <b>Paste</b> here.</ol>"
- name: sort
type: select
label: Sort requested from site
@@ -464,6 +471,9 @@ login:
path: index.php
search:
headers:
User-Agent: ["{{ .Config.useragent }}"]
paths:
# http://filbi1976.org/search.php?t=0&cs=1&cs_post=1&sc=1&keywords=&cs_where=title&cs_forb=&submit=Hand+Search&fid%5B%5D=0&cs_format=0&cs_year=0&cs_filter=0&sk=nt&sd=d&cs_private=0
- path: search.php
@@ -490,11 +500,11 @@ search:
fields:
category:
selector: a[href*="/viewtopic.php?f="]
selector: a[href$=".html"]
attribute: href
filters:
- name: querystring
args: f
- name: regexp
args: -f(\d+)
title:
selector: a.topictitle
details:

View File

@@ -8,58 +8,58 @@ encoding: utf-8
followredirect: true
requestDelay: 2
links:
- https://www.lastfiles.ro/
- http://www.lastfiles.ro/
- https://www.last-torrents.org/
- http://www.last-torrents.org/
legacylinks:
- http://last-torrents.org/
- https://last-torrents.org/
- https://www.lastfiles.ro/
- http://www.lastfiles.ro/
- http://www.last-torrents.org/
caps:
categorymappings:
- {id: 31, cat: Movies/UHD, desc: "Movies: 4K", default: true}
- {id: 1, cat: TV/Anime, desc: "Movies: Anime", default: true}
- {id: 2, cat: TV/Anime, desc: "Movies: Anime-Ro", default: true}
- {id: 5, cat: Movies/BluRay, desc: "Movies: BluRay", default: true}
- {id: 9, cat: Movies/DVD, desc: "Movies: DVD", default: true}
- {id: 11, cat: Movies/HD, desc: "Movies: HD", default: true}
- {id: 58, cat: Movies/SD, desc: "Movies: Cam", default: true}
- {id: 61, cat: Movies/3D, desc: "Movies: 3D", default: true}
- {id: 24, cat: Movies/SD, desc: "Movies: XVID", default: true}
- {id: 16, cat: Movies, desc: "Movies: Old", default: true}
- {id: 54, cat: Movies/WEB-DL, desc: "Movies: WEB-DL", default: true}
- {id: 56, cat: TV/Documentary, desc: "Documentary", default: true}
- {id: 18, cat: Movies, desc: "Movies: Pack", default: true}
- {id: 32, cat: Movies/UHD, desc: "Movies: 4KRO", default: true}
- {id: 6, cat: Movies/BluRay, desc: "Movies: BluRay-Ro", default: true}
- {id: 12, cat: Movies/HD, desc: "Movies: HD-Ro", default: true}
- {id: 81, cat: Movies/HD, desc: "Movies: x265-Ro", default: true}
- {id: 82, cat: Movies/HD, desc: "Movies: x265", default: true}
- {id: 9, cat: Movies/DVD, desc: "Movies: DVD", default: true}
- {id: 10, cat: Movies/DVD, desc: "Movies: DVD-Ro", default: true}
- {id: 59, cat: Movies/SD, desc: "Movies: Cam-RO", default: true}
- {id: 25, cat: Movies/SD, desc: "Movies: XVID-Ro", default: true}
- {id: 11, cat: Movies/HD, desc: "Movies: HD", default: true}
- {id: 12, cat: Movies/HD, desc: "Movies: HD-Ro", default: true}
- {id: 13, cat: TV/HD, desc: "HDTV Episodes", default: true}
- {id: 14, cat: TV/HD, desc: "HDTV Episodes-Ro", default: true}
- {id: 16, cat: Movies, desc: "Movies: Old", default: true}
- {id: 17, cat: Movies, desc: "Movies: Old-Ro", default: true}
- {id: 55, cat: Movies/WEB-DL, desc: "Movies: WEB-DL Ro", default: true}
- {id: 57, cat: TV/Documentary, desc: "Documentary-Ro", default: true}
- {id: 62, cat: Movies/3D, desc: "Movies: 3D-Ro", default: true}
- {id: 18, cat: Movies, desc: "Movies: Pack", default: true}
- {id: 19, cat: Movies, desc: "Movies: Pack-Ro", default: true}
- {id: 20, cat: TV, desc: "TV Episodes", default: true}
- {id: 21, cat: TV, desc: "TV Episodes-Ro", default: true}
- {id: 13, cat: TV/HD, desc: "HDTV Episodes", default: true}
- {id: 14, cat: TV/HD, desc: "HDTV Episodes-Ro", default: true}
- {id: 28, cat: Audio, desc: "Music", default: true}
- {id: 51, cat: PC/Mobile-Android, desc: "Android Apps", default: true}
- {id: 22, cat: Other, desc: "RoContent", default: true}
- {id: 24, cat: Movies/SD, desc: "Movies: XVID", default: true}
- {id: 25, cat: Movies/SD, desc: "Movies: XVID-Ro", default: true}
- {id: 26, cat: PC/0day, desc: "Software", default: true}
- {id: 27, cat: XXX, desc: "Movies: XXX", default: false}
- {id: 28, cat: Audio, desc: "Music", default: true}
- {id: 30, cat: PC/Games, desc: "Games: PC-ISO", default: true}
- {id: 31, cat: Movies/UHD, desc: "Movies: 4K", default: true}
- {id: 32, cat: Movies/UHD, desc: "Movies: 4K-Ro", default: true}
- {id: 33, cat: PC/Games, desc: "Games: Packs", default: true}
- {id: 1, cat: TV/Anime, desc: "Movies: Anime", default: true}
- {id: 2, cat: TV/Anime, desc: "Movies: Anime-Ro", default: true}
- {id: 42, cat: TV/Sport, desc: "Sport", default: true}
- {id: 43, cat: Books, desc: "Documents", default: true}
- {id: 44, cat: Other, desc: "Images", default: true}
- {id: 49, cat: Other, desc: "Diverse", default: true}
- {id: 22, cat: Other, desc: "RoContent", default: true}
- {id: 51, cat: PC/Mobile-Android, desc: "Android Apps", default: true}
- {id: 54, cat: Movies/WEB-DL, desc: "Movies: WEB-DL", default: true}
- {id: 55, cat: Movies/WEB-DL, desc: "Movies: WEB-DL Ro", default: true}
- {id: 56, cat: TV/Documentary, desc: "Documentary", default: true}
- {id: 57, cat: TV/Documentary, desc: "Documentary-Ro", default: true}
- {id: 58, cat: Movies/SD, desc: "Movies: Cam", default: true}
- {id: 59, cat: Movies/SD, desc: "Movies: Cam-Ro", default: true}
- {id: 60, cat: XXX/ImageSet, desc: "Images: XXX", default: false}
- {id: 27, cat: XXX, desc: "Movies: XXX", default: false}
- {id: 61, cat: Movies/3D, desc: "Movies: 3D", default: true}
- {id: 62, cat: Movies/3D, desc: "Movies: 3D-Ro", default: true}
- {id: 81, cat: Movies/HD, desc: "Movies: x265-Ro", default: true}
- {id: 82, cat: Movies/HD, desc: "Movies: x265", default: true}
modes:
search: [q]
@@ -83,22 +83,22 @@ settings:
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">FlareSolverr</a> to access it.
- name: sort
type: select
label: Sort requested from site
default: 3
options:
3: created
6: seeders
4: size
1: title
- name: type
type: select
label: Order requested from site
default: desc
options:
desc: desc
asc: asc
# - name: sort
# type: select
# label: Sort requested from site
# default: 3
# options:
# 3: created
# 6: seeders
# 4: size
# 1: title
# - name: type
# type: select
# label: Order requested from site
# default: desc
# options:
# desc: desc
# asc: asc
login:
path: takelogin.php
@@ -118,88 +118,54 @@ login:
search:
paths:
# https://www.last-torrents.org/externalid?searchex=tt5834760&search_by=imdbid
# cannot support imdbid or tmdbid searches while using path category filters
- path: browse.php
categories: [31, 5, 9, 11, 58, 61, 24, 16, 54, 56, 18, 32, 6, 12, 81, 82, 10, 59, 25, 17, 55, 57, 62, 19, 20, 21, 13, 14, 28, 51, 26, 30, 33, 1, 2, 42, 43, 44, 49, 22]
categories: [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 30, 31, 32, 33, 42, 43, 44, 49, 51, 54, 55, 56, 57, 58, 59, 60, 61, 62, 81, 82]
- path: browseadult.php
categories: [60, 27]
categories: [27, 60]
keywordsfilters:
- name: re_replace
args: ["(\\w+)", " +$1"] # prepend + to each word
inputs:
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
search: "{{ if .Query.Genre }}{{ .Query.Genre }} {{ else }}{{ end }}{{ .Keywords }}"
# title, descr, genre, all
# title, genre, all
searchin: "{{ if .Query.Genre }}all{{ else }}title{{ end }}"
# 0 active, 1 incldead, 2 onlydead
incldead: 1
only_free: "{{ if .Config.freeleech }}1{{ else }}{{ end }}"
sort: "{{ .Config.sort }}"
type: "{{ .Config.type }}"
# 0 active, 1 incldead, 2 onlydead, 3 free, 4 silver, 5 seedbox
type: "{{ if .Config.freeleech }}3{{ else }}1{{ end }}"
# sort and type can only be used in a non-search query due to conflicting parameters
# sort: "{{ .Config.sort }}"
# type: "{{ .Config.type }}"
rows:
selector: tbody > tr:has(a[href^="download.php"])
selector: div.py-3
fields:
category:
selector: img[src*="pic/caticons/1/categories"]
attribute: src
case:
img[src$="4k.png"]: 31
img[src$="bluray.png"]: 5
img[src$="dvd.png"]: 9
img[src$="hd.png"]: 11
img[src$="cam.png"]: 58
img[src$="3D.png"]: 61
img[src$="xvid.png"]: 24
img[src$="oldies.png"]: 16
img[src$="web-DL.png"]: 54
img[src$="doc.png"]: 56
img[src$="4kRO.png"]: 32
img[src$="bluray-ro.png"]: 6
img[src$="hd-ro.png"]: 12
img[src$="x265ro.png"]: 81
img[src$="x265.png"]: 82
img[src$="dvd-ro.png"]: 10
img[src$="cam_ro.png"]: 59
img[src$="xvid-ro.png"]: 25
img[src$="oldies-ro.png"]: 17
img[src$="web-DLRO.png"]: 55
img[src$="doc-ro.png"]: 57
img[src$="3DRO.png"]: 62
img[src$="pack-ro.png"]: 19
img[src$="tvepisode.png"]: 20
img[src$="tvepisode-ro.png"]: 21
img[src$="hdtve.png"]: 13
img[src$="hdtve-ro.png"]: 14
img[src$="music.png"]: 28
img[src$="android.png"]: 51
img[src$="soft.png"]: 26
img[src$="pciso.png"]: 30
img[src$="gpack.png"]: 33
img[src$="pack.png"]: 18
img[src$="anime.png"]: 1
img[src$="anime-ro.png"]: 2
img[src$="sport.png"]: 42
img[src$="docs.png"]: 43
img[src$="images.png"]: 44
img[src$="misc.png"]: 49
img[src$="rocontent.png"]: 22
img[src$="xxximgset.png"]: 60
img[src$="xxx.png"]: 27
selector: a[href*="cat="]
attribute: href
filters:
- name: querystring
args: cat
title:
selector: a[onmouseover]
selector: a[href^="t"]
details:
selector: a[onmouseover]
selector: a[href^="t"]
attribute: href
poster:
selector: a[onmouseover]
attribute: onmouseover
selector: a[href^="t"]
attribute: title
filters:
- name: regexp
args: "src=(.+?) "
- name: replace
args: ["./pic/noposter.png", ""]
genre:
selector: font[size]
selector: i:has(a[href$="searchin=genre"]), font[size]
filters:
- name: replace
args: [" & ", "_&_"]
- name: replace
args: ["Hip Hop", "Hip_Hop"]
- name: replace
@@ -210,28 +176,28 @@ search:
selector: a[href^="download.php"]
attribute: href
date_day:
selector: td:nth-child(4) b:contains("day")
selector: div.col-sm-4:nth-of-type(3):contains("day")
# auto adjusted by site account profile
optional: true
filters:
- name: fuzzytime
date_year:
selector: td:nth-child(4) b:not(:contains("day"))
selector: div.col-sm-4:nth-of-type(3):contains(":"):not(:contains("day"))
# auto adjusted by site account profile
optional: true
filters:
- name: dateparse
args: "Jan 2 2006 03:04 PM"
args: "Jan 2 2006, 03:04 PM"
date:
text: "{{ if or .Result.date_day .Result.date_year }}{{ or .Result.date_day .Result.date_year }}{{ else }}now{{ end }}"
size:
selector: td:nth-child(5)
selector: div.col-sm-4:nth-of-type(4)
grabs:
selector: td:nth-child(6)
selector: div.col-sm-4:contains("Completed") b
seeders:
selector: td:nth-child(7)
selector: div.col-sm-4:contains("Seeders") b
leechers:
selector: td:nth-child(8)
selector: div.col-sm-4:contains("Leechers") b
downloadvolumefactor:
case:
img[src$="half2.png"]: 0.5

View File

@@ -243,7 +243,7 @@ search:
img[src$="/docdivers.png"]: 21
img[src$="/dochistoire.png"]: 22
img[src$="/docsemitv.png"]: 164
img[src$="/docsseries.png"]: 198
img[src$="/unknown198.png"]: 198 # film doc spectacle
img[src$="/film3d.png"]: 25
img[src$="/film4k.png"]: 26
img[src$="/film4klight.png"]: 27
@@ -283,7 +283,7 @@ search:
img[src$="/loglinux.png"]: 71
img[src$="/logwindows.png"]: 72
img[src$="/animcoffret.png"]: 7
img[src$="/unknown197.png"]: 197 # serie episode
img[src$="/docsseries.png"]: 197
img[src$="/unknown182.png"]: 182 # serie jap anim
img[src$="/unknown166.png"]: 166 # serie docu
img[src$="/unknown194.png"]: 194 # serie docu divers
@@ -304,7 +304,7 @@ search:
img[src$="/seriewebrip.png"]: 93
img[src$="/seriesfrwebdl720p.png"]: 178
img[src$="/seriesfrwebdl1080p.png"]: 179
img[src$="/unknown199.png"]: 199 # serie webdl
img[src$="/seriewebdl.png"]: 199
img[src$="/serievostfrbdrip.png"]: 188
img[src$="/serievostfrdvdrip.png"]: 189
img[src$="/serievostfrhdrip.png"]: 190

View File

@@ -0,0 +1,179 @@
---
id: nicept
name: NicePT
description: "NicePT is a CHINESE Private Torrent Tracker for 3X"
language: zh-CN
type: private
encoding: UTF-8
links:
- https://www.nicept.net/
caps:
categorymappings:
- {id: 500, cat: XXX, desc: "日本有码"}
- {id: 401, cat: XXX, desc: "日本无码"}
- {id: 402, cat: XXX, desc: "欧美"}
- {id: 501, cat: XXX, desc: "其他(限制级)"}
- {id: 403, cat: XXX/Other, desc: "动漫(限制级)"}
- {id: 503, cat: XXX, desc: "真人秀,自拍(限制级)"}
- {id: 404, cat: XXX/ImageSet, desc: "写真、套图"}
- {id: 504, cat: XXX, desc: "SM调教限制级"}
modes:
search: [q]
settings:
- name: username
type: text
label: Username
- name: password
type: password
label: Password
- name: 2facode
type: text
label: 2FA code
- name: info_2fa
type: info
label: "About 2FA code"
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the NicePT Web Site. Otherwise just leave it empty."
- name: freeleech
type: checkbox
label: Search freeleech only
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"]
captcha:
type: image
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
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:
- path: torrents.php
inputs:
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
search: "{{ .Keywords }}"
# 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 (unused)
search_area: 0
# 0 AND, 1 OR, 2 exact
search_mode: 0
sort: "{{ .Config.sort }}"
type: "{{ .Config.type }}"
rows:
selector: table.torrents > tbody > tr:has(a[href^="details.php?id="])
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title_default:
selector: a[href^="details.php?id="]
title_optional:
optional: true
selector: a[title][href^="details.php?id="]
attribute: title
title:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
date_elapsed:
# time type: time elapsed (default)
selector: td.rowfollow:nth-child(4) > 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_added:
# time added
selector: td.rowfollow:nth-child(4):not(:has(span))
optional: true
filters:
- name: append
args: " +08:00" # CST
- name: dateparse
args: "2006-01-0215:04:05 -07:00"
date:
text: "{{ if or .Result.date_elapsed .Result.date_added }}{{ or .Result.date_elapsed .Result.date_added }}{{ else }}now{{ end }}"
size:
selector: td.rowfollow:nth-child(5)
seeders:
selector: td.rowfollow:nth-child(6)
leechers:
selector: td.rowfollow:nth-child(7)
grabs:
selector: td.rowfollow:nth-child(8)
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
minimumratio:
text: 1.0
minimumseedtime:
# 1 day (as seconds = 24 x 60 x 60)
text: 86400
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.29 2022-10-13

View File

@@ -132,7 +132,7 @@ search:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img.nexus-lazy-load
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
@@ -191,4 +191,4 @@ search:
remove: a, img, span
description:
text: "{{ .Result.genre }}"
# NexusPHP 1.7.29 2022-10-13
# NexusPHP v1.7.29 2022-10-13

View File

@@ -131,6 +131,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
@@ -185,4 +188,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP v1.8.0 2023-01-05
# NexusPHP v1.8.0 2023-01-11

View File

@@ -138,4 +138,4 @@ search:
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP Standard v1.5 Beta 4
# Ourbits 1.1.0 (Based on NexusPHP Standard v1.5 Beta 4) 3f7f4b0 2023-02-01

View File

@@ -128,8 +128,8 @@ search:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img.pr5
attribute: src
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
@@ -187,4 +187,4 @@ search:
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.16
# NexusPHP v1.8.0 2023-01-16

View File

@@ -123,6 +123,10 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
# site does not have posters enabled. just in case a future update.
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]

View File

@@ -136,8 +136,8 @@ search:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-orig]
attribute: data-orig
selector: img[data-src]
attribute: data-src
imdbid:
selector: a[href$="&search_area=4"]
attribute: href

View File

@@ -139,4 +139,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP
# NexusPHP v3.1 2021-07-05

View File

@@ -0,0 +1,192 @@
---
id: sharkpt
name: SharkPT
description: "SharkPT is a CHINESE Private Torrent Tracker for MOVIES / TV / GENERAL"
language: zh-CN
type: private
encoding: UTF-8
links:
- https://sharkpt.net/
caps:
categorymappings:
- {id: 401, cat: Movies, desc: "Movies/电影"}
- {id: 404, cat: TV/Documentary, desc: "Documentaries/纪录片"}
- {id: 405, cat: TV/Anime, desc: "Animations/动漫"}
- {id: 402, cat: TV, desc: "TV Series/电视连续剧"}
- {id: 403, cat: TV, desc: "TV Shows/综艺"}
- {id: 406, cat: Audio/Video, desc: "MusicVideo/音乐视频"}
- {id: 407, cat: TV/Sport, desc: "Sports/体育"}
- {id: 409, cat: Other, desc: "Misc/其他"}
- {id: 408, cat: Audio, desc: "Music/HQ Audio"}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, doubanid]
movie-search: [q, imdbid, doubanid]
music-search: [q]
settings:
- name: username
type: text
label: Username
- name: password
type: password
label: Password
- name: 2facode
type: text
label: 2FA code
- name: info_2fa
type: info
label: "About 2FA code"
default: "Only fill in the <b>2FA code</b> box if you have enabled <b>2FA</b> on the SharkPT Web Site. Otherwise just leave it empty."
- name: freeleech
type: checkbox
label: Search freeleech only
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"]
captcha:
type: image
selector: img[alt="CAPTCHA"]
input: imagestring
inputs:
secret: ""
username: "{{ .Config.username }}"
password: "{{ .Config.password }}"
two_step_code: "{{ .Config.2facode }}"
logout: ""
securelogin: ""
ssl: yes
trackerssl: yes
error:
- selector: td.embedded:has(h2:contains("失败"))
test:
path: index.php
selector: a[href="mybonus.php"]
search:
paths:
- path: torrents.php
inputs:
$raw: "{{ range .Categories }}cat{{.}}=1&{{end}}"
search: "{{ if .Query.IMDBID }}{{ .Query.IMDBID }}{{ else }}{{ end }}{{ if or .Query.IMDBID .Query.DoubanID }} {{ else }}{{ .Keywords }}{{ end }}{{ if .Query.DoubanID }}{{ .Query.DoubanID }}{{ else }}{{ 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 (4 does not appear to work)
search_area: "{{ if or .Query.IMDBID .Query.DoubanID }}1{{ 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(a[href^="details.php?id="])
fields:
category:
selector: a[href^="?cat="]
attribute: href
filters:
- name: querystring
args: cat
title_default:
selector: a[href^="details.php?id="]
title_optional:
optional: true
selector: a[title][href^="details.php?id="]
attribute: title
title:
text: "{{ if .Result.title_optional }}{{ .Result.title_optional }}{{ else }}{{ .Result.title_default }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
imdbid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="imdb.com/title/tt"]
attribute: href
doubanid:
# site currently only has a badge and rating, the id is not present. just in case a future update.
selector: a[href*="movie.douban.com/subject/"]
attribute: href
date_elapsed:
# time type: time elapsed (default)
selector: td.rowfollow:nth-child(4) > 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_added:
# time added
selector: td.rowfollow:nth-child(4):not(:has(span))
optional: true
filters:
- name: append
args: " +08:00" # CST
- name: dateparse
args: "2006-01-0215:04:05 -07:00"
date:
text: "{{ if or .Result.date_elapsed .Result.date_added }}{{ or .Result.date_elapsed .Result.date_added }}{{ else }}now{{ end }}"
size:
selector: td.rowfollow:nth-child(5)
seeders:
selector: td.rowfollow:nth-child(6)
leechers:
selector: td.rowfollow:nth-child(7)
grabs:
selector: td.rowfollow:nth-child(8)
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
minimumseedtime:
# 7 day (as seconds = 7 x 24 x 60 x 60)
text: 604800
description:
selector: td.rowfollow:nth-child(2)
remove: a, img
# NexusPHP v1.7.33 2022-12-19

View File

@@ -23,6 +23,7 @@ caps:
- {id: 90, cat: Movies/3D, desc: "Movies-3-D"}
- {id: 91, cat: Movies, desc: "Movies-Packs"}
- {id: 108, cat: Movies, desc: "Movies Remux"}
- {id: 32, cat: Movies, desc: "Movies"}
- {id: 14, cat: Audio/Other, desc: "Alben / Sampler / Singles"}
- {id: 36, cat: Audio/Audiobook, desc: "Hörbuch"}
- {id: 71, cat: Audio/Other, desc: "Soundtracks"}

View File

@@ -175,7 +175,7 @@ search:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img.nexus-lazy-load
selector: img[data-src]
attribute: data-src
date_elapsed:
# time type: time elapsed (default)
@@ -222,4 +222,4 @@ search:
"*": 1
minimumratio:
text: 0.81
# NexusPHP Custom 1.7.24 2022-09-11
# NexusPHP v1.7.33 2022-12-19 (custom)

View File

@@ -184,4 +184,4 @@ search:
minimumseedtime:
# 3 days (as seconds = 3 x 24 x 60 x 60)
text: 259200
# NexusPHP Standard v1.5 Beta 4
# Engine n/a

View File

@@ -196,6 +196,9 @@ search:
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img[data-src]
attribute: data-src
date_elapsed:
# time type: time elapsed (default)
selector: td:nth-child(4) > span[title]
@@ -241,4 +244,4 @@ search:
"*": 1
minimumratio:
text: 1.0
# NexusPHP v1.7.30 2022-10-21
# NexusPHP v1.7.30 2022-11-05

View File

@@ -0,0 +1,158 @@
---
id: thedarkcommunity
name: TheDarkCommunity (API)
description: "TheDarkCommunity (TDC) is a Private Torrent Tracker for MOVIES / TV"
language: en-US
type: private
encoding: UTF-8
links:
- https://thedarkcommunity.cc/
caps:
categorymappings:
- {id: 1, cat: Movies, desc: "Movies"}
- {id: 2, cat: TV, desc: "TV"}
modes:
search: [q]
tv-search: [q, season, ep, imdbid, tvdbid, tmdbid]
movie-search: [q, imdbid, tmdbid]
settings:
- name: apikey
type: text
label: APIKey
- name: info_key
type: info
label: About your API key
default: "Find or Generate a new API Token by accessing your <a href=\"https://thedarkcommunity.cc//\" target =_blank>TheDarkCommunity</a> account <i>My Security</i> page and clicking on the <b>API Token</b> tab."
- 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: /api/torrents
method: get
inputs:
api_token: "{{ .Config.apikey }}"
error:
- selector: a[href*="/login"]
message:
text: "The API key was not accepted by {{ .Config.sitelink }}."
search:
paths:
# https://hdinnovations.github.io/UNIT3D-Community-Edition-Docs/api_endpoints.html
# https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/app/Http/Controllers/API/TorrentController.php
- path: "/api/torrents/filter"
response:
type: json
inputs:
# if we have an id based search, add Season and Episode as query in name for UNIT3D < v6. Else pass S/E Params for UNIT3D >= v6
api_token: "{{ .Config.apikey }}"
name: "{{ .Keywords }}"
$raw: "{{ if .Query.Season }}&seasonNumber={{ .Query.Season }}{{ else }}{{ end }}{{ if .Query.Ep }}&episodeNumber={{ .Query.Ep }}{{ else }}{{ end }}{{ if .Query.TMDBID }}&tmdbId={{ .Query.TMDBID }}{{ else }}{{ end }}{{ if .Query.IMDBIDShort }}&imdbId={{ .Query.IMDBIDShort }}{{ else }}{{ end }}{{ if .Query.TVDBID }}&tvdbId={{ .Query.TVDBID }}{{ else }}{{ end }}{{ range .Categories }}&categories[]={{.}}{{end}}{{ if .Config.freeleech }}&free[]=100{{ else }}{{ end }}"
sortField: "{{ .Config.sort }}"
sortDirection: "{{ .Config.type }}"
perPage: 100
page: 1
keywordsfilters:
- name: re_replace
args: ["\\.", " "]
rows:
selector: data
attribute: attributes
count:
selector: meta.total
fields:
category:
selector: category_id
title:
selector: name
details:
selector: details_link
download:
selector: download_link
infohash:
selector: info_hash
poster:
selector: meta.poster
filters:
- name: replace
args: ["https://via.placeholder.com/90x135", ""]
imdbid:
selector: imdb_id
tmdbid:
selector: tmdb_id
tvdbid:
selector: tvdb_id
genre:
selector: meta.genres
filters:
- name: re_replace
args: ["(?i)(Science Fiction)", "Science_Fiction"]
- name: re_replace
args: ["(?i)(TV Movie)", "TV_Movie"]
- name: replace
args: [" & ", "_&_"]
description:
text: "{{ .Result.genre }}"
files:
selector: num_file
seeders:
selector: seeders
leechers:
selector: leechers
grabs:
selector: times_completed
date:
# "created_at": "2021-10-18T00:34:50.000000Z" is returned by Newtonsoft.Json.Linq as 18/10/2021 00:34:50
selector: created_at
filters:
- name: append
args: " +00:00" # GMT
- name: dateparse
args: "01/02/2006 15:04:05 -07:00"
size:
selector: size
downloadvolumefactor:
# api returns 0%, 25%, 50%, 75%, 100%
selector: freeleech
case:
0%: 1 # not free
25%: 0.75
50%: 0.5
75%: 0.25
100%: 0 # freeleech
"*": 0 # catch errors
uploadvolumefactor:
# api returns 0=false, 1=true
selector: double_upload
case:
0: 1 # normal
1: 2 # double
minimumseedtime:
# 7 day (as seconds = 7 x 24 x 60 x 60)
text: 604800
# json UNIT3D 6.5.0

View File

@@ -172,4 +172,4 @@ search:
description:
selector: td:nth-child(2)
remove: a, img
# NexusPHP Standard v1.5 Beta 4
# NexusPHP Standard v1.5 Beta 4

View File

@@ -124,9 +124,6 @@ login:
search:
paths:
- path: /
keywordsfilters:
- name: re_replace
args: ["[^a-zA-Z0-9]+", "%25"]
inputs:
p: torrents
pid: 32
@@ -142,6 +139,8 @@ search:
rows:
selector: "table#torrents_table_classic > tbody > tr:has(td.torrent_name){{ if .Config.freeleech }}:has(img[title=\"FREE!\"]){{ else }}{{ end }}"
filters:
- name: andmatch
fields:
category:

View File

@@ -46,6 +46,7 @@ caps:
- {id: Séries, cat: TV, desc: "TV"}
- {id: Musiques, cat: Audio, desc: "Music"}
- {id: Ebooks, cat: Books, desc: "Books"}
- {id: Livres, cat: Books, desc: "Livres"}
- {id: Logiciels, cat: PC, desc: "Software"}
- {id: Jeux-PC, cat: PC/Games, desc: "PC Games"}
- {id: Jeux-Consoles, cat: Console/XBox 360, desc: "Console Games"}

View File

@@ -39,6 +39,7 @@ caps:
- {id: Séries, cat: TV, desc: "TV"}
- {id: Musiques, cat: Audio, desc: "Music"}
- {id: Ebooks, cat: Books, desc: "Books"}
- {id: Livres, cat: Books, desc: "Livres"}
- {id: Logiciels, cat: PC, desc: "Software"}
- {id: Jeux-PC, cat: PC/Games, desc: "PC Games"}
- {id: Jeux-Consoles, cat: Console/XBox 360, desc: "Console Games"}

View File

@@ -83,6 +83,10 @@ caps:
book-search: [q]
settings:
- name: excludeads
type: checkbox
label: Exclude results which include advertisements
default: false
- name: sort
type: select
label: Sort requested from site
@@ -127,9 +131,13 @@ search:
order: "{{ .Config.type }}"
rows:
selector: div.tgxtable > div:has(div[class^="tgxtablecell shrink"])
selector: "div.tgxtable > div:has(div[class^=\"tgxtablecell shrink\"]){{ if .Config.excludeads }}:not(:has(i.fab.fa-adversal)){{ else }}{{ end }}"
fields:
_ads:
optional: true
selector: i.fab.fa-adversal
attribute: title
category:
selector: div a[href^="/torrents.php?cat="]
attribute: href
@@ -152,7 +160,7 @@ search:
- name: re_replace
args: ["-", " "]
title:
text: "{{ if or .Result.title_full .Result.title_text }}{{ or .Result.title_full .Result.title_text }}{{ else }}{{ .Result.href }}{{ end }}"
text: "{{ if or .Result.title_full .Result.title_text }}{{ or .Result.title_full .Result.title_text }}{{ else }}{{ .Result.href }}{{ end }}{{ if .Result._ads }} (Ads included!){{ else }}{{ end }}"
details:
selector: div a[href^="/torrent/"]
attribute: href

View File

@@ -7,10 +7,8 @@ type: public
encoding: UTF-8
followredirect: true
links:
- https://torrentqq240.com/
- https://torrentqq242.com/
legacylinks:
- https://torrentqq225.com/
- https://torrentqq226.com/
- https://torrentqq227.com/
- https://torrentqq228.com/
- https://torrentqq229.com/
@@ -24,6 +22,8 @@ legacylinks:
- https://torrentqq237.com/
- https://torrentqq238.com/
- https://torrentqq239.com/
- https://torrentqq240.com/
- https://torrentqq241.com/
caps:
categorymappings:

View File

@@ -7,10 +7,8 @@ type: public
encoding: UTF-8
followredirect: true
links:
- https://torrentsir89.com/
- https://torrentsir90.com/
legacylinks:
- http://torrentsir78.com/
- https://torrentsir78.com/
- http://torrentsir79.com/
- https://torrentsir79.com/
- http://torrentsir80.com/
@@ -30,6 +28,8 @@ legacylinks:
- http://torrentsir88.com/
- https://torrentsir88.com/
- http://torrentsir89.com/
- https://torrentsir89.com/
- http://torrentsir90.com/
caps:
categorymappings:

View File

@@ -7,11 +7,10 @@ type: public
encoding: UTF-8
followredirect: true
links:
- https://viewtorrent2.com/
- https://viewtorrent4.com/
legacylinks:
- https://torrentview.net/
- https://torrentview.co/
- https://torrentview47.com/
- https://torrentview49.com/
- https://torrentview50.com/
- https://torrentview52.com/
@@ -25,6 +24,7 @@ legacylinks:
- https://torrentview67.com/
- https://torrentview68.com/
- https://viewtorrent1.com/
- https://viewtorrent2.com/
caps:
categorymappings:

View File

@@ -7,9 +7,8 @@ type: public
encoding: UTF-8
followredirect: true
links:
- https://torrentwiz51.com/
- https://torrentwiz52.com/
legacylinks:
- https://torrentwiz35.com/
- https://torrentwiz36.com/
- https://torrentwiz37.com/
- https://torrentwiz38.com/
@@ -24,6 +23,7 @@ legacylinks:
- https://torrentwiz48.com/
- https://torrentwiz49.com/
- https://torrentwiz50.com/
- https://torrentwiz51.com/
caps:
categorymappings:

View File

@@ -16,6 +16,22 @@ caps:
search: [q]
music-search: [q, artist]
settings:
- name: username
type: text
label: Username
- name: password
type: password
label: Password
- name: freeleech
type: checkbox
label: Filter 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.
login:
path: takelogin.php
method: post
@@ -32,6 +48,7 @@ search:
- path: browse.php
inputs:
search: "{{ if .Query.Artist }}{{ .Query.Artist }}{{ else }}{{ .Keywords }}{{ end }}"
$raw: "{{ if .Config.freeleech }}&includeFL=on{{ else }}{{ end }}"
rows:
selector: table.mainouter table > tbody > tr:has(a[href^="details.php?id="])
@@ -47,6 +64,9 @@ search:
details:
selector: a[href^="details.php?id="]
attribute: href
filters:
- name: replace
args: ["&hit=1", ""] # avoid redirect
date:
selector: td:nth-child(6)
# auto adjusted by site account profile
@@ -64,7 +84,9 @@ search:
size:
selector: td:nth-child(7)
downloadvolumefactor:
text: 1
case:
"span:contains(\"FREELEECH\")": 0
"*": 1
uploadvolumefactor:
text: 1
# engine tbd

View File

@@ -0,0 +1,175 @@
---
id: unleashthecartoons
name: UnleashTheCartoons
description: "UnleashTheCartoons is a ROMANIAN Private Torrent Tracker for ANIMATED MOVIES / TV"
language: ro-RO
type: private
encoding: UTF-8
links:
- https://unleashthecartoons.world/
caps:
categorymappings:
- {id: 1, cat: Movies, desc: "Movies"}
- {id: 2, cat: TV, desc: "TV Series"}
- {id: 3, cat: TV/Anime, desc: "Anime"}
- {id: 4, cat: Books, desc: "Ebooks"}
modes:
search: [q]
tv-search: [q, season, ep]
movie-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: multilang
type: checkbox
label: Replace MULTi by another language in release name
default: false
- name: multilanguage
type: select
label: Replace MULTi by this language
default: Romanian
options:
Romanian: Romanian
MULTi Romanian: MULTi Romanian
English: English
MULTi English: MULTi English
- 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 }}"
returnto: /
error:
- selector: div.myFrame-caption:contains("Error")
message:
selector: div.myFrame-content
- selector: div.myFrame-caption:contains("Access denied")
message:
selector: div.myFrame-content
test:
path: /
selector: a[href="account-logout.php"]
search:
paths:
# https://unleashthecartoons.world/torrents-search.php?search=&cat=0&incldead=1&freeleech=0&inclexternal=0&lang=0&sort=id&order=desc
- path: torrents-search.php
inputs:
$raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
search: "{{ .Keywords }}"
# 0 active, 1 incldead, 2 onlydead
incldead: 1
# 0 all, 1 nofree, 2 onlyfree
freeleech: "{{ if .Config.freeleech }}2{{ else }}0{{ end }}"
# 0 Local/External, 1 Local Only, 2 External Only
inclexternal: 0
# 0 all, 1 Romanian, 2 English, 3 Romanian/English
lang: 0
sort: "{{ .Config.sort }}"
order: "{{ .Config.type }}"
# does not support imdbid searches, does not return imdb link in results
keywordsfilters:
- name: re_replace
args: ["\\.", " "]
- name: re_replace
args: ["\\s+", " "]
- name: re_replace
args: ["(\\w+)", "+$1"] # prepend + to each word
rows:
selector: table.ttable_headinner tr.t-row
fields:
category:
selector: a[href*="cat="]
attribute: href
filters:
- name: querystring
args: cat
_lang:
optional: true
selector: img[src*="images/languages"]
attribute: alt
filters:
- name: replace
args: ["Ro/Eng", "MULTi"]
title_phase1:
selector: a[href^="torrents-details.php?id="]
filters:
- name: append
args: "{{ if .Result._lang }} ({{ .Result._lang }}){{ else }}{{ end }}"
title_multilang:
text: "{{ .Result.title_phase1 }}"
filters:
- name: re_replace
args: ["(?i)(\\bmulti\\b)", "{{ .Config.multilanguage }}"]
title:
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
details:
selector: a[href^="torrents-details.php?id="]
attribute: href
filters:
- name: replace
args: ["&hit=1", ""] # avoid redirect
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: a[onMouseover]
attribute: onMouseover
filters:
- name: regexp
args: src=(.+?)>
- name: replace
args: ["images/nocover.png", ""]
size:
selector: td:nth-last-child(7)
date:
selector: td:nth-last-child(3)
seeders:
selector: td:nth-last-child(6)
leechers:
selector: td:nth-last-child(5)
downloadvolumefactor:
case:
img[src="images/free.png"]: 0
"*": 1
uploadvolumefactor:
text: 1
minimumratio:
text: 1.0
minimumseedtime:
# 2 days (as seconds = 2 x 24 x 60 x 60)
text: 172800
# TorrentTrader

View File

@@ -0,0 +1,154 @@
---
id: vtorrent
name: vTorrent
description: "vTorrent is a POLISH Semi-Private Torrent Tracker for MOVIES / TV / GENERAL"
language: pl-PL
type: semi-private
encoding: UTF-8
links:
- https://vtorrent.pl/
caps:
categorymappings:
- {id: 695, cat: Movies, desc: "FILMY"}
- {id: 94, cat: Movies/HD, desc: "FILMY RMVB"}
- {id: 124, cat: Movies/SD, desc: "FILMY (S)VCD"}
- {id: 64, cat: Movies/SD, desc: "FILMY Divx / Xvid"}
- {id: 81, cat: Movies/DVD, desc: "FILMY DVD-R"}
- {id: 109, cat: Movies/HD, desc: "FILMY x264 / HDTV"}
- {id: 697, cat: Movies/UHD, desc: "FILMY 4K"}
- {id: 696, cat: Movies/3D, desc: "FILMY 3D"}
- {id: 230, cat: TV, desc: "TV / Seriale"}
- {id: 307, cat: TV/Anime, desc: "Anime"}
- {id: 698, cat: PC/Games, desc: "GRY"}
- {id: 155, cat: PC/Games, desc: "GRY Gry PC"}
- {id: 173, cat: Console, desc: "GRY Gry Konsole"}
- {id: 704, cat: Other, desc: "GRY Poradniki, spolszczenia i inne"}
- {id: 699, cat: PC, desc: "PROGRAMY"}
- {id: 138, cat: PC/0day, desc: "PROGRAMY Windows"}
- {id: 288, cat: PC/Mac, desc: "PROGRAMY Linux / Mac"}
- {id: 700, cat: Audio, desc: "MUZYKA"}
- {id: 187, cat: Audio, desc: "MUZYKA Albumy i składanki"}
- {id: 701, cat: Audio/Video, desc: "MUZYKA Koncerty"}
- {id: 702, cat: TV/Documentary, desc: "DOKUMENTY"}
- {id: 249, cat: Books/EBook, desc: "DOKUMENTY Książki"}
- {id: 283, cat: Audio/Audiobook, desc: "DOKUMENTY Audiobooki"}
- {id: 251, cat: Books/Mags, desc: "DOKUMENTY Czasopisma"}
- {id: 284, cat: Books/Comics, desc: "DOKUMENTY Komiksy"}
- {id: 703, cat: Other, desc: "POZOSTAŁE"}
- {id: 310, cat: Other, desc: "POZOSTAŁE Dla Dzieci"}
- {id: 294, cat: TV/Sport, desc: "POZOSTAŁE Sport"}
- {id: 316, cat: PC/Mobile-Other, desc: "POZOSTAŁE GSM / PDA"}
- {id: 333, cat: Other, desc: "POZOSTAŁE Tapety"}
- {id: 325, cat: Other/Misc, desc: "POZOSTAŁE Różne"}
- {id: 334, cat: XXX, desc: "EROTYKA"}
- {id: 705, cat: XXX/x264, desc: "EROTYKA Filmy XXX"}
- {id: 706, cat: XXX/ImageSet, desc: "EROTYKA Zdjęcia XXX"}
- {id: 340, cat: XXX/Other, desc: "EROTYKA Czasopisma XXX"}
- {id: 342, cat: XXX/Other, desc: "EROTYKA Gry XXX"}
- {id: 308, cat: XXX/Other, desc: "EROTYKA Hentai"}
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: multilang
type: checkbox
label: Replace MULTI by another language in release name
default: false
- name: multilanguage
type: select
label: Replace MULTI by this language
default: POLISH
options:
POLISH: POLISH
MULTI.POLISH: MULTI.POLISH
- name: info_tpp
type: info
label: Results Per Page
default: For best results, change the <b>Torrentów na stronę:</b> setting to <b>100</b> on your account profile.
login:
path: login.php
method: form
form: form
inputs:
uid: "{{ .Config.username }}"
pwd: "{{ .Config.password }}"
keeplogged: 1
error:
- selector: font[color="#FF0000"]
test:
path: index.php
selector: a[href^="logout.php?check_hash="]
search:
paths:
# https://vtorrent.pl/torrents.php?erotyka=1&page=0
- path: torrents.php
inputs:
# does not support multi category selection. so using default for all
# $raw: "{{ range .Categories }}c{{.}}=1&{{end}}"
category: 0
search: "{{ .Keywords }}"
erotyka: 1
page: 0
# does not support sorting results, or imdbid searching, or have imdb in results
rows:
selector: table.lista[width="100%"]:not(table[align]) > tbody > tr:has(a[href^="details.php?id="])
fields:
category:
selector: a[href^="torrents.php?category="]
attribute: href
filters:
- name: querystring
args: category
title_phase1:
selector: a[href^="details.php?id="]
title_multilang:
selector: a[href^="details.php?id="]
filters:
- name: re_replace
args: ["(?i)(\\.multi\\.)", ".{{ .Config.multilanguage }}."]
- name: re_replace
args: ["(?i)(\\.pl\\.)", ".POLISH."]
title:
text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
details:
selector: a[href^="details.php?id="]
attribute: href
download:
selector: a[href^="download.php?id="]
attribute: href
poster:
selector: img
attribute: src
date:
selector: td:nth-child(6)
# auto adjusted by site account profile
filters:
- name: dateparse
args: "02/01/2006 15:04:05"
size:
selector: td:nth-child(7)
seeders:
selector: td:nth-child(9)
leechers:
selector: td:nth-child(10)
downloadvolumefactor:
text: 0
uploadvolumefactor:
text: 1
# engine n/a

View File

@@ -37,7 +37,7 @@ namespace Jackett.Common.Indexers
private const string SearchUrl = "buscar/";
public override string[] AlternativeSiteLinks { get; protected set; } = {
"https://dontorrent.surf/",
"https://dontorrent.how/",
"https://todotorrents.net/",
"https://tomadivx.net/",
"https://seriesblanco.one/",
@@ -60,7 +60,8 @@ namespace Jackett.Common.Indexers
"https://dontorrent.mba/",
"https://dontorrent.army/",
"https://dontorrent.blue/",
"https://dontorrent.beer/"
"https://dontorrent.beer/",
"https://dontorrent.surf/"
};
private static Dictionary<string, string> CategoriesMap => new Dictionary<string, string>
@@ -78,7 +79,7 @@ namespace Jackett.Common.Indexers
: base(id: "dontorrent",
name: "DonTorrent",
description: "DonTorrent is a SPANISH public tracker for MOVIES / TV / GENERAL",
link: "https://dontorrent.surf/",
link: "https://dontorrent.how/",
caps: new TorznabCapabilities
{
TvSearchParams = new List<TvSearchParam>
@@ -105,6 +106,9 @@ namespace Jackett.Common.Indexers
Language = "es-ES";
Type = "public";
// avoid CLoudflare too many requests limiter
webclient.requestDelay = 2.1;
var matchWords = new BoolConfigurationItem("Match words in title") { Value = true };
configData.AddDynamic("MatchWords", matchWords);

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Jackett.Common.Models;
@@ -17,11 +18,15 @@ namespace Jackett.Common.Indexers
[ExcludeFromCodeCoverage]
public class FileList : BaseWebIndexer
{
public override string[] AlternativeSiteLinks { get; protected set; } = {
"https://filelist.io/",
"https://flro.org/"
};
public override string[] LegacySiteLinks { get; protected set; } =
{
"http://filelist.ro/",
"https://filelist.ro/",
"https://flro.org/",
"http://filelist.ro/",
"http://flro.org/"
};
@@ -128,35 +133,42 @@ namespace Jackett.Common.Indexers
try
{
var json = JArray.Parse(response);
foreach (var row in json)
{
var detailsUri = new Uri(DetailsUrl + "?id=" + (string)row["id"]);
var seeders = (int)row["seeders"];
var peers = seeders + (int)row["leechers"];
var publishDate = DateTimeUtil.FromFuzzyTime((string)row["upload_date"] + " +0200");
var downloadVolumeFactor = (int)row["freeleech"] == 1 ? 0 : 1;
var uploadVolumeFactor = (int)row["doubleup"] == 1 ? 2 : 1;
var imdbId = ((JObject)row).ContainsKey("imdb") ? ParseUtil.GetImdbID((string)row["imdb"]) : null;
var link = new Uri((string)row["download_link"]);
var isFreeleech = row.Value<bool>("freeleech");
// skip non-freeleech results when freeleech only is set
if (configData.Freeleech.Value && !isFreeleech)
continue;
var detailsUri = new Uri(DetailsUrl + "?id=" + row.Value<string>("id"));
var seeders = row.Value<int>("seeders");
var peers = seeders + row.Value<int>("leechers");
var publishDate = DateTime.Parse(row.Value<string>("upload_date") + " +0200", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
var downloadVolumeFactor = isFreeleech ? 0 : 1;
var uploadVolumeFactor = row.Value<bool>("doubleup") ? 2 : 1;
var imdbId = ((JObject)row).ContainsKey("imdb") ? ParseUtil.GetImdbID(row.Value<string>("imdb")) : null;
var link = new Uri(row.Value<string>("download_link"));
var release = new ReleaseInfo
{
Title = (string)row["name"],
Guid = detailsUri,
Details = detailsUri,
Link = link,
Category = MapTrackerCatDescToNewznab((string)row["category"]),
Size = (long)row["size"],
Files = (long)row["files"],
Grabs = (long)row["times_completed"],
Title = row.Value<string>("name").Trim(),
Category = MapTrackerCatDescToNewznab(row.Value<string>("category")),
Size = row.Value<long>("size"),
Files = row.Value<long>("files"),
Grabs = row.Value<long>("times_completed"),
Seeders = seeders,
Peers = peers,
MinimumRatio = 1,
MinimumSeedTime = 172800, //48 hours
Imdb = imdbId,
PublishDate = publishDate,
DownloadVolumeFactor = downloadVolumeFactor,
UploadVolumeFactor = uploadVolumeFactor,
Guid = detailsUri,
Imdb = imdbId
MinimumRatio = 1,
MinimumSeedTime = 172800 // 48 hours
};
releases.Add(release);
@@ -175,29 +187,33 @@ namespace Jackett.Common.Indexers
private async Task<string> CallProviderAsync(TorznabQuery query)
{
var searchUrl = ApiUrl;
var searchString = query.GetQueryString();
var cat = string.Join(",", MapTorznabCapsToTrackers(query));
var searchString = query.GetQueryString().Trim();
var queryCollection = new NameValueCollection
{
{"category", cat}
{"category", string.Join(",", MapTorznabCapsToTrackers(query))}
};
if (configData.Freeleech.Value)
queryCollection.Set("freeleech", "1");
if (query.IsImdbQuery)
{
queryCollection.Add("type", "imdb");
queryCollection.Add("query", query.ImdbID);
queryCollection.Add("action", "search-torrents");
queryCollection.Set("action", "search-torrents");
queryCollection.Set("type", "imdb");
queryCollection.Set("query", query.ImdbID);
}
else if (!string.IsNullOrWhiteSpace(searchString))
{
queryCollection.Add("type", "name");
queryCollection.Add("query", searchString);
queryCollection.Add("action", "search-torrents");
queryCollection.Set("action", "search-torrents");
queryCollection.Set("type", "name");
queryCollection.Set("query", searchString);
}
else
queryCollection.Add("action", "latest-torrents");
queryCollection.Set("action", "latest-torrents");
searchUrl += "?" + queryCollection.GetQueryString();
try
{
var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(configData.Username.Value + ":" + configData.Passkey.Value));
@@ -206,6 +222,7 @@ namespace Jackett.Common.Indexers
{"Authorization", "Basic " + auth}
};
var response = await RequestWithCookiesAsync(searchUrl, headers: headers);
return response.ContentString;
}
catch (Exception inner)

View File

@@ -80,6 +80,7 @@ namespace Jackett.Common.Indexers
var pairs = new Dictionary<string, string> {
{ "username", configData.Username.Value },
{ "password", configData.Password.Value },
{ "returnto", "" },
{ "login", "Login" }
};
@@ -107,11 +108,12 @@ namespace Jackett.Common.Indexers
* so ?search=reality+love+s04e19&s_title=1&s_tag=1
* will find Love Island S04E19 as well as My Kitchen Rules S12E02
* since the former has a match for Love in the title, and the latter has a tag for Reality-TV.
*
*
* FunFiles only has genres for movies and tv
*/
var ValidList = new List<string>() {
var validList = new List<string>
{
"action",
"adventure",
"animation",
@@ -147,25 +149,27 @@ namespace Jackett.Common.Indexers
var qc = new NameValueCollection
{
{"incldead", "1"},
{"showspam", "1"},
{"cat", MapTorznabCapsToTrackers(query).FirstIfSingleOrDefault("0")}
{ "cat", "0" },
{ "incldead", "1" },
{ "showspam", "1" },
{ "s_title", "1" }
};
var queryCats = MapTorznabCapsToTrackers(query);
queryCats.ForEach(cat => qc.Set($"c{cat}", "1"));
if (query.IsImdbQuery)
{
qc.Add("search", query.ImdbID);
qc.Add("s_desc", "1");
qc.Set("search", query.ImdbID);
qc.Set("s_desc", "1");
}
else
if (query.IsGenreQuery)
else if (query.IsGenreQuery)
{
qc.Add("search", query.Genre + " " + query.GetQueryString());
qc.Add("s_tag", "1");
qc.Add("s_title", "1");
qc.Set("search", query.Genre + " " + query.GetQueryString());
qc.Set("s_tag", "1");
}
else
qc.Add("search", query.GetQueryString());
qc.Set("search", query.GetQueryString());
var searchUrl = SearchUrl + "?" + qc.GetQueryString();
var results = await RequestWithCookiesAndRetryAsync(searchUrl);
@@ -176,64 +180,64 @@ namespace Jackett.Common.Indexers
results = await RequestWithCookiesAndRetryAsync(searchUrl);
}
char[] delimiters = { ',', ' ', '/', ')', '(', '.', ';', '[', ']', '"', '|', ':' };
try
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(results.ContentString);
var rows = dom.QuerySelectorAll("table[cellpadding=2] > tbody > tr:has(td.row3)");
var rows = dom.QuerySelectorAll("table.mainframe table[cellpadding=\"2\"] > tbody > tr:has(td.row3)");
foreach (var row in rows)
{
var qDownloadLink = row.QuerySelector("a[href^=\"download.php\"]");
if (qDownloadLink == null)
throw new Exception("Download links not found. Make sure you can download from the website.");
var link = new Uri(SiteLink + qDownloadLink.GetAttribute("href"));
var qDetailsLink = row.QuerySelector("a[href^=\"details.php?id=\"]");
var title = qDetailsLink.GetAttribute("title").Trim();
var details = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
var title = qDetailsLink?.GetAttribute("title")?.Trim();
var details = new Uri(SiteLink + qDetailsLink?.GetAttribute("href")?.Replace("&hit=1", ""));
var qCatLink = row.QuerySelector("a[href^=\"browse.php?cat=\"]");
var catStr = qCatLink.GetAttribute("href").Split('=')[1].Split('&')[0];
var categoryLink = row.QuerySelector("a[href^=\"browse.php?cat=\"]")?.GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "cat");
var files = ParseUtil.CoerceInt(row.Children[3].TextContent);
var publishDate = DateTimeUtil.FromTimeAgo(row.Children[5].TextContent);
var size = ReleaseInfo.GetBytes(row.Children[7].TextContent);
var grabs = ParseUtil.CoerceInt(row.Children[8].TextContent);
var seeders = ParseUtil.CoerceInt(row.Children[9].TextContent);
var leechers = ParseUtil.CoerceInt(row.Children[10].TextContent);
var ka = row.NextElementSibling.QuerySelector("table > tbody > tr:nth-child(3)");
var ulFactor = ParseUtil.CoerceDouble(ka.Children[0].TextContent.Replace("X", ""));
var dlFactor = ParseUtil.CoerceDouble(ka.Children[1].TextContent.Replace("X", ""));
ka = row.NextElementSibling.QuerySelector("span[style=\"float:left\"]");
var description = ka.TextContent;
var qGenres = description.ToLower().Replace(" & ", "_&_").Replace(" and ", "_and_");
char[] delimiters = { ',', ' ', '/', ')', '(', '.', ';', '[', ']', '"', '|', ':' };
var releaseGenres = ValidList.Intersect(qGenres.Split(delimiters, System.StringSplitOptions.RemoveEmptyEntries));
releaseGenres = releaseGenres.Select(x => x.Replace("_", " "));
var release = new ReleaseInfo
{
Title = title,
Details = details,
Link = link,
Guid = link,
Category = MapTrackerCatToNewznab(catStr),
PublishDate = publishDate,
Size = size,
Files = files,
Grabs = grabs,
Link = link,
Details = details,
Title = title,
Category = MapTrackerCatToNewznab(cat),
Size = ReleaseInfo.GetBytes(row.Children[7].TextContent),
Files = ParseUtil.CoerceInt(row.Children[3].TextContent),
Grabs = ParseUtil.CoerceInt(row.Children[8].TextContent),
Seeders = seeders,
Peers = leechers + seeders,
Description = description,
PublishDate = DateTimeUtil.FromTimeAgo(row.Children[5].TextContent),
DownloadVolumeFactor = 1,
UploadVolumeFactor = 1,
MinimumRatio = 1,
MinimumSeedTime = 172800, // 48 hours
DownloadVolumeFactor = dlFactor,
UploadVolumeFactor = ulFactor
MinimumSeedTime = 172800 // 48 hours
};
if (release.Genres == null)
release.Genres = new List<string>();
release.Genres = releaseGenres.ToList();
var nextRow = row.NextElementSibling;
if (nextRow != null)
{
var qStats = nextRow.QuerySelector("table > tbody > tr:nth-child(3)");
release.UploadVolumeFactor = ParseUtil.CoerceDouble(qStats?.Children[0].TextContent.Replace("X", ""));
release.DownloadVolumeFactor = ParseUtil.CoerceDouble(qStats?.Children[1].TextContent.Replace("X", ""));
release.Description = nextRow.QuerySelector("span[style=\"float:left\"]")?.TextContent.Trim();
var genres = release.Description.ToLower().Replace(" & ", "_&_").Replace(" and ", "_and_");
var releaseGenres = validList.Intersect(genres.Split(delimiters, StringSplitOptions.RemoveEmptyEntries));
release.Genres = releaseGenres.Select(x => x.Trim().Replace("_", " ")).ToList();
}
releases.Add(release);
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -11,10 +12,10 @@ using Jackett.Common.Models;
using Jackett.Common.Models.IndexerConfig;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
using Jackett.Common.Utils.Clients;
using Newtonsoft.Json.Linq;
using NLog;
using static Jackett.Common.Models.IndexerConfig.ConfigurationData;
using WebClient = Jackett.Common.Utils.Clients.WebClient;
namespace Jackett.Common.Indexers
{
@@ -22,7 +23,7 @@ namespace Jackett.Common.Indexers
public class HDSpace : BaseWebIndexer
{
private string LoginUrl => SiteLink + "index.php?page=login";
private string SearchUrl => SiteLink + "index.php?page=torrents&";
private string SearchUrl => SiteLink + "index.php?page=torrents";
private new ConfigurationDataBasicLogin configData => (ConfigurationDataBasicLogin)base.configData;
@@ -121,17 +122,16 @@ namespace Jackett.Common.Indexers
if (query.IsImdbQuery)
{
queryCollection.Add("options", "2");
queryCollection.Add("search", query.ImdbIDShort);
queryCollection.Set("options", "2");
queryCollection.Set("search", query.ImdbIDShort);
}
else
{
queryCollection.Add("options", "0");
queryCollection.Add("search", query.GetQueryString());
queryCollection.Set("options", "0");
queryCollection.Set("search", query.GetQueryString().Replace(".", " "));
}
// remove . as not used in titles
var response = await RequestWithCookiesAndRetryAsync(SearchUrl + queryCollection.GetQueryString().Replace(".", " "));
var response = await RequestWithCookiesAndRetryAsync($"{SearchUrl}&{queryCollection.GetQueryString()}");
try
{
@@ -156,6 +156,11 @@ namespace Jackett.Common.Indexers
release.Title = qLink.TextContent.Trim();
release.Details = new Uri(SiteLink + qLink.GetAttribute("href"));
release.Guid = release.Details;
release.Link = new Uri(SiteLink + row.Children[3].FirstElementChild.GetAttribute("href"));
var torrentTitle = ParseUtil.GetArgumentFromQueryString(release.Link.ToString(), "f")?.Replace(".torrent", "").Trim();
if (!string.IsNullOrWhiteSpace(torrentTitle))
release.Title = WebUtility.HtmlDecode(torrentTitle);
var qGenres = row.QuerySelector("span[style=\"color: #000000 \"]");
var description = "";
@@ -166,9 +171,6 @@ namespace Jackett.Common.Indexers
if (imdbLink != null)
release.Imdb = ParseUtil.GetImdbID(imdbLink.GetAttribute("href").Split('/').Last());
var qDownload = row.Children[3].FirstElementChild;
release.Link = new Uri(SiteLink + qDownload.GetAttribute("href"));
var dateStr = row.Children[4].TextContent.Trim();
//"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23"
release.PublishDate = DateTimeUtil.FromUnknown(dateStr);
@@ -188,8 +190,9 @@ namespace Jackett.Common.Indexers
else
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 1;
var qCat = row.QuerySelector("a[href^=\"index.php?page=torrents&category=\"]");
var cat = qCat.GetAttribute("href").Split('=')[2];
var categoryLink = row.QuerySelector("a[href^=\"index.php?page=torrents&category=\"]").GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "category");
release.Category = MapTrackerCatToNewznab(cat);
release.Description = description;

View File

@@ -170,15 +170,14 @@ namespace Jackett.Common.Indexers
var poster = posterMatch.Success ? new Uri(SiteLink + posterMatch.Groups[1].Value.Replace("\\", "/")) : null;
var link = new Uri(SiteLink + row.Children[4].FirstElementChild.GetAttribute("href"));
var description = row.Children[2].QuerySelector("span").TextContent;
var description = row.Children[2].QuerySelector("span")?.TextContent.Trim();
var size = ReleaseInfo.GetBytes(row.Children[7].TextContent);
var dateTag = row.Children[6].FirstElementChild;
var dateString = string.Join(" ", dateTag.Attributes.Select(attr => attr.Name));
var publishDate = DateTime.ParseExact(dateString, "dd MMM yyyy HH:mm:ss zz00", CultureInfo.InvariantCulture).ToLocalTime();
var dateAdded = string.Join(" ", row.Children[6].FirstElementChild.Attributes.Select(a => a.Name).Take(4));
var publishDate = DateTime.ParseExact(dateAdded, "dd MMM yyyy HH:mm:ss", CultureInfo.InvariantCulture);
var catStr = row.FirstElementChild.FirstElementChild.GetAttribute("href").Split('=')[1];
var cat = MapTrackerCatToNewznab(catStr);
var categoryLink = row.FirstElementChild.FirstElementChild.GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "category");
// Sometimes the uploader column is missing, so seeders, leechers, and grabs may be at a different index.
// There's room for improvement, but this works for now.
@@ -230,7 +229,7 @@ namespace Jackett.Common.Indexers
Guid = details,
Link = link,
PublishDate = publishDate,
Category = cat,
Category = MapTrackerCatToNewznab(cat),
Description = description,
Poster = poster,
Imdb = imdb,

View File

@@ -5,6 +5,7 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using Jackett.Common.Models;
@@ -22,6 +23,7 @@ namespace Jackett.Common.Indexers
private string BrowsePage => SiteLink + "browse.php";
private string LoginUrl => SiteLink + "takelogin.php";
private string QueryString => "?do=search&keywords={0}&search_type=t_name&category=0&include_dead_torrents=no";
private readonly Regex _dateMatchRegex = new Regex(@"\d{4}-\d{2}-\d{2} \d{2}:\d{2} [AaPp][Mm]", RegexOptions.Compiled);
public override string[] LegacySiteLinks { get; protected set; } = {
"http://immortalseed.me/"
@@ -131,12 +133,9 @@ namespace Jackett.Common.Indexers
{
var parser = new HtmlParser();
var document = parser.ParseDocument(response.ContentString);
var messageEl = document.QuerySelector("#main table");
var errorMessage = response.ContentString;
if (messageEl != null)
errorMessage = messageEl.TextContent.Trim();
var errorMessage = document.QuerySelector("#main table td:contains(\"ERROR\")")?.TextContent.Trim();
throw new ExceptionWithConfigData(errorMessage, configData);
throw new ExceptionWithConfigData(errorMessage ?? "Login failed.", configData);
});
return IndexerConfigurationStatus.RequiresTesting;
@@ -189,25 +188,25 @@ namespace Jackett.Common.Indexers
release.Details = new Uri(qDetails.GetAttribute("href"));
// 2021-03-17 03:39 AM
var dateString = row.QuerySelectorAll("td:nth-of-type(2) div").Last().LastChild.TextContent.Trim();
release.PublishDate = DateTime.ParseExact(dateString, "yyyy-MM-dd hh:mm tt", CultureInfo.InvariantCulture);
var sizeStr = row.QuerySelector("td:nth-of-type(5)").TextContent.Trim();
release.Size = ReleaseInfo.GetBytes(sizeStr);
// requests can be 'Pre Release Time: 2013-04-22 02:00 AM Uploaded: 3 Years, 6 Months, 4 Weeks, 2 Days, 16 Hours, 52 Minutes, 41 Seconds after Pre'
var dateMatch = _dateMatchRegex.Match(row.QuerySelector("td:nth-of-type(2) > div:last-child").TextContent.Trim());
if (dateMatch.Success)
release.PublishDate = DateTime.ParseExact(dateMatch.Value, "yyyy-MM-dd hh:mm tt", CultureInfo.InvariantCulture);
release.Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-of-type(5)").TextContent.Trim());
release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(7)").TextContent.Trim());
release.Peers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(8)").TextContent.Trim()) + release.Seeders;
var catLink = row.QuerySelector("td:nth-of-type(1) a").GetAttribute("href");
var catSplit = catLink.IndexOf("category=");
if (catSplit > -1)
catLink = catLink.Substring(catSplit + 9);
release.Category = MapTrackerCatToNewznab(catLink);
var categoryLink = row.QuerySelector("td:nth-of-type(1) a").GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "category");
release.Category = MapTrackerCatToNewznab(cat);
var grabs = row.QuerySelector("td:nth-child(6)").TextContent;
release.Grabs = ParseUtil.CoerceInt(grabs);
var cover = row.QuerySelector("td:nth-of-type(2) > div > img[src]")?.GetAttribute("src")?.Trim();
release.Poster = !string.IsNullOrEmpty(cover) && cover.StartsWith("/") ? new Uri(SiteLink + cover.TrimStart('/')) : null;
if (row.QuerySelector("img[title^=\"Free Torrent\"]") != null)
release.DownloadVolumeFactor = 0;
else if (row.QuerySelector("img[title^=\"Silver Torrent\"]") != null)
@@ -215,10 +214,7 @@ namespace Jackett.Common.Indexers
else
release.DownloadVolumeFactor = 1;
if (row.QuerySelector("img[title^=\"x2 Torrent\"]") != null)
release.UploadVolumeFactor = 2;
else
release.UploadVolumeFactor = 1;
release.UploadVolumeFactor = row.QuerySelector("img[title^=\"x2 Torrent\"]") != null ? 2 : 1;
releases.Add(release);
}

View File

@@ -49,9 +49,9 @@ namespace Jackett.Common.Indexers
}
};
private new ConfigurationDataBasicLogin configData
private new ConfigurationDataBasicLoginWith2FA configData
{
get => (ConfigurationDataBasicLogin)base.configData;
get => (ConfigurationDataBasicLoginWith2FA)base.configData;
set => base.configData = value;
}
@@ -73,7 +73,7 @@ namespace Jackett.Common.Indexers
logger: l,
p: ps,
cacheService: cs,
configData: new ConfigurationDataBasicLogin())
configData: new ConfigurationDataBasicLoginWith2FA())
{
Encoding = Encoding.UTF8;
Language = "en-US";
@@ -93,26 +93,24 @@ namespace Jackett.Common.Indexers
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
{
LoadValuesFromJson(configJson);
var pairs = new Dictionary<string, string>
{
{"username", configData.Username.Value},
{"password", configData.Password.Value},
{"keeplogged", "1"},
{"login", "Login"}
};
{
{ "username", configData.Username.Value },
{ "password", configData.Password.Value },
{ "code", configData.TwoFactorAuth.Value },
{ "keeplogged", "1" },
{ "login", "Login" }
};
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, SearchUrl, LandingUrl, true);
await ConfigureIfOK(result.Cookies, result.ContentString?.Contains("logout.php") == true,
() =>
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(result.ContentString);
var warningNode = dom.QuerySelector("#loginform > .warning");
var errorMessage = warningNode?.TextContent.Trim().Replace("\n\t", " ");
throw new ExceptionWithConfigData(errorMessage, configData);
});
await ConfigureIfOK(result.Cookies, result.ContentString?.Contains("logout.php") == true, () =>
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(result.ContentString);
var errorMessage = dom.QuerySelector("#loginform > .warning")?.TextContent.Trim();
throw new Exception(errorMessage ?? "Login failed.");
});
SaveConfig();
@@ -126,61 +124,64 @@ namespace Jackett.Common.Indexers
var searchString = query.GetQueryString();
var searchUrl = SearchUrl;
var searchParams = new Dictionary<string, string> { };
var queryCollection = new NameValueCollection { };
var queryCollection = new NameValueCollection
{
{ "order_by", "time" },
{ "order_way", "desc" }
};
// Search String
if (!string.IsNullOrWhiteSpace(query.ImdbID))
queryCollection.Add("cataloguenumber", query.ImdbID);
queryCollection.Set("cataloguenumber", query.ImdbID);
else if (!string.IsNullOrWhiteSpace(searchString))
queryCollection.Add("searchstr", searchString);
queryCollection.Set("searchstr", searchString);
// Filter Categories
if (query.HasSpecifiedCategories)
{
foreach (var cat in MapTorznabCapsToTrackers(query))
{
queryCollection.Add("filter_cat[" + cat.ToString() + "]", "1");
}
}
queryCollection.Set($"filter_cat[{cat}]", "1");
if (query.Artist != null)
queryCollection.Add("artistname", query.Artist);
queryCollection.Set("artistname", query.Artist);
if (query.Label != null)
queryCollection.Add("recordlabel", query.Label);
queryCollection.Set("recordlabel", query.Label);
if (query.Year != null)
queryCollection.Add("year", query.Year.ToString());
queryCollection.Set("year", query.Year.ToString());
if (query.Album != null)
queryCollection.Add("groupname", query.Album);
queryCollection.Set("groupname", query.Album);
if (query.IsGenreQuery)
queryCollection.Add("taglist", query.Genre);
{
queryCollection.Set("taglist", query.Genre);
queryCollection.Set("tags_type", "0");
}
searchUrl += "?" + queryCollection.GetQueryString();
var searchPage = await RequestWithCookiesAndRetryAsync(searchUrl, method: RequestType.POST, data: searchParams);
var searchPage = await RequestWithCookiesAndRetryAsync(searchUrl);
// Occasionally the cookies become invalid, login again if that happens
if (searchPage.IsRedirect)
{
await ApplyConfiguration(null);
searchPage = await RequestWithCookiesAndRetryAsync(searchUrl, method: RequestType.POST, data: searchParams);
searchPage = await RequestWithCookiesAndRetryAsync(searchUrl);
}
try
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(searchPage.ContentString);
var albumRows = dom.QuerySelectorAll("table#torrent_table > tbody > tr:has(strong > a[href*=\"torrents.php?id=\"])");
var albumRows = dom.QuerySelectorAll("table#torrent_table > tbody > tr.group:has(strong > a[href*=\"torrents.php?id=\"])");
foreach (var row in albumRows)
{
var releaseGroupRegex = new Regex(@"torrents\.php\?id=([0-9]+)");
var releaseYearRegex = new Regex(@"\[(\d{4})\]$");
var albumNameNode = row.QuerySelector("strong > a[href*=\"torrents.php?id=\"]");
var albumNameNode = row.QuerySelector("strong > a[href^=\"torrents.php?id=\"]");
var artistsNameNodes = row.QuerySelectorAll("strong > a[href*=\"artist.php?id=\"]");
var albumYearNode = row.QuerySelector("strong:has(a[href*=\"torrents.php?id=\"])");
var categoryNode = row.QuerySelector(".cats_col > div");
@@ -196,15 +197,8 @@ namespace Jackett.Common.Indexers
}
var releaseArtist = "Various Artists";
if (artistsNameNodes.Count() > 0)
{
var aristNames = new List<string>();
foreach (var aristNode in artistsNameNodes)
{
aristNames.Add(aristNode.TextContent.Trim());
}
releaseArtist = string.Join(", ", aristNames);
}
if (artistsNameNodes.Any())
releaseArtist = string.Join(", ", artistsNameNodes.Select(artist => artist.TextContent.Trim()).ToList());
var releaseAlbumName = albumNameNode.TextContent.Trim();
var releaseGroupId = ParseUtil.CoerceInt(releaseGroupRegex.Match(albumNameNode.GetAttribute("href")).Groups[1].ToString());
@@ -226,86 +220,63 @@ namespace Jackett.Common.Indexers
}
}
var releaseRows = dom.QuerySelectorAll(String.Format(".group_torrent.groupid_{0}", releaseGroupId));
string lastEdition = null;
var releaseRows = dom.QuerySelectorAll($"table#torrent_table > tbody > tr.group_torrent.groupid_{releaseGroupId}:has(a[href*=\"torrents.php?id=\"])");
foreach (var releaseDetails in releaseRows)
{
var editionInfoDetails = releaseDetails.QuerySelector(".edition_info");
// https://libble.me/torrents.php?id=51694&torrentid=89758
var release = new ReleaseInfo();
// Process as release details
if (editionInfoDetails != null)
var releaseMediaDetails = releaseDetails.Children[0].Children[1];
var releaseMediaType = releaseMediaDetails.TextContent;
release.Link = new Uri(SiteLink + releaseDetails.QuerySelector("a[href^=\"torrents.php?action=download&id=\"]").GetAttribute("href"));
release.Guid = release.Link;
release.Details = new Uri(SiteLink + albumNameNode.GetAttribute("href"));
// Aug 31 2020, 15:50
try
{
lastEdition = editionInfoDetails.QuerySelector("strong").TextContent;
var dateAdded = releaseDetails.QuerySelector("td:nth-child(3) > span[title]").GetAttribute("title").Trim();
release.PublishDate = DateTime.ParseExact(dateAdded, "MMM dd yyyy, HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
// Process as torrent
else
catch (Exception)
{
// https://libble.me/torrents.php?id=51694&torrentid=89758
var release = new ReleaseInfo();
var releaseMediaDetails = releaseDetails.Children[0].Children[1];
var releaseFileCountDetails = releaseDetails.Children[1];
var releaseDateDetails = releaseDetails.Children[2].Children[0];
var releaseSizeDetails = releaseDetails.Children[3];
var releaseGrabsDetails = releaseDetails.Children[4];
var releaseSeedsCountDetails = releaseDetails.Children[5];
var releasePeersCountDetails = releaseDetails.Children[6];
var releaseDownloadDetails = releaseDetails.QuerySelector("a[href*=\"action=download\"]");
var releaseMediaType = releaseMediaDetails.TextContent;
release.Link = new Uri(SiteLink + releaseDownloadDetails.GetAttribute("href"));
release.Guid = release.Link;
release.Details = new Uri(SiteLink + albumNameNode.GetAttribute("href"));
// Aug 31 2020, 15:50
try
{
release.PublishDate = DateTime.ParseExact(
releaseDateDetails.GetAttribute("title").Trim(),
"MMM dd yyyy, HH:mm",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal
);
}
catch (Exception)
{
}
release.Files = ParseUtil.CoerceInt(releaseFileCountDetails.TextContent.Trim());
release.Grabs = ParseUtil.CoerceInt(releaseGrabsDetails.TextContent.Trim());
release.Seeders = ParseUtil.CoerceInt(releaseSeedsCountDetails.TextContent.Trim());
release.Peers = release.Seeders + ParseUtil.CoerceInt(releasePeersCountDetails.TextContent.Trim());
release.Size = ReleaseInfo.GetBytes(releaseSizeDetails.TextContent.Trim());
release.Poster = releaseThumbnailUri;
release.Category = releaseNewznabCategory;
release.MinimumSeedTime = 259200; // 72 hours
// Attempt to find volume factor tag
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 1;
var releaseTags = releaseMediaType.Split('/').Select(tag => tag.Trim()).ToList();
for (var i = releaseTags.Count - 1; i >= 0; i--)
{
var releaseTag = releaseTags[i];
if (VolumeTagMappings.ContainsKey(releaseTag))
{
var volumeFactor = VolumeTagMappings[releaseTag];
release.DownloadVolumeFactor = volumeFactor.DownloadVolumeFactor;
release.UploadVolumeFactor = volumeFactor.UploadVolumeFactor;
releaseTags.RemoveAt(i);
}
}
// Set title (with volume factor tags stripped)
var releaseTagsString = string.Join(" / ", releaseTags);
release.Title = String.Format("{0} - {1} {2} {3}", releaseArtist, releaseAlbumName, releaseAlbumYear, releaseTagsString);
release.Description = releaseDescription;
release.Genres = releaseGenres;
releases.Add(release);
release.PublishDate = DateTimeUtil.FromTimeAgo(releaseDetails.QuerySelector("td:nth-child(3)")?.TextContent.Trim());
}
release.Files = ParseUtil.CoerceInt(releaseDetails.QuerySelector("td:nth-child(2)").TextContent);
release.Grabs = ParseUtil.CoerceInt(releaseDetails.QuerySelector("td:nth-child(5)").TextContent);
release.Seeders = ParseUtil.CoerceInt(releaseDetails.QuerySelector("td:nth-child(6)").TextContent);
release.Peers = release.Seeders + ParseUtil.CoerceInt(releaseDetails.QuerySelector("td:nth-child(7)").TextContent);
release.Size = ReleaseInfo.GetBytes(releaseDetails.QuerySelector("td:nth-child(4)").TextContent.Trim());
release.Poster = releaseThumbnailUri;
release.Category = releaseNewznabCategory;
release.MinimumSeedTime = 259200; // 72 hours
// Attempt to find volume factor tag
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 1;
var releaseTags = releaseMediaType.Split('/').Select(tag => tag.Trim()).ToList();
for (var i = releaseTags.Count - 1; i >= 0; i--)
{
var releaseTag = releaseTags[i];
if (VolumeTagMappings.ContainsKey(releaseTag))
{
var volumeFactor = VolumeTagMappings[releaseTag];
release.DownloadVolumeFactor = volumeFactor.DownloadVolumeFactor;
release.UploadVolumeFactor = volumeFactor.UploadVolumeFactor;
releaseTags.RemoveAt(i);
}
}
// Set title (with volume factor tags stripped)
var releaseTagsString = string.Join(" / ", releaseTags);
release.Title = $"{releaseArtist} - {releaseAlbumName} {releaseAlbumYear} {releaseTagsString}".Trim(' ', '-');
release.Description = releaseDescription;
release.Genres = releaseGenres;
releases.Add(release);
}
}
}

View File

@@ -421,10 +421,12 @@ namespace Jackett.Common.Indexers
// Eg. Doctor.Who.2005.(Доктор.Кто).S02E08
// the season/episode part is already parsed by Jackett
// query.GetQueryString = Doctor.Who.2005.(Доктор.Кто).
// query.GetQueryString = Doctor.Who.2005.(Доктор.Кто).S02E08
// query.Season = 2
// query.Episode = 8
var searchTerm = query.GetQueryString();
// remove the season/episode from the query as MejorTorrent only wants the series name
searchTerm = Regex.Replace(searchTerm, @"[S|s]\d+[E|e]\d+", "");
// Server returns a 500 error if a UTF character higher than \u00FF (ÿ) is included,
// so we need to strip them

View File

@@ -5,13 +5,13 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
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;
@@ -169,26 +169,32 @@ namespace Jackett.Common.Indexers
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var releases = new List<ReleaseInfo>();
var limit = query.Limit > 0 ? query.Limit : 100;
var offset = query.Offset > 0 ? query.Offset : 0;
var qParams = new NameValueCollection
{
{"tor[text]", query.GetQueryString()},
{"tor[searchType]", configData.SearchType.Value},
{"tor[srchIn][title]", "true"},
{"tor[srchIn][author]", "true"},
{"tor[searchType]", configData.ExcludeVip?.Value == true ? "nVIP" : "all"}, // exclude VIP torrents
{"tor[srchIn][narrator]", "true"},
{"tor[searchIn]", "torrents"},
{"tor[hash]", ""},
{"tor[sortType]", "default"},
{"tor[startNumber]", "0"},
{"tor[perpage]", limit.ToString()},
{"tor[startNumber]", offset.ToString()},
{"thumbnails", "1"}, // gives links for thumbnail sized versions of their posters
//{ "posterLink", "1"}, // gives links for a full sized poster
//{ "dlLink", "1"}, // include the url to download the torrent
{"description", "1"} // include the description
//{"bookmarks", "0"} // include if the item is bookmarked or not
};
// Exclude VIP torrents
if (configData.SearchInDescription.Value)
qParams.Add("tor[srchIn][description]", "true");
if (configData.SearchInSeries.Value)
qParams.Add("tor[srchIn][series]", "true");
if (configData.SearchInFilenames.Value)
qParams.Add("tor[srchIn][filenames]", "true");
var catList = MapTorznabCapsToTrackers(query);
if (catList.Any())
@@ -207,10 +213,17 @@ namespace Jackett.Common.Indexers
if (qParams.Count > 0)
urlSearch += $"?{qParams.GetQueryString()}";
var response = await RequestWithCookiesAndRetryAsync(urlSearch);
var response = await RequestWithCookiesAndRetryAsync(
urlSearch,
headers: new Dictionary<string, string>
{
{"Accept", "application/json"}
});
if (response.ContentString.StartsWith("Error"))
throw new Exception(response.ContentString);
var releases = new List<ReleaseInfo>();
try
{
var jsonContent = JObject.Parse(response.ContentString);
@@ -222,30 +235,46 @@ namespace Jackett.Common.Indexers
foreach (var item in jsonContent.Value<JArray>("data"))
{
//TODO shift to ReleaseInfo object initializer for consistency
var release = new ReleaseInfo();
var id = item.Value<long>("id");
release.Title = item.Value<string>("title");
var link = new Uri(sitelink, "/tor/download.php?tid=" + id);
var details = new Uri(sitelink, "/t/" + id);
release.Description = item.Value<string>("description");
var release = new ReleaseInfo
{
Guid = details,
Title = item.Value<string>("title").Trim(),
Description = item.Value<string>("description").Trim(),
Link = link,
Details = details,
Category = MapTrackerCatToNewznab(item.Value<string>("category")),
PublishDate = DateTime.ParseExact(item.Value<string>("added"), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime(),
Grabs = item.Value<long>("times_completed"),
Files = item.Value<long>("numfiles"),
Seeders = item.Value<int>("seeders"),
Peers = item.Value<int>("seeders") + item.Value<int>("leechers"),
Size = ReleaseInfo.GetBytes(item.Value<string>("size")),
DownloadVolumeFactor = item.Value<bool>("free") ? 0 : 1,
UploadVolumeFactor = 1,
// MinimumRatio = 1, // global MR is 1.0 but torrents must be seeded for 3 days regardless of ratio
MinimumSeedTime = 259200 // 72 hours
};
var authorInfo = item.Value<string>("author_info");
string author = null;
if (!string.IsNullOrWhiteSpace(authorInfo))
if (authorInfo != null)
try
{
authorInfo = Regex.Unescape(authorInfo);
var authorInfoJson = JObject.Parse(authorInfo);
author = authorInfoJson.First.Last.Value<string>();
var authorInfoList = JsonConvert.DeserializeObject<Dictionary<string, string>>(authorInfo);
var author = authorInfoList?.Take(5).Select(v => v.Value).ToList();
if (author != null && author.Any())
release.Title += " by " + string.Join(", ", author);
}
catch (Exception)
{
// the JSON on author_info field can be malformed due to double quotes
logger.Warn($"{DisplayName} error parsing author_info: {authorInfo}");
}
if (author != null)
release.Title += " by " + author;
var flags = new List<string>();
@@ -255,38 +284,14 @@ namespace Jackett.Common.Indexers
var filetype = item.Value<string>("filetype");
if (!string.IsNullOrEmpty(filetype))
flags.Add(filetype);
flags.Add(filetype.ToUpper());
if (flags.Count > 0)
release.Title += " [" + string.Join(" / ", flags) + "]";
if (item.Value<int>("vip") == 1)
if (item.Value<bool>("vip"))
release.Title += " [VIP]";
var category = item.Value<string>("category");
release.Category = MapTrackerCatToNewznab(category);
release.Link = new Uri(sitelink, "/tor/download.php?tid=" + id);
release.Details = new Uri(sitelink, "/t/" + id);
release.Guid = release.Details;
var dateStr = item.Value<string>("added");
var dateTime = DateTime.ParseExact(dateStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
release.PublishDate = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc).ToLocalTime();
release.Grabs = item.Value<long>("times_completed");
release.Files = item.Value<long>("numfiles");
release.Seeders = item.Value<int>("seeders");
release.Peers = item.Value<int>("leechers") + release.Seeders;
var size = item.Value<string>("size");
release.Size = ReleaseInfo.GetBytes(size);
release.DownloadVolumeFactor = item.Value<int>("free") == 1 ? 0 : 1;
release.UploadVolumeFactor = 1;
// release.MinimumRatio = 1; // global MR is 1.0 but torrents must be seeded for 3 days regardless of ratio
release.MinimumSeedTime = 259200; // 72 hours
releases.Add(release);
}
}

View File

@@ -103,12 +103,14 @@ namespace Jackett.Common.Indexers
request["method"] = method;
request["params"] = parameters;
request["id"] = Guid.NewGuid().ToString().Substring(0, 8);
return request.ToString();
}
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var ValidList = new List<string>() {
var validList = new List<string>
{
"action",
"adventure",
"children",
@@ -138,7 +140,8 @@ namespace Jackett.Common.Indexers
"war",
"western"
};
var ValidCats = new List<string>() {
var validCats = new List<string>
{
"sd",
"hd",
"uhd",
@@ -150,17 +153,15 @@ namespace Jackett.Common.Indexers
"2160p"
};
var searchParam = new JObject();
var searchString = query.GetQueryString();
var searchParam = new JObject
{
["age"] = ">0"
};
var searchString = query.GetQueryString();
if (!string.IsNullOrWhiteSpace(searchString))
{
searchParam["name"] = "%" + Regex.Replace(searchString, @"[ -._]", "%").Trim() + "%";
}
else
{
searchParam["name"] = "%";
}
searchParam["name"] = "%" + Regex.Replace(searchString, @"[ -._]+", "%").Trim() + "%";
if (query.IsGenreQuery)
{
var genre = new JArray
@@ -169,11 +170,11 @@ namespace Jackett.Common.Indexers
};
searchParam["tags"] = genre;
}
var limit = query.Limit;
if (limit == 0)
limit = (int)TorznabCaps.LimitsDefault;
var offset = query.Offset;
var releases = new List<ReleaseInfo>();
var parameters = new JArray
{
@@ -192,47 +193,56 @@ namespace Jackett.Common.Indexers
}, rawbody: JsonRPCRequest("getTorrents", parameters), emulateBrowser: false);
if (response.ContentString != null && response.ContentString.Contains("Invalid params"))
throw new Exception($"Invalid API Key configured");
throw new Exception("Invalid API Key configured");
char[] delimiters = { ',', ' ', '/', ')', '(', '.', ';', '[', ']', '"', '|', ':' };
var releases = new List<ReleaseInfo>();
try
{
var json = JObject.Parse(response.ContentString);
foreach (var r in json["result"]["items"].Cast<JObject>())
var jsonContent = JObject.Parse(response.ContentString);
foreach (var item in jsonContent.Value<JObject>("result").Value<JArray>("items"))
{
var link = new Uri(item.Value<string>("download"));
var details = new Uri($"{SiteLink}torrents.php?id={item.Value<string>("group_id")}");
var descriptions = new List<string>();
if (!string.IsNullOrWhiteSpace((string)r["group_name"]))
descriptions.Add("Group Name: " + (string)r["group_name"]);
var link = new Uri((string)r["download"]);
var details = new Uri($"{SiteLink}torrents.php?id={(string)r["group_id"]}");
var publishDate = DateTime.ParseExact((string)r["rls_utc"] + " +00:00", "yyyy-MM-dd HH:mm:ss zzz", CultureInfo.InvariantCulture);
var tags = string.Join(",", r["tags"]);
char[] delimiters = { ',', ' ', '/', ')', '(', '.', ';', '[', ']', '"', '|', ':' };
var releaseGenres = ValidList.Intersect(tags.ToLower().Split(delimiters, System.StringSplitOptions.RemoveEmptyEntries)).ToList();
if (!string.IsNullOrWhiteSpace(item.Value<string>("group_name")))
descriptions.Add("Group Name: " + item.Value<string>("group_name"));
var tags = string.Join(",", item.Value<JArray>("tags"));
var releaseGenres = validList.Intersect(tags.ToLower().Split(delimiters, StringSplitOptions.RemoveEmptyEntries)).ToList();
descriptions.Add("Tags: " + string.Join(",", releaseGenres));
var releaseCats = ValidCats.Intersect(tags.ToLower().Split(delimiters, System.StringSplitOptions.RemoveEmptyEntries)).ToList();
var releaseCats = validCats.Intersect(tags.ToLower().Split(delimiters, StringSplitOptions.RemoveEmptyEntries)).ToList();
var release = new ReleaseInfo
{
Title = (string)r["rls_name"],
Category = MapTrackerCatToNewznab(releaseCats.Any() ? releaseCats.First() : "TV"),
Details = details,
Guid = link,
Link = link,
PublishDate = publishDate,
Seeders = (int)r["seed"],
Peers = (int)r["seed"] + (int)r["leech"],
Size = (long)r["size"],
Grabs = (int)r["snatch"],
UploadVolumeFactor = 1,
Details = details,
Title = item.Value<string>("rls_name").Trim(),
Category = MapTrackerCatToNewznab(releaseCats.Any() ? releaseCats.First() : "TV"),
PublishDate = DateTime.Parse(item.Value<string>("rls_utc"), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal),
Seeders = item.Value<int>("seed"),
Peers = item.Value<int>("seed") + item.Value<int>("leech"),
Size = item.Value<long>("size"),
Files = item.Value<JArray>("file_list").Count,
Grabs = item.Value<int>("snatch"),
DownloadVolumeFactor = 0, // ratioless
UploadVolumeFactor = 1,
MinimumRatio = 0, // ratioless
MinimumSeedTime = 86400, // 24 hours
MinimumSeedTime = item.Value<string>("cat").ToLower() == "season" ? 432000 : 86400, // 120 hours for seasons and 24 hours for episodes
Description = string.Join("<br />\n", descriptions)
};
if (release.Genres == null)
release.Genres = new List<string>();
release.Genres = releaseGenres;
var banner = (string)r["series_banner"];
if ((!string.IsNullOrEmpty(banner)) && (!banner.Contains("noimage.png")))
release.Poster = new Uri((string)r["series_banner"]);
var banner = item.Value<string>("series_banner");
if (!string.IsNullOrEmpty(banner) && !banner.Contains("noimage.png"))
release.Poster = new Uri(banner);
releases.Add(release);
}
@@ -241,6 +251,7 @@ namespace Jackett.Common.Indexers
{
OnParseError(response.ContentString, ex);
}
return releases;
}
}

View File

@@ -27,8 +27,6 @@ namespace Jackett.Common.Indexers
private string LoginUrl => SiteLink + "login.php";
private string LoginCheckUrl => SiteLink + "takelogin.php";
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 ConfigurationDataNorbits ConfigData => (ConfigurationDataNorbits)configData;
@@ -218,14 +216,14 @@ namespace Jackett.Common.Indexers
// Check login before performing a query
await CheckLoginAsync();
var SearchTerms = new List<string> { exactSearchTerm };
var searchTerms = new List<string> { exactSearchTerm };
// duplicate search without diacritics
var baseSearchTerm = StringUtil.RemoveDiacritics(exactSearchTerm);
if (baseSearchTerm != exactSearchTerm)
SearchTerms.Add(baseSearchTerm);
searchTerms.Add(baseSearchTerm);
foreach (var searchTerm in SearchTerms)
foreach (var searchTerm in searchTerms)
{
// Build our query
var request = BuildQuery(searchTerm, query, searchUrl);
@@ -259,49 +257,38 @@ namespace Jackett.Common.Indexers
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)
{
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();
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, ""));
var qFiles = row.QuerySelector("td:nth-of-type(3) > a"); // Files
var files = qFiles != null ? ParseUtil.CoerceInt(Regex.Match(qFiles.TextContent, @"\d+").Value) : 1;
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, @"<[^>]+>|&nbsp;", "").Trim();
var date = DateTime.ParseExact(dateTime, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime();
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()));
var link = new Uri(SiteLink + row.QuerySelector("td:nth-of-type(2) > a[href*=\"download.php?id=\"]")?.GetAttribute("href").TrimStart('/'));
var qDetails = row.QuerySelector("td:nth-of-type(2) > a[href*=\"details.php?id=\"]");
var title = qDetails?.GetAttribute("title").Trim();
var details = new Uri(SiteLink + qDetails?.GetAttribute("href").TrimStart('/'));
var mainCategory = row.QuerySelector("td:nth-of-type(1) > div > a[href*=\"main_cat[]\"]")?.GetAttribute("href")?.Split('?').Last();
var secondCategory = row.QuerySelector("td:nth-of-type(1) > div > a[href*=\"sub2_cat[]\"]")?.GetAttribute("href")?.Split('?').Last();
var categoryList = new[] { mainCategory, secondCategory };
var cat = string.Join("&", categoryList.Where(c => !string.IsNullOrWhiteSpace(c)));
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)").TextContent);
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)").TextContent);
// Building release infos
var release = new ReleaseInfo
{
Category = MapTrackerCatToNewznab(cat),
Title = name,
Seeders = seeders,
Peers = seeders + leechers,
PublishDate = date,
Size = size,
Files = files,
Grabs = completed,
Guid = details,
Details = details,
Link = downloadLink,
Link = link,
Title = title,
Category = MapTrackerCatToNewznab(cat),
Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-of-type(7)").TextContent),
Files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(3) > a")?.TextContent.Trim()),
Grabs = ParseUtil.CoerceLong(row.QuerySelector("td:nth-of-type(8)")?.FirstChild?.TextContent.Trim()),
Seeders = seeders,
Peers = seeders + leechers,
PublishDate = DateTime.ParseExact(row.QuerySelector("td:nth-of-type(5)")?.TextContent.Trim(), "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture),
DownloadVolumeFactor = 1,
UploadVolumeFactor = 1,
MinimumRatio = 1,
MinimumSeedTime = 172800 // 48 hours
};
@@ -311,8 +298,7 @@ namespace Jackett.Common.Indexers
{
genres = genres.Trim().Replace("\xA0", " ").Replace("(", "").Replace(")", "").Replace(" | ", ",");
release.Description = genres;
if (release.Genres == null)
release.Genres = new List<string>();
release.Genres ??= new List<string>();
release.Genres = release.Genres.Union(genres.Split(',')).ToList();
}
@@ -326,10 +312,6 @@ namespace Jackett.Common.Indexers
release.DownloadVolumeFactor = 0.5;
else if (row.QuerySelector("img[title=\"90% Freeleech\"]") != null)
release.DownloadVolumeFactor = 0.1;
else
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 1;
releases.Add(release);
}
@@ -348,29 +330,26 @@ namespace Jackett.Common.Indexers
/// </summary>
/// <param name="term">Term to search</param>
/// <param name="query">Torznab Query for categories mapping</param>
/// <param name="url">Search url for provider</param>
/// <param name="searchUrl">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 searchUrl, int page = 0)
{
var parameters = new NameValueCollection();
var categoriesList = MapTorznabCapsToTrackers(query);
var searchterm = term;
// Building our tracker query
parameters.Add("incldead", "1");
parameters.Add("fullsearch", ConfigData.UseFullSearch.Value ? "1" : "0");
parameters.Add("scenerelease", "0");
var parameters = new NameValueCollection
{
{ "incldead", "1" },
{ "fullsearch", ConfigData.UseFullSearch.Value ? "1" : "0" },
{ "scenerelease", "0" }
};
// If search term provided
if (!string.IsNullOrWhiteSpace(query.ImdbID))
{
searchterm = "imdbsearch=" + query.ImdbID;
}
else if (!string.IsNullOrWhiteSpace(term))
{
searchterm = "search=" + WebUtilityHelpers.UrlEncode(term, Encoding.GetEncoding(28591));
}
else
{
// Showing all torrents (just for output function)
@@ -378,17 +357,16 @@ namespace Jackett.Common.Indexers
term = "all";
}
var CatQryStr = "";
foreach (var cat in categoriesList)
CatQryStr += "&" + cat;
// Building our query
url += "?" + searchterm + "&" + parameters.GetQueryString() + "&" + CatQryStr;
searchUrl += "?" + searchterm + "&" + parameters.GetQueryString();
logger.Info("\nBuilded query for \"" + term + "\"... " + url);
var categoriesList = MapTorznabCapsToTrackers(query);
if (categoriesList.Any())
searchUrl += "&" + string.Join("&", categoriesList);
// Return our search url
return url;
logger.Info("\nBuilded query for \"" + term + "\"... " + searchUrl);
return searchUrl;
}
/// <summary>

View File

@@ -175,7 +175,8 @@ namespace Jackett.Common.Indexers
// get results and follow redirect
results = await RequestWithCookiesAsync(searchUrl, referer: SearchUrl, headers: header);
if (results.ContentString.Contains("Internal server error"))
throw new Exception("Partis is offline, returning an Internal server error");
// parse results
try
{

View File

@@ -24,7 +24,7 @@ namespace Jackett.Common.Indexers
protected string cap_sid = null;
protected string cap_code_field = null;
private static readonly Regex s_StripRussianRegex = new Regex(@"(\([А-Яа-яЁё\W]+\))|(^[А-Яа-яЁё\W\d]+\/ )|([а-яА-ЯЁё \-]+,+)|([а-яА-ЯЁё]+)");
private static readonly Regex s_StripRussianRegex = new Regex(@"(\([\p{IsCyrillic}\W]+\))|(^[\p{IsCyrillic}\W\d]+\/ )|([\p{IsCyrillic} \-]+,+)|([\p{IsCyrillic}]+)");
private new ConfigurationDataPornolab configData
{
@@ -238,11 +238,13 @@ namespace Jackett.Common.Indexers
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl, true);
await ConfigureIfOK(result.Cookies, result.ContentString != null && result.ContentString.Contains("Вы зашли как:"), () =>
{
logger.Debug(result.ContentString);
var errorMessage = "Unknown error message, please report";
var LoginResultParser = new HtmlParser();
var LoginResultDocument = LoginResultParser.ParseDocument(result.ContentString);
var errormsg = LoginResultDocument.QuerySelector("h4[class=\"warnColor1 tCenter mrg_16\"]");
var errormsg = LoginResultDocument.QuerySelector("div");
if (errormsg != null && errormsg.TextContent.Contains("Форум временно отключен"))
errorMessage = errormsg.TextContent;
errormsg = LoginResultDocument.QuerySelector("h4[class=\"warnColor1 tCenter mrg_16\"]");
if (errormsg != null)
errorMessage = errormsg.TextContent;
@@ -286,36 +288,34 @@ namespace Jackett.Common.Indexers
}
try
{
var RowsSelector = "table#tor-tbl > tbody > tr";
var searchResultParser = new HtmlParser();
var searchResultDocument = searchResultParser.ParseDocument(results.ContentString);
var SearchResultParser = new HtmlParser();
var SearchResultDocument = SearchResultParser.ParseDocument(results.ContentString);
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
foreach (var Row in Rows)
var rows = searchResultDocument.QuerySelectorAll("table#tor-tbl > tbody > tr");
foreach (var row in rows)
{
try
{
var qDownloadLink = Row.QuerySelector("a.tr-dl");
var qDownloadLink = row.QuerySelector("a.tr-dl");
if (qDownloadLink == null) // Expects moderation
continue;
var qForumLink = Row.QuerySelector("a.f");
var qDetailsLink = Row.QuerySelector("a.tLink");
var qSize = Row.QuerySelector("td:nth-child(6) u");
var qForumLink = row.QuerySelector("a.f");
var qDetailsLink = row.QuerySelector("a.tLink");
var qSize = row.QuerySelector("td:nth-child(6) u");
var link = new Uri(SiteLink + "forum/" + qDetailsLink.GetAttribute("href"));
var seederString = Row.QuerySelector("td:nth-child(7) b").TextContent;
var seederString = row.QuerySelector("td:nth-child(7) b").TextContent;
var seeders = string.IsNullOrWhiteSpace(seederString) ? 0 : ParseUtil.CoerceInt(seederString);
var timestr = Row.QuerySelector("td:nth-child(11) u").TextContent;
var forum = qForumLink;
var forumid = forum.GetAttribute("href").Split('=')[1];
var forumid = ParseUtil.GetArgumentFromQueryString(qForumLink?.GetAttribute("href"), "f");
var title = configData.StripRussianLetters.Value
? s_StripRussianRegex.Replace(qDetailsLink.TextContent, "")
: qDetailsLink.TextContent;
var size = ReleaseInfo.GetBytes(qSize.TextContent);
var leechers = ParseUtil.CoerceInt(Row.QuerySelector("td:nth-child(8)").TextContent);
var grabs = ParseUtil.CoerceLong(Row.QuerySelector("td:nth-child(9)").TextContent);
var publishDate = DateTimeUtil.UnixTimestampToDateTime(long.Parse(timestr));
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);
var grabs = ParseUtil.CoerceLong(row.QuerySelector("td:nth-child(9)").TextContent);
var publishDate = DateTimeUtil.UnixTimestampToDateTime(long.Parse(row.QuerySelector("td:nth-child(11) u").TextContent));
var release = new ReleaseInfo
{
MinimumRatio = 1,
@@ -338,7 +338,7 @@ namespace Jackett.Common.Indexers
}
catch (Exception ex)
{
logger.Error(string.Format("{0}: Error while parsing row '{1}':\n\n{2}", Id, Row.OuterHtml, ex));
logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}':\n\n{ex}");
}
}
}

View File

@@ -160,14 +160,20 @@ namespace Jackett.Common.Indexers
// Send Post
var result = await RequestWithCookiesAsync(LoginUrl, loginPage.Cookies, RequestType.POST, data: pairs);
if (result.RedirectingTo == null)
throw new ExceptionWithConfigData("Login failed. Did you use the PIN number that pretome emailed you?", configData);
throw new ExceptionWithConfigData("Login failed. Did you use the PIN number that PreToMe emailed you?", configData);
// Get result from redirect
var loginCookies = result.Cookies;
await FollowIfRedirect(result, LoginUrl, null, loginCookies);
await ConfigureIfOK(loginCookies, result.ContentString?.Contains("logout.php") == true,
() => throw new ExceptionWithConfigData("Login failed", configData));
await ConfigureIfOK(loginCookies, result.ContentString?.Contains("logout.php") == true, () =>
{
var loginResultParser = new HtmlParser();
var loginResultDocument = loginResultParser.ParseDocument(result.ContentString);
var errorMessage = loginResultDocument.QuerySelector("table.body_table font[color~=\"red\"]")?.TextContent.Trim();
throw new ExceptionWithConfigData(errorMessage ?? "Login failed", configData);
});
return IndexerConfigurationStatus.RequiresTesting;
}
@@ -236,27 +242,26 @@ namespace Jackett.Common.Indexers
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(response.ContentString);
var rows = dom.QuerySelectorAll("table > tbody > tr.browse");
foreach (var row in rows)
{
var qLink = row.Children[1].QuerySelector("a");
var title = qLink.GetAttribute("title");
if (qLink.QuerySelectorAll("span").Length == 1 && title.StartsWith("NEW! |"))
title = title.Substring(6).Trim();
var qDetails = row.QuerySelector("a[href^=\"details.php?id=\"]");
var title = qDetails?.GetAttribute("title");
if (!query.MatchQueryStringAND(title))
continue; // we have to skip bad titles due to tags + any word search
var details = new Uri(SiteLink + qLink.GetAttribute("href"));
var link = new Uri(SiteLink + row.Children[2].QuerySelector("a").GetAttribute("href"));
var dateStr = Regex.Replace(row.Children[5].InnerHtml, @"\<br[\s]{0,1}[\/]{0,1}\>", " ");
var details = new Uri(SiteLink + qDetails.GetAttribute("href"));
var link = new Uri(SiteLink + row.QuerySelector("a[href^=\"download.php\"]")?.GetAttribute("href"));
var dateStr = Regex.Replace(row.QuerySelector("td:nth-of-type(6)").InnerHtml, @"\<br[\s]{0,1}[\/]{0,1}\>", " ").Trim();
var publishDate = DateTimeUtil.FromTimeAgo(dateStr);
var files = ParseUtil.CoerceInt(row.Children[3].TextContent);
var size = ReleaseInfo.GetBytes(row.Children[7].TextContent);
var grabs = ParseUtil.CoerceInt(row.Children[8].TextContent);
var seeders = ParseUtil.CoerceInt(row.Children[9].TextContent);
var leechers = ParseUtil.CoerceInt(row.Children[10].TextContent);
var cat = row.FirstElementChild.FirstElementChild.GetAttribute("href").Replace("browse.php?", string.Empty);
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(10)")?.TextContent);
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(11)")?.TextContent);
var cat = row.QuerySelector("td:nth-of-type(1) a[href^=\"browse.php\"]")?.GetAttribute("href")?.Split('?').Last();
var release = new ReleaseInfo
{
@@ -265,10 +270,10 @@ namespace Jackett.Common.Indexers
Guid = details,
Link = link,
PublishDate = publishDate,
Size = size,
Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-of-type(8)")?.TextContent),
Category = MapTrackerCatToNewznab(cat),
Files = files,
Grabs = grabs,
Files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(4)")?.TextContent),
Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(9)")?.TextContent),
Seeders = seeders,
Peers = leechers + seeders,
MinimumRatio = 0.75,

View File

@@ -22,24 +22,21 @@ namespace Jackett.Common.Indexers
[ExcludeFromCodeCoverage]
public class RuTracker : BaseWebIndexer
{
public override string[] AlternativeSiteLinks { get; protected set; } = {
"https://rutracker.org/",
"https://rutracker.net/",
"https://rutracker.nl/"
};
private new ConfigurationDataRutracker configData => (ConfigurationDataRutracker)base.configData;
private readonly TitleParser _titleParser = new TitleParser();
private string LoginUrl => SiteLink + "forum/login.php";
private string SearchUrl => SiteLink + "forum/tracker.php";
private string _capSid;
private string _capCodeField;
private new ConfigurationDataRutracker configData => (ConfigurationDataRutracker)base.configData;
public override string[] AlternativeSiteLinks { get; protected set; } = {
"https://rutracker.org/",
"https://rutracker.net/",
"https://rutracker.nl/"
};
private Regex _regexToFindTagsInReleaseTitle = new Regex(@"\[[^\[]+\]|\([^(]+\)");
public RuTracker(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
ICacheService cs)
public RuTracker(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps, ICacheService cs)
: base(id: "rutracker",
name: "RuTracker",
description: "RuTracker is a Semi-Private Russian torrent site with a thriving file-sharing community",
@@ -74,6 +71,7 @@ namespace Jackett.Common.Indexers
Encoding = Encoding.GetEncoding("windows-1251");
Language = "ru-RU";
Type = "semi-private";
// note: when refreshing the categories use the tracker.php page and NOT the search.php page!
AddCategoryMapping(22, TorznabCatType.Movies, "Наше кино");
AddCategoryMapping(941, TorznabCatType.Movies, "|- Кино СССР");
@@ -147,6 +145,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(816, TorznabCatType.TVHD, "|- Мультсериалы (DVD Video)");
AddCategoryMapping(1460, TorznabCatType.TVHD, "|- Мультсериалы (HD Video)");
AddCategoryMapping(33, TorznabCatType.TVAnime, "Аниме");
AddCategoryMapping(1106, TorznabCatType.TVAnime, "|- Онгоинги (HD Video)");
AddCategoryMapping(1105, TorznabCatType.TVAnime, "|- Аниме (HD Video)");
AddCategoryMapping(599, TorznabCatType.TVAnime, "|- Аниме (DVD)");
AddCategoryMapping(1389, TorznabCatType.TVAnime, "|- Аниме (основной подраздел)");
@@ -164,15 +163,15 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(9, TorznabCatType.TV, "Русские сериалы");
AddCategoryMapping(81, TorznabCatType.TVHD, "|- Русские сериалы (HD Video)");
AddCategoryMapping(920, TorznabCatType.TVSD, "|- Русские сериалы (DVD Video)");
AddCategoryMapping(80, TorznabCatType.TV, "|- Возвращение Мухтара");
AddCategoryMapping(1535, TorznabCatType.TV, "|- Воронины");
AddCategoryMapping(188, TorznabCatType.TV, "|- Чернобыль: Зона отчуждения");
AddCategoryMapping(91, TorznabCatType.TV, "|- Кухня / Отель Элеон");
AddCategoryMapping(80, TorznabCatType.TV, "|- Сельский детектив");
AddCategoryMapping(1535, TorznabCatType.TV, "|- По законам военного времени");
AddCategoryMapping(188, TorznabCatType.TV, "|- Московские тайны");
AddCategoryMapping(91, TorznabCatType.TV, "|- Я знаю твои секреты");
AddCategoryMapping(990, TorznabCatType.TV, "|- Универ / Универ. Новая общага / СашаТаня");
AddCategoryMapping(1408, TorznabCatType.TV, "|- Ольга / Физрук");
AddCategoryMapping(1408, TorznabCatType.TV, "|- Женская версия");
AddCategoryMapping(175, TorznabCatType.TV, "|- След");
AddCategoryMapping(79, TorznabCatType.TV, "|- Солдаты и пр.");
AddCategoryMapping(104, TorznabCatType.TV, "|- Тайны следствия");
AddCategoryMapping(79, TorznabCatType.TV, "|- Некрасивая подружка");
AddCategoryMapping(104, TorznabCatType.TV, "|- Психология преступления");
AddCategoryMapping(189, TorznabCatType.TVForeign, "Зарубежные сериалы");
AddCategoryMapping(842, TorznabCatType.TVForeign, "|- Новинки и сериалы в стадии показа");
AddCategoryMapping(235, TorznabCatType.TVForeign, "|- Сериалы США и Канады");
@@ -241,18 +240,18 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1537, TorznabCatType.TVForeign, "|- Для некондиционных раздач");
AddCategoryMapping(2100, TorznabCatType.TVForeign, "Азиатские сериалы");
AddCategoryMapping(820, TorznabCatType.TVForeign, "|- Азиатские сериалы (UHD Video)");
AddCategoryMapping(915, TorznabCatType.TVForeign, "|- Корейские сериалы с озвучкой");
AddCategoryMapping(1242, TorznabCatType.TVForeign, "|- Корейские сериалы с субтитрами");
AddCategoryMapping(717, TorznabCatType.TVForeign, "|- Китайские сериалы с субтитрами");
AddCategoryMapping(1939, TorznabCatType.TVForeign, "|- Японские сериалы с озвучкой");
AddCategoryMapping(2412, TorznabCatType.TVForeign, "|- Прочие азиатские сериалы с озвучкой");
AddCategoryMapping(915, TorznabCatType.TVForeign, "|- Корейские сериалы");
AddCategoryMapping(1242, TorznabCatType.TVForeign, "|- Корейские сериалы (HD Video)");
AddCategoryMapping(717, TorznabCatType.TVForeign, "|- Китайские сериалы");
AddCategoryMapping(1939, TorznabCatType.TVForeign, "|- Японские сериалы");
AddCategoryMapping(2412, TorznabCatType.TVForeign, "|- Сериалы Таиланда, Индонезии, Сингапура");
AddCategoryMapping(2102, TorznabCatType.TVForeign, "|- VMV и др. ролики");
AddCategoryMapping(19, TorznabCatType.TVDocumentary, "СМИ");
AddCategoryMapping(670, TorznabCatType.TVDocumentary, "Вера и религия");
AddCategoryMapping(1475, TorznabCatType.TVDocumentary, "|- [Видео Религия] Христианство");
AddCategoryMapping(2107, TorznabCatType.TVDocumentary, "|- [Видео Религия] Ислам");
AddCategoryMapping(294, TorznabCatType.TVDocumentary, "|- [Видео Религия] Религии Индии, Тибета и Восточной Азии");
AddCategoryMapping(1453, TorznabCatType.TVDocumentary, "|- [Видео Религия] Культы и новые религиозные движения");
AddCategoryMapping(294, TorznabCatType.TVDocumentary, "|- [Видео Религия] Религии Индии, Тибета и Восточной Азии");
AddCategoryMapping(46, TorznabCatType.TVDocumentary, "Документальные фильмы и телепередачи");
AddCategoryMapping(103, TorznabCatType.TVDocumentary, "|- Документальные (DVD)");
AddCategoryMapping(671, TorznabCatType.TVDocumentary, "|- [Док] Биографии. Личности и кумиры");
@@ -295,7 +294,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(979, TorznabCatType.TVDocumentary, "|- Путешествия и туризм (HD Video)");
AddCategoryMapping(2169, TorznabCatType.TVDocumentary, "|- Флора и фауна (HD Video)");
AddCategoryMapping(2166, TorznabCatType.TVDocumentary, "|- История (HD Video)");
AddCategoryMapping(2164, TorznabCatType.TVDocumentary, "|- BBC, Discovery, National Geographic, History Channel (HD Video)");
AddCategoryMapping(2164, TorznabCatType.TVDocumentary, "|- BBC, Discovery, National Geographic, History Channel, Netflix (HD Video)");
AddCategoryMapping(2163, TorznabCatType.TVDocumentary, "|- Криминальная документалистика (HD Video)");
AddCategoryMapping(85, TorznabCatType.TVDocumentary, "|- Некондиционное видео - Документальные (HD Video)");
AddCategoryMapping(24, TorznabCatType.TVDocumentary, "Развлекательные телепередачи и шоу, приколы и юмор");
@@ -346,8 +345,8 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(255, TorznabCatType.TVSport, "Спортивные турниры, фильмы и передачи");
AddCategoryMapping(256, TorznabCatType.TVSport, "|- Автоспорт");
AddCategoryMapping(1986, TorznabCatType.TVSport, "|- Мотоспорт");
AddCategoryMapping(660, TorznabCatType.TVSport, "|- Формула-1 (2021)");
AddCategoryMapping(1551, TorznabCatType.TVSport, "|- Формула-1 (2012-2020)");
AddCategoryMapping(660, TorznabCatType.TVSport, "|- Формула-1 (2022)");
AddCategoryMapping(1551, TorznabCatType.TVSport, "|- Формула-1 (2012-2021)");
AddCategoryMapping(626, TorznabCatType.TVSport, "|- Формула 1 (до 2011 вкл.)");
AddCategoryMapping(262, TorznabCatType.TVSport, "|- Велоспорт");
AddCategoryMapping(1326, TorznabCatType.TVSport, "|- Волейбол/Гандбол");
@@ -371,17 +370,16 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1319, TorznabCatType.TVSport, "|- Спорт (видео)");
AddCategoryMapping(1608, TorznabCatType.TVSport, "⚽ Футбол");
AddCategoryMapping(2294, TorznabCatType.TVSport, "|- UHDTV");
AddCategoryMapping(1229, TorznabCatType.TVSport, "|- Чемпионат Мира 2022 (финальный турнир)] (финальный турнир)");
AddCategoryMapping(1229, TorznabCatType.TVSport, "|- Чемпионат Мира 2022 (финальный турнир)");
AddCategoryMapping(1693, TorznabCatType.TVSport, "|- Чемпионат Мира 2022 (отбор)");
AddCategoryMapping(2532, TorznabCatType.TVSport, "|- Чемпионат Европы 2020 [2021] (финальный турнир)");
AddCategoryMapping(136, TorznabCatType.TVSport, "|- Чемпионат Европы 2020 [2021] (отбор)");
AddCategoryMapping(592, TorznabCatType.TVSport, "|- Лига Наций");
AddCategoryMapping(1693, TorznabCatType.TVSport, "|- Чемпионат Мира 2022 (отбор)");
AddCategoryMapping(2533, TorznabCatType.TVSport, "|- Чемпионат Мира 2018 (игры)");
AddCategoryMapping(1952, TorznabCatType.TVSport, "|- Чемпионат Мира 2018 (обзорные передачи, документалистика)");
AddCategoryMapping(1621, TorznabCatType.TVSport, "|- Чемпионаты Мира");
AddCategoryMapping(2075, TorznabCatType.TVSport, "|- Россия 2022-2023");
AddCategoryMapping(1668, TorznabCatType.TVSport, "|- Россия 2021-2022");
AddCategoryMapping(2075, TorznabCatType.TVSport, "|- Россия 2020-2021");
AddCategoryMapping(1613, TorznabCatType.TVSport, "|- Россия/СССР");
AddCategoryMapping(1614, TorznabCatType.TVSport, "|- Англия");
AddCategoryMapping(1623, TorznabCatType.TVSport, "|- Испания");
@@ -397,7 +395,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1617, TorznabCatType.TVSport, "|- Еврокубки");
AddCategoryMapping(1620, TorznabCatType.TVSport, "|- Чемпионаты Европы");
AddCategoryMapping(1998, TorznabCatType.TVSport, "|- Товарищеские турниры и матчи");
AddCategoryMapping(1343, TorznabCatType.TVSport, "|- Обзорные и аналитические передачи 2018-2021");
AddCategoryMapping(1343, TorznabCatType.TVSport, "|- Обзорные и аналитические передачи 2018-2022");
AddCategoryMapping(751, TorznabCatType.TVSport, "|- Обзорные и аналитические передачи");
AddCategoryMapping(497, TorznabCatType.TVSport, "|- Документальные фильмы (футбол)");
AddCategoryMapping(1697, TorznabCatType.TVSport, "|- Мини-футбол/Пляжный футбол");
@@ -405,7 +403,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(2001, TorznabCatType.TVSport, "|- Международные соревнования");
AddCategoryMapping(2002, TorznabCatType.TVSport, "|- NBA / NCAA (до 2000 г.)");
AddCategoryMapping(283, TorznabCatType.TVSport, "|- NBA / NCAA (2000-2010 гг.)");
AddCategoryMapping(1997, TorznabCatType.TVSport, "|- NBA / NCAA (2010-2022 гг.)");
AddCategoryMapping(1997, TorznabCatType.TVSport, "|- NBA / NCAA (2010-2023 гг.)");
AddCategoryMapping(2003, TorznabCatType.TVSport, "|- Европейский клубный баскетбол");
AddCategoryMapping(2009, TorznabCatType.TVSport, "🏒 Хоккей");
AddCategoryMapping(2010, TorznabCatType.TVSport, "|- Хоккей с мячом / Бенди");
@@ -799,7 +797,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(2430, TorznabCatType.AudioLossless, "|- Этническая музыка Индии (lossless)");
AddCategoryMapping(1283, TorznabCatType.AudioMP3, "|- Этническая музыка Африки и Ближнего Востока (lossy)");
AddCategoryMapping(2085, TorznabCatType.AudioLossless, "|- Этническая музыка Африки и Ближнего Востока (lossless)");
AddCategoryMapping(1282, TorznabCatType.Audio, "|- Фольклорная, Народная, Эстрадная музыка Кавказа и Закавказья (lossless)");
AddCategoryMapping(1282, TorznabCatType.Audio, "|- Фольклорная, Народная, Эстрадная музыка Кавказа и Закавказья (lossy и lossless)");
AddCategoryMapping(1284, TorznabCatType.AudioMP3, "|- Этническая музыка Северной и Южной Америки (lossy)");
AddCategoryMapping(1285, TorznabCatType.AudioLossless, "|- Этническая музыка Северной и Южной Америки (lossless)");
AddCategoryMapping(1138, TorznabCatType.Audio, "|- Этническая музыка Австралии, Тихого и Индийского океанов (lossy и lossless)");
@@ -963,8 +961,8 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(202, TorznabCatType.AudioMP3, "|- Indie Rock, Indie Pop, Dream Pop, Brit-Pop (lossy)");
AddCategoryMapping(172, TorznabCatType.AudioLossless, "|- Post-Punk, Shoegaze, Garage Rock, Noise Rock (lossless)");
AddCategoryMapping(236, TorznabCatType.AudioMP3, "|- Post-Punk, Shoegaze, Garage Rock, Noise Rock (lossy)");
AddCategoryMapping(1742, TorznabCatType.AudioLossless, "|- Indie, Post-Rock & Post-Punk (lossless)");
AddCategoryMapping(1743, TorznabCatType.AudioMP3, "|- Indie, Post-Rock & Post-Punk (lossy)");
AddCategoryMapping(1742, TorznabCatType.AudioLossless, "|- Post-Rock (lossless)");
AddCategoryMapping(1743, TorznabCatType.AudioMP3, "|- Post-Rock (lossy)");
AddCategoryMapping(1744, TorznabCatType.AudioLossless, "|- Industrial & Post-industrial (lossless)");
AddCategoryMapping(1745, TorznabCatType.AudioMP3, "|- Industrial & Post-industrial (lossy)");
AddCategoryMapping(1746, TorznabCatType.AudioLossless, "|- Emocore, Post-hardcore, Metalcore (lossless)");
@@ -1122,8 +1120,8 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1886, TorznabCatType.AudioVideo, "|- Электронная музыка (DVD Video)");
AddCategoryMapping(2509, TorznabCatType.AudioVideo, "|- Документальные фильмы о музыке и музыкантах (DVD Video)");
AddCategoryMapping(2507, TorznabCatType.AudioVideo, "Неофициальные DVD видео");
AddCategoryMapping(2263, TorznabCatType.AudioVideo, "Классическая музыка, Опера, Балет, Мюзикл (Неофициальные DVD Video)");
AddCategoryMapping(2511, TorznabCatType.AudioVideo, "Шансон, Авторская песня, Сборные концерты, МДЖ (Неофициальные DVD Video)");
AddCategoryMapping(2263, TorznabCatType.AudioVideo, "|- Классическая музыка, Опера, Балет, Мюзикл (Неофициальные DVD Video)");
AddCategoryMapping(2511, TorznabCatType.AudioVideo, "|- Шансон, Авторская песня, Сборные концерты, МДЖ (Неофициальные DVD Video)");
AddCategoryMapping(2264, TorznabCatType.AudioVideo, "|- Зарубежная и Отечественная Поп-музыка (Неофициальные DVD Video)");
AddCategoryMapping(2262, TorznabCatType.AudioVideo, "|- Джаз и Блюз (Неофициальные DVD Video)");
AddCategoryMapping(2261, TorznabCatType.AudioVideo, "|- Зарубежная и Отечественная Рок-музыка (Неофициальные DVD Video)");
@@ -1244,7 +1242,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1041, TorznabCatType.PC, "|- Изменение интерфейса ОС Windows");
AddCategoryMapping(1636, TorznabCatType.PC, "|- Скринсейверы");
AddCategoryMapping(1042, TorznabCatType.PC, "|- Разное (Системные программы под Windows)");
AddCategoryMapping(1059, TorznabCatType.PC, "Архив (Разрегистрированные раздачи)");
AddCategoryMapping(1059, TorznabCatType.PC, "|- Архив (Разрегистрированные раздачи)");
AddCategoryMapping(1014, TorznabCatType.PC, "Системы для бизнеса, офиса, научной и проектной работы");
AddCategoryMapping(2134, TorznabCatType.PC, "|- Медицина - интерактивный софт");
AddCategoryMapping(1060, TorznabCatType.PC, "|- Всё для дома: кройка, шитьё, кулинария");
@@ -1290,7 +1288,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(1357, TorznabCatType.OtherMisc, "|- Авторские работы");
AddCategoryMapping(890, TorznabCatType.OtherMisc, "|- Официальные сборники векторных клипартов");
AddCategoryMapping(830, TorznabCatType.OtherMisc, "|- Прочие векторные клипарты");
AddCategoryMapping(1290, TorznabCatType.OtherMisc, "|- Photostoсks");
AddCategoryMapping(1290, TorznabCatType.OtherMisc, "|- Photostocks");
AddCategoryMapping(1962, TorznabCatType.OtherMisc, "|- Дополнения для программ компоузинга и постобработки");
AddCategoryMapping(831, TorznabCatType.OtherMisc, "|- Рамки, шаблоны, текстуры и фоны");
AddCategoryMapping(829, TorznabCatType.OtherMisc, "|- Прочие растровые клипарты");
@@ -1378,7 +1376,7 @@ namespace Jackett.Common.Indexers
AddCategoryMapping(147, TorznabCatType.Books, "|- Публикации и учебные материалы (тексты)");
AddCategoryMapping(847, TorznabCatType.MoviesOther, "|- Трейлеры и дополнительные материалы к фильмам");
AddCategoryMapping(1167, TorznabCatType.TVOther, "|- Любительские видеоклипы");
AddCategoryMapping(321, TorznabCatType.Other, "Место встречи изменить - Отчеты о встречах");
AddCategoryMapping(321, TorznabCatType.Other, "|- Отчеты о встречах");
}
public override async Task<ConfigurationData> GetConfigurationForSetup()
@@ -1389,17 +1387,15 @@ namespace Jackett.Common.Indexers
var response = await RequestWithCookiesAsync(LoginUrl);
var parser = new HtmlParser();
var doc = parser.ParseDocument(response.ContentString);
var captchaimg = doc.QuerySelector("img[src^=\"https://static.t-ru.org/captcha/\"]");
var captchaimg = doc.QuerySelector("img[src^=\"https://static.rutracker.cc/captcha/\"]");
if (captchaimg != null)
{
var captchaImage = await RequestWithCookiesAsync(captchaimg.GetAttribute("src"));
configData.CaptchaImage.Value = captchaImage.ContentBytes;
var codefield = doc.QuerySelector("input[name^=\"cap_code_\"]");
_capCodeField = codefield.GetAttribute("name");
var sidfield = doc.QuerySelector("input[name=\"cap_sid\"]");
_capSid = sidfield.GetAttribute("value");
_capCodeField = doc.QuerySelector("input[name^=\"cap_code_\"]")?.GetAttribute("name");
_capSid = doc.QuerySelector("input[name=\"cap_sid\"]")?.GetAttribute("value");
}
else
configData.CaptchaImage.Value = null;
@@ -1435,11 +1431,13 @@ namespace Jackett.Common.Indexers
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, CookieHeader, true, null, LoginUrl, true);
await ConfigureIfOK(result.Cookies, result.ContentString != null && result.ContentString.Contains("id=\"logged-in-username\""), () =>
{
logger.Debug(result.ContentString);
var errorMessage = "Unknown error message, please report";
var parser = new HtmlParser();
var doc = parser.ParseDocument(result.ContentString);
var errormsg = doc.QuerySelector("h4[class=\"warnColor1 tCenter mrg_16\"]");
var errormsg = doc.QuerySelector("div.msg-main");
if (errormsg != null)
errorMessage = errormsg.TextContent;
errormsg = doc.QuerySelector("h4[class=\"warnColor1 tCenter mrg_16\"]");
if (errormsg != null)
errorMessage = errormsg.TextContent;
@@ -1515,6 +1513,7 @@ namespace Jackett.Common.Indexers
queryCollection.Add("f", string.Join(",", MapTorznabCapsToTrackers(query)));
var searchUrl = SearchUrl + "?" + queryCollection.GetQueryString();
return searchUrl;
}
@@ -1539,6 +1538,7 @@ namespace Jackett.Common.Indexers
var qDetailsLink = row.QuerySelector("td.t-title-col > div.t-title > a.tLink");
var details = new Uri(SiteLink + "forum/" + qDetailsLink.GetAttribute("href"));
var title = qDetailsLink.TextContent.Trim();
var category = GetCategoryOfRelease(row);
var size = GetSizeOfRelease(row);
@@ -1554,7 +1554,14 @@ namespace Jackett.Common.Indexers
{
MinimumRatio = 1,
MinimumSeedTime = 0,
Title = qDetailsLink.TextContent,
Title = _titleParser.Parse(
title,
category,
configData.StripRussianLetters.Value,
configData.MoveAllTagsToEndOfReleaseTitle.Value,
configData.MoveFirstTagsToEndOfReleaseTitle.Value
),
Description = title,
Details = details,
Link = link,
Guid = details,
@@ -1568,60 +1575,6 @@ namespace Jackett.Common.Indexers
UploadVolumeFactor = 1
};
// TODO finish extracting release variables to simplify release initialization
if (IsAnyTvCategory(release.Category))
{
// extract season and episodes
var regex = new Regex(".+\\/\\s([^а-яА-я\\/]+)\\s\\/.+Сезон\\s*[:]*\\s+(\\d+).+(?:Серии|Эпизод)+\\s*[:]*\\s+(\\d+-*\\d*).+,\\s+(.+)\\][\\s]?(.*)");
//replace double 4K quality in title
release.Title = release.Title.Replace(", 4K]", "]");
var title = regex.Replace(release.Title, "$1 - S$2E$3 - rus $4 $5");
title = Regex.Replace(title, "-Rip", "Rip", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "WEB-DLRip", "WEBDL", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "WEB-DL", "WEBDL", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "HDTVRip", "HDTV", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "Кураж-Бамбей", "kurazh", RegexOptions.IgnoreCase);
release.Title = title;
}
else if (IsAnyMovieCategory(release.Category))
{
// remove director's name from title
// rutracker movies titles look like: russian name / english name (russian director / english director) other stuff
// Ирландец / The Irishman (Мартин Скорсезе / Martin Scorsese) [2019, США, криминал, драма, биография, WEB-DL 1080p] Dub (Пифагор) + MVO (Jaskier) + AVO (Юрий Сербин) + Sub Rus, Eng + Original Eng
// this part should be removed: (Мартин Скорсезе / Martin Scorsese)
var director = new Regex(@"(\([А-Яа-яЁё\W]+)\s/\s(.+?)\)");
release.Title = director.Replace(release.Title, "");
// Bluray quality fix: radarr parse Blu-ray Disc as Bluray-1080p but should be BR-DISK
release.Title = Regex.Replace(release.Title, "Blu-ray Disc", "BR-DISK", RegexOptions.IgnoreCase);
// language fix: all rutracker releases contains russian track
if (release.Title.IndexOf("rus", StringComparison.OrdinalIgnoreCase) < 0)
release.Title += " rus";
}
if (configData.StripRussianLetters.Value)
{
var regex = new Regex(@"(\([А-Яа-яЁё\W]+\))|(^[А-Яа-яЁё\W\d]+\/ )|([а-яА-ЯЁё \-]+,+)|([а-яА-ЯЁё]+)");
release.Title = regex.Replace(release.Title, "");
}
if (configData.MoveAllTagsToEndOfReleaseTitle.Value)
{
release.Title = MoveAllTagsToEndOfReleaseTitle(release.Title);
}
else if (configData.MoveFirstTagsToEndOfReleaseTitle.Value)
{
release.Title = MoveFirstTagsToEndOfReleaseTitle(release.Title);
}
if (release.Category.Contains(TorznabCatType.Audio.ID))
{
release.Title = DetectRereleaseInReleaseTitle(release.Title);
}
return release;
}
catch (Exception ex)
@@ -1637,7 +1590,7 @@ namespace Jackett.Common.Indexers
var qSeeders = row.QuerySelector("td:nth-child(7)");
if (qSeeders != null && !qSeeders.TextContent.Contains("дн"))
{
var seedersString = qSeeders.QuerySelector("b").TextContent;
var seedersString = qSeeders.QuerySelector("b")?.TextContent.Trim();
if (!string.IsNullOrWhiteSpace(seedersString))
seeders = ParseUtil.CoerceInt(seedersString);
}
@@ -1646,107 +1599,186 @@ namespace Jackett.Common.Indexers
private ICollection<int> GetCategoryOfRelease(in IElement row)
{
var forum = row.QuerySelector("td.f-name-col > div.f-name > a");
var forumid = forum.GetAttribute("href").Split('=')[1];
return MapTrackerCatToNewznab(forumid);
var forum = row.QuerySelector("td.f-name-col > div.f-name > a")?.GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(forum, "f");
return MapTrackerCatToNewznab(cat);
}
private long GetSizeOfRelease(in IElement row)
{
var qSize = row.QuerySelector("td.tor-size");
var size = ReleaseInfo.GetBytes(qSize.GetAttribute("data-ts_text"));
return size;
}
private long GetSizeOfRelease(in IElement row) => ReleaseInfo.GetBytes(row.QuerySelector("td.tor-size")?.GetAttribute("data-ts_text"));
private DateTime GetPublishDateOfRelease(in IElement row)
{
var timestr = row.QuerySelector("td:nth-child(10)").GetAttribute("data-ts_text");
var publishDate = DateTimeUtil.UnixTimestampToDateTime(long.Parse(timestr));
return publishDate;
}
private DateTime GetPublishDateOfRelease(in IElement row) => DateTimeUtil.UnixTimestampToDateTime(long.Parse(row.QuerySelector("td:nth-child(10)")?.GetAttribute("data-ts_text")));
private bool IsAnyTvCategory(ICollection<int> category)
public class TitleParser
{
return category.Contains(TorznabCatType.TV.ID)
|| TorznabCatType.TV.SubCategories.Any(subCat => category.Contains(subCat.ID));
}
private bool IsAnyMovieCategory(ICollection<int> category)
{
return category.Contains(TorznabCatType.Movies.ID)
|| TorznabCatType.Movies.SubCategories.Any(subCat => category.Contains(subCat.ID));
}
private string MoveAllTagsToEndOfReleaseTitle(string input)
{
var output = input + " ";
foreach (Match match in _regexToFindTagsInReleaseTitle.Matches(input))
private static readonly List<Regex> _FindTagsInTitlesRegexList = new List<Regex>
{
var tag = match.ToString();
output = output.Replace(tag, "") + tag;
}
output = output.Trim();
return output;
}
new Regex(@"\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)"),
new Regex(@"\[(?>\[(?<c>)|[^\[\]]+|\](?<-c>))*(?(c)(?!))\]")
};
private string MoveFirstTagsToEndOfReleaseTitle(string input)
{
var output = input + " ";
var expectedIndex = 0;
foreach (Match match in _regexToFindTagsInReleaseTitle.Matches(input))
private readonly Regex _stripCyrillicRegex = new Regex(@"(\([\p{IsCyrillic}\W]+\))|(^[\p{IsCyrillic}\W\d]+\/ )|([\p{IsCyrillic} \-]+,+)|([\p{IsCyrillic}]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleCommaRegex = new Regex(@"\s(\d+),(\d+)", RegexOptions.Compiled);
private readonly Regex _tvTitleCyrillicXRegex = new Regex(@"([\s-])Х+([\s\)\]])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleRusSeasonEpisodeOfRegex = new Regex(@"Сезон\s*[:]*\s+(\d+).+(?:Серии|Эпизод|Выпуски)+\s*[:]*\s+(\d+(?:-\d+)?)\s*из\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleRusSeasonEpisodeRegex = new Regex(@"Сезон\s*[:]*\s+(\d+).+(?:Серии|Эпизод|Выпуски)+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleRusSeasonRegex = new Regex(@"Сезон\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleRusEpisodeOfRegex = new Regex(@"(?:Серии|Эпизод|Выпуски)+\s*[:]*\s+(\d+(?:-\d+)?)\s*из\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleRusEpisodeRegex = new Regex(@"(?:Серии|Эпизод|Выпуски)+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public string Parse(string title, ICollection<int> category, bool stripCyrillicLetters = true, bool moveAllTagsToEndOfReleaseTitle = false, bool moveFirstTagsToEndOfReleaseTitle = false)
{
if (match.Index > expectedIndex)
// https://www.fileformat.info/info/unicode/category/Pd/list.htm
title = Regex.Replace(title, @"\p{Pd}", "-", RegexOptions.Compiled | RegexOptions.IgnoreCase);
// replace double 4K quality in title
title = Regex.Replace(title, @"\b(2160p), 4K\b", "$1", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (IsAnyTvCategory(category))
{
var substring = input.Substring(expectedIndex, match.Index - expectedIndex);
if (string.IsNullOrWhiteSpace(substring))
expectedIndex = match.Index;
else
break;
title = _tvTitleCommaRegex.Replace(title, " $1-$2");
title = _tvTitleCyrillicXRegex.Replace(title, "$1XX$2");
title = _tvTitleRusSeasonEpisodeOfRegex.Replace(title, "S$1E$2 of $3");
title = _tvTitleRusSeasonEpisodeRegex.Replace(title, "S$1E$2");
title = _tvTitleRusSeasonRegex.Replace(title, "S$1");
title = _tvTitleRusEpisodeOfRegex.Replace(title, "E$1 of $2");
title = _tvTitleRusEpisodeRegex.Replace(title, "E$1");
}
var tag = match.ToString();
output = output.Replace(tag, "") + tag;
expectedIndex += tag.Length;
else if (IsAnyMovieCategory(category))
{
// remove director's name from title
// rutracker movies titles look like: russian name / english name (russian director / english director) other stuff
// Ирландец / The Irishman (Мартин Скорсезе / Martin Scorsese) [2019, США, криминал, драма, биография, WEB-DL 1080p] Dub (Пифагор) + MVO (Jaskier) + AVO (Юрий Сербин) + Sub Rus, Eng + Original Eng
// this part should be removed: (Мартин Скорсезе / Martin Scorsese)
title = Regex.Replace(title, @"(\([\p{IsCyrillic}\W]+)\s/\s(.+?)\)", string.Empty, RegexOptions.Compiled | RegexOptions.IgnoreCase);
// Bluray quality fix: radarr parse Blu-ray Disc as Bluray-1080p but should be BR-DISK
title = Regex.Replace(title, @"\bBlu-ray Disc\b", "BR-DISK", RegexOptions.Compiled | RegexOptions.IgnoreCase);
// language fix: all rutracker releases contains russian track
if (title.IndexOf("rus", StringComparison.OrdinalIgnoreCase) < 0)
title += " rus";
}
if (stripCyrillicLetters)
title = _stripCyrillicRegex.Replace(title, string.Empty).Trim(' ', '-');
if (moveAllTagsToEndOfReleaseTitle)
title = MoveAllTagsToEndOfReleaseTitle(title);
else if (moveFirstTagsToEndOfReleaseTitle)
title = MoveFirstTagsToEndOfReleaseTitle(title);
if (IsAnyAudioCategory(category))
title = DetectRereleaseInReleaseTitle(title);
title = Regex.Replace(title, @"\b-Rip\b", "Rip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bHDTVRip\b", "HDTV", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEB-DLRip\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEBDLRip\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEBDL\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bКураж-Бамбей\b", "kurazh", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\(\s*\/\s*", "(", RegexOptions.Compiled);
title = Regex.Replace(title, @"\s*\/\s*\)", ")", RegexOptions.Compiled);
title = Regex.Replace(title, @"[\[\(]\s*[\)\]]", "", RegexOptions.Compiled);
title = title.Trim(' ', '&', ',', '.', '!', '?', '+', '-', '_', '|', '/', '\\', ':');
// replace multiple spaces with a single space
title = Regex.Replace(title, @"\s+", " ");
return title.Trim();
}
output = output.Trim();
return output;
}
/// <summary>
/// Searches the release title to find a 'year1/year2' pattern that would indicate that this is a re-release of an old music album.
/// If the release is found to be a re-release, this is added to the title as a new tag.
/// Not to be confused with discographies; they mostly follow the 'year1-year2' pattern.
/// </summary>
private string DetectRereleaseInReleaseTitle(string input)
{
var fullTitle = input;
private static bool IsAnyTvCategory(ICollection<int> category) => category.Contains(TorznabCatType.TV.ID) || TorznabCatType.TV.SubCategories.Any(subCat => category.Contains(subCat.ID));
var squareBracketTags = input.FindSubstringsBetween('[', ']', includeOpeningAndClosing: true);
input = input.RemoveSubstrings(squareBracketTags);
private static bool IsAnyMovieCategory(ICollection<int> category) => category.Contains(TorznabCatType.Movies.ID) || TorznabCatType.Movies.SubCategories.Any(subCat => category.Contains(subCat.ID));
var roundBracketTags = input.FindSubstringsBetween('(', ')', includeOpeningAndClosing: true);
input = input.RemoveSubstrings(roundBracketTags);
private static bool IsAnyAudioCategory(ICollection<int> category) => category.Contains(TorznabCatType.Audio.ID) || TorznabCatType.Audio.SubCategories.Any(subCat => category.Contains(subCat.ID));
var regex = new Regex(@"\d{4}");
var yearsInTitle = regex.Matches(input);
if (yearsInTitle == null || yearsInTitle.Count < 2)
private static string MoveAllTagsToEndOfReleaseTitle(string input)
{
//Can only be a re-release if there's at least 2 years in the title.
return fullTitle;
var output = input;
foreach (var findTagsRegex in _FindTagsInTitlesRegexList)
{
foreach (Match match in findTagsRegex.Matches(input))
{
var tag = match.ToString();
output = $"{output.Replace(tag, "")} {tag}".Trim();
}
}
return output.Trim();
}
regex = new Regex(@"(\d{4}) *\/ *(\d{4})");
var regexMatch = regex.Match(input);
if (!regexMatch.Success)
private static string MoveFirstTagsToEndOfReleaseTitle(string input)
{
//Not in the expected format. Return the unaltered title.
return fullTitle;
var output = input;
foreach (var findTagsRegex in _FindTagsInTitlesRegexList)
{
var expectedIndex = 0;
foreach (Match match in findTagsRegex.Matches(output))
{
if (match.Index > expectedIndex)
{
var substring = output.Substring(expectedIndex, match.Index - expectedIndex);
if (string.IsNullOrWhiteSpace(substring))
expectedIndex = match.Index;
else
break;
}
var tag = match.ToString();
var regex = new Regex(Regex.Escape(tag));
output = $"{regex.Replace(output, string.Empty, 1)} {tag}".Trim();
expectedIndex += tag.Length;
}
}
return output.Trim();
}
var originalYear = regexMatch.Groups[1].ToString();
fullTitle = fullTitle.Replace(regexMatch.ToString(), originalYear);
/// <summary>
/// Searches the release title to find a 'year1/year2' pattern that would indicate that this is a re-release of an old music album.
/// If the release is found to be a re-release, this is added to the title as a new tag.
/// Not to be confused with discographies; they mostly follow the 'year1-year2' pattern.
/// </summary>
private static string DetectRereleaseInReleaseTitle(string input)
{
var fullTitle = input;
return fullTitle + "(Re-release)";
var squareBracketTags = input.FindSubstringsBetween('[', ']', includeOpeningAndClosing: true);
input = input.RemoveSubstrings(squareBracketTags);
var roundBracketTags = input.FindSubstringsBetween('(', ')', includeOpeningAndClosing: true);
input = input.RemoveSubstrings(roundBracketTags);
var regex = new Regex(@"\d{4}");
var yearsInTitle = regex.Matches(input);
if (yearsInTitle == null || yearsInTitle.Count < 2)
{
//Can only be a re-release if there's at least 2 years in the title.
return fullTitle;
}
regex = new Regex(@"(\d{4}) *\/ *(\d{4})");
var regexMatch = regex.Match(input);
if (!regexMatch.Success)
{
//Not in the expected format. Return the unaltered title.
return fullTitle;
}
var originalYear = regexMatch.Groups[1].ToString();
fullTitle = fullTitle.Replace(regexMatch.ToString(), originalYear);
return fullTitle + "(Re-release)";
}
}
}
}

View File

@@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
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;
@@ -57,7 +57,7 @@ namespace Jackett.Common.Indexers
cacheService: cs,
configData: new ConfigurationDataSceneTime())
{
Encoding = Encoding.GetEncoding("iso-8859-1");
Encoding = Encoding.UTF8;
Language = "en-US";
Type = "private";
@@ -119,14 +119,14 @@ namespace Jackett.Common.Indexers
var catList = MapTorznabCapsToTrackers(query);
foreach (var cat in catList)
qParams.Add("c" + cat, "1");
qParams.Set($"c{cat}", "1");
if (!string.IsNullOrEmpty(query.SanitizedSearchTerm))
qParams.Add("search", query.GetQueryString());
qParams.Set("search", query.GetQueryString());
// If Only Freeleech Enabled
if (configData.Freeleech.Value)
qParams.Add("freeleech", "on");
qParams.Set("freeleech", "on");
var searchUrl = SearchUrl + "?" + qParams.GetQueryString();
var results = await RequestWithCookiesAsync(searchUrl);
@@ -161,8 +161,7 @@ namespace Jackett.Common.Indexers
if (table == null)
return releases; // no results
var headerColumns = table.QuerySelectorAll("tbody > tr > td.cat_Head")
.Select(x => x.TextContent).ToList();
var headerColumns = table.QuerySelectorAll("tbody > tr > td.cat_Head").Select(x => x.TextContent).ToList();
var categoryIndex = headerColumns.FindIndex(x => x.Equals("Type"));
var nameIndex = headerColumns.FindIndex(x => x.Equals("Name"));
var sizeIndex = headerColumns.FindIndex(x => x.Equals("Size"));
@@ -172,44 +171,44 @@ namespace Jackett.Common.Indexers
var rows = dom.QuerySelectorAll("tr.browse");
foreach (var row in rows)
{
// TODO convert to initializer
var qDescCol = row.Children[nameIndex];
var qLink = qDescCol.QuerySelector("a");
var title = qLink.TextContent;
if (!query.MatchQueryStringAND(title))
continue;
var details = new Uri(SiteLink + "/" + qLink.GetAttribute("href"));
var torrentId = ParseUtil.GetArgumentFromQueryString(qLink.GetAttribute("href"), "id");
var link = new Uri(string.Format(DownloadUrl, torrentId));
var categoryLink = row.Children[categoryIndex].QuerySelector("a")?.GetAttribute("href");
var cat = categoryLink != null ? ParseUtil.GetArgumentFromQueryString(categoryLink, "cat") : "82"; // default
var seeders = ParseUtil.CoerceInt(row.Children[seedersIndex].TextContent.Trim());
var dateAdded = qDescCol.QuerySelector("span[class=\"elapsedDate\"]").GetAttribute("title").Trim();
var publishDate = DateTime.TryParseExact(dateAdded, "dddd, MMMM d, yyyy \\a\\t h:mmtt", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var date)
? date
: DateTimeUtil.FromTimeAgo(qDescCol.QuerySelector("span[class=\"elapsedDate\"]").TextContent.Trim());
var release = new ReleaseInfo
{
Guid = details,
Details = details,
Link = link,
Title = title,
Category = MapTrackerCatToNewznab(cat),
PublishDate = publishDate,
Size = ReleaseInfo.GetBytes(row.Children[sizeIndex].TextContent),
Seeders = seeders,
Peers = ParseUtil.CoerceInt(row.Children[leechersIndex].TextContent.Trim()) + seeders,
DownloadVolumeFactor = row.QuerySelector("font > b:contains(Freeleech)") != null ? 0 : 1,
UploadVolumeFactor = 1,
MinimumRatio = 1,
MinimumSeedTime = 259200 // 72 hours
};
var catId = "82"; // default
var qCatLink = row.Children[categoryIndex].QuerySelector("a");
if (qCatLink != null)
{
catId = new Regex(@"\?cat=(\d*)").Match(qCatLink.GetAttribute("href")).Groups[1].ToString().Trim();
}
release.Category = MapTrackerCatToNewznab(catId);
var qDescCol = row.Children[nameIndex];
var qLink = qDescCol.QuerySelector("a");
release.Title = qLink.TextContent;
if (!query.MatchQueryStringAND(release.Title))
continue;
release.Details = new Uri(SiteLink + "/" + qLink.GetAttribute("href"));
release.Guid = release.Details;
var torrentId = qLink.GetAttribute("href").Split('=')[1];
release.Link = new Uri(string.Format(DownloadUrl, torrentId));
release.PublishDate = DateTimeUtil.FromTimeAgo(qDescCol.ChildNodes.Last().TextContent);
var sizeStr = row.Children[sizeIndex].TextContent;
release.Size = ReleaseInfo.GetBytes(sizeStr);
release.Seeders = ParseUtil.CoerceInt(row.Children[seedersIndex].TextContent.Trim());
release.Peers = ParseUtil.CoerceInt(row.Children[leechersIndex].TextContent.Trim()) + release.Seeders;
release.DownloadVolumeFactor = row.QuerySelector("font > b:contains(Freeleech)") != null ? 0 : 1;
release.UploadVolumeFactor = 1;
releases.Add(release);
}
}

View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using Jackett.Common.Helpers;
using Jackett.Common.Models;
using Jackett.Common.Models.IndexerConfig;
using Jackett.Common.Models.IndexerConfig.Bespoke;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
using Jackett.Common.Utils.Clients;
@@ -31,7 +31,7 @@ namespace Jackett.Common.Indexers
"https://speeders.me/"
};
private new ConfigurationDataBasicLogin configData => (ConfigurationDataBasicLogin)base.configData;
private new ConfigurationDataSpeedCD configData => (ConfigurationDataSpeedCD)base.configData;
public SpeedCD(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
ICacheService cs)
@@ -63,7 +63,7 @@ namespace Jackett.Common.Indexers
logger: l,
p: ps,
cacheService: cs,
configData: new ConfigurationDataBasicLogin(
configData: new ConfigurationDataSpeedCD(
@"Speed.Cd have increased their security. If you are having problems please check the security tab
in your Speed.Cd profile. Eg. Geo Locking, your seedbox may be in a different country to the one where you login via your
web browser.<br><br>For best results, change the 'Torrents per page' setting to 100 in 'Profile Settings > Torrents'."))
@@ -116,7 +116,8 @@ namespace Jackett.Common.Indexers
private async Task DoLogin()
{
// first request with username
var pairs = new Dictionary<string, string> {
var pairs = new Dictionary<string, string>
{
{ "username", configData.Username.Value }
};
var result = await RequestLoginAndFollowRedirect(LoginUrl1, pairs, null, true, null, SiteLink);
@@ -127,7 +128,8 @@ namespace Jackett.Common.Indexers
var token = matches.Groups[1].Value;
// second request with token and password
pairs = new Dictionary<string, string> {
pairs = new Dictionary<string, string>
{
{ "pwd", configData.Password.Value },
{ "a", token }
};
@@ -138,9 +140,11 @@ namespace Jackett.Common.Indexers
var parser = new HtmlParser();
var dom = parser.ParseDocument(result.ContentString);
var errorMessage = dom.QuerySelector("h5")?.TextContent;
if (result.ContentString.Contains("Wrong Captcha!"))
errorMessage = "Captcha required due to a failed login attempt. Login via a browser to whitelist your IP and then reconfigure Jackett.";
throw new Exception(errorMessage);
throw new Exception(errorMessage ?? "Login failed.");
});
}
@@ -155,20 +159,42 @@ namespace Jackett.Common.Indexers
foreach (var cat in catList)
qc.Add(cat);
if (configData.Freeleech.Value)
qc.Add("freeleech");
if (configData.ExcludeArchives.Value)
qc.Add("norar");
if (query.IsImdbQuery)
{
var term = query.ImdbID;
if (!string.IsNullOrWhiteSpace(query.GetEpisodeSearchString()))
{
term += $" {query.GetEpisodeSearchString()}";
if (query.Season > 0 && string.IsNullOrEmpty(query.Episode))
term += "*";
}
qc.Add("deep");
qc.Add("q");
qc.Add(query.ImdbID);
qc.Add(WebUtilityHelpers.UrlEncode(term.Trim(), Encoding));
}
else
{
var term = query.GetQueryString();
if (!string.IsNullOrWhiteSpace(query.GetEpisodeSearchString()) && query.Season > 0 && string.IsNullOrEmpty(query.Episode))
term += "*";
qc.Add("q");
qc.Add(WebUtilityHelpers.UrlEncode(query.GetQueryString(), Encoding));
qc.Add(WebUtilityHelpers.UrlEncode(term.Trim(), Encoding));
}
var searchUrl = SearchUrl + string.Join("/", qc);
var response = await RequestWithCookiesAndRetryAsync(searchUrl);
if (!response.ContentString.Contains("/logout.php")) // re-login
{
await DoLogin();
@@ -179,42 +205,35 @@ namespace Jackett.Common.Indexers
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(response.ContentString);
var rows = dom.QuerySelectorAll("div.boxContent > table > tbody > tr");
var rows = dom.QuerySelectorAll("div.boxContent > table > tbody > tr");
foreach (var row in rows)
{
var cells = row.QuerySelectorAll("td");
var title = row.QuerySelector("td[class='lft'] > div > a").TextContent.Trim();
var link = new Uri(SiteLink + row.QuerySelector("img[title='Download']").ParentElement.GetAttribute("href").TrimStart('/'));
var details = new Uri(SiteLink + row.QuerySelector("td[class='lft'] > div > a").GetAttribute("href").TrimStart('/'));
var size = ReleaseInfo.GetBytes(cells[5].TextContent);
var grabs = ParseUtil.CoerceInt(cells[6].TextContent);
var seeders = ParseUtil.CoerceInt(cells[7].TextContent);
var leechers = ParseUtil.CoerceInt(cells[8].TextContent);
var pubDateStr = row.QuerySelector("span[class^='elapsedDate']").GetAttribute("title").Replace(" at", "");
var publishDate = DateTime.ParseExact(pubDateStr, "dddd, MMMM d, yyyy h:mmtt", CultureInfo.InvariantCulture);
var cat = row.QuerySelector("a").GetAttribute("href").Split('/').Last();
var downloadVolumeFactor = row.QuerySelector("span:contains(\"[Freeleech]\")") != null ? 0 : 1;
var title = CleanTitle(row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]")?.TextContent);
var link = new Uri(SiteLink + row.QuerySelector("td:nth-child(4) a[href^=\"/download/\"]")?.GetAttribute("href")?.TrimStart('/'));
var details = new Uri(SiteLink + row.QuerySelector("td:nth-child(2) > div > a[href^=\"/t/\"]")?.GetAttribute("href")?.TrimStart('/'));
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)")?.TextContent);
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)")?.TextContent);
var pubDateStr = row.QuerySelector("td:nth-child(2) span[class^=\"elapsedDate\"]")?.GetAttribute("title")?.Replace(" at", "");
var cat = row.QuerySelector("td:nth-child(1) a")?.GetAttribute("href")?.Split('/').Last();
var downloadVolumeFactor = row.QuerySelector("td:nth-child(2) span:contains(\"[Freeleech]\")") != null ? 0 : 1;
var release = new ReleaseInfo
{
Title = title,
Link = link,
Guid = link,
Link = link,
Details = details,
PublishDate = publishDate,
Title = title,
Category = MapTrackerCatToNewznab(cat),
Size = size,
Grabs = grabs,
PublishDate = DateTime.ParseExact(pubDateStr, "dddd, MMMM d, yyyy h:mmtt", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal),
Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(6)")?.TextContent),
Grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)")?.TextContent),
Seeders = seeders,
Peers = seeders + leechers,
MinimumRatio = 1,
MinimumSeedTime = 259200, // 72 hours
DownloadVolumeFactor = downloadVolumeFactor,
UploadVolumeFactor = 1
UploadVolumeFactor = 1,
MinimumRatio = 1,
MinimumSeedTime = 259200 // 72 hours
};
releases.Add(release);
@@ -226,5 +245,12 @@ namespace Jackett.Common.Indexers
}
return releases;
}
private static string CleanTitle(string title)
{
title = Regex.Replace(title, @"\[REQ(UEST)?\]", string.Empty, RegexOptions.Compiled | RegexOptions.IgnoreCase);
return title.Trim(' ', '.');
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -9,29 +10,26 @@ 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;
using WebClient = Jackett.Common.Utils.Clients.WebClient;
namespace Jackett.Common.Indexers
{
[ExcludeFromCodeCoverage]
public class Toloka : BaseWebIndexer
{
private string LoginUrl => SiteLink + "/login.php";
private string SearchUrl => SiteLink + "/tracker.php";
protected string cap_sid = null;
protected string cap_code_field = null;
private new ConfigurationDataToloka configData
{
get => (ConfigurationDataToloka)base.configData;
set => base.configData = value;
}
public Toloka(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps,
ICacheService cs)
private readonly TitleParser _titleParser = new TitleParser();
private string LoginUrl => SiteLink + "login.php";
private string SearchUrl => SiteLink + "tracker.php";
public Toloka(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps, ICacheService cs)
: base(id: "toloka",
name: "Toloka.to",
description: "Toloka is a Semi-Private Ukrainian torrent site with a thriving file-sharing community",
@@ -209,6 +207,7 @@ namespace Jackett.Common.Indexers
{ "password", configData.Password.Value },
{ "autologin", "on" },
{ "ssl", "on" },
{ "redirect", "" },
{ "login", "Вхід" }
};
@@ -216,15 +215,14 @@ namespace Jackett.Common.Indexers
await ConfigureIfOK(result.Cookies, result.ContentString != null && result.ContentString.Contains("logout=true"), () =>
{
logger.Debug(result.ContentString);
var errorMessage = "Unknown error message, please report";
var LoginResultParser = new HtmlParser();
var LoginResultDocument = LoginResultParser.ParseDocument(result.ContentString);
var errormsg = LoginResultDocument.QuerySelector("h4[class=\"warnColor1 tCenter mrg_16\"]");
if (errormsg != null)
errorMessage = errormsg.TextContent;
throw new ExceptionWithConfigData(errorMessage, configData);
var loginResultParser = new HtmlParser();
var loginResultDocument = loginResultParser.ParseDocument(result.ContentString);
var errorMessage = loginResultDocument.QuerySelector("table.forumline table span.gen")?.FirstChild?.TextContent;
throw new ExceptionWithConfigData(errorMessage ?? "Unknown error message, please report.", configData);
});
return IndexerConfigurationStatus.RequiresTesting;
}
@@ -241,16 +239,12 @@ namespace Jackett.Common.Indexers
// if the search string is empty use the getnew view
if (string.IsNullOrWhiteSpace(searchString))
{
qc.Add("nm", searchString);
}
else // use the normal search
{
searchString = searchString.Replace("-", " ");
if (query.Season != 0)
{
searchString += " Сезон " + query.Season;
}
qc.Add("nm", searchString);
}
@@ -259,81 +253,63 @@ namespace Jackett.Common.Indexers
var searchUrl = SearchUrl + "?" + qc.GetQueryString();
var results = await RequestWithCookiesAsync(searchUrl);
if (!results.ContentString.Contains("logout=true"))
{
// re login
await ApplyConfiguration(null);
results = await RequestWithCookiesAsync(searchUrl);
}
try
{
var RowsSelector = "table.forumline > tbody > tr[class*=prow]";
var searchResultParser = new HtmlParser();
var searchResultDocument = searchResultParser.ParseDocument(results.ContentString);
var rows = searchResultDocument.QuerySelectorAll("table.forumline > tbody > tr[class*=\"prow\"]");
var SearchResultParser = new HtmlParser();
var SearchResultDocument = SearchResultParser.ParseDocument(results.ContentString);
var Rows = SearchResultDocument.QuerySelectorAll(RowsSelector);
foreach (var Row in Rows)
foreach (var row in rows)
{
try
{
var qDownloadLink = Row.QuerySelector("td:nth-child(6) > a");
var qDownloadLink = row.QuerySelector("td:nth-child(6) > a");
if (qDownloadLink == null) // Expects moderation
continue;
var qDetailsLink = Row.QuerySelector("td:nth-child(3) > a");
var qSize = Row.QuerySelector("td:nth-child(7)");
var seedersStr = Row.QuerySelector("td:nth-child(10) > b").TextContent;
var seeders = string.IsNullOrWhiteSpace(seedersStr) ? 0 : ParseUtil.CoerceInt(seedersStr);
var timestr = Row.QuerySelector("td:nth-child(13)").TextContent;
var forum = Row.QuerySelector("td:nth-child(2) > a");
var forumid = forum.GetAttribute("href").Split('=')[1];
var qDetailsLink = row.QuerySelector("td:nth-child(3) > a");
var details = new Uri(SiteLink + qDetailsLink.GetAttribute("href"));
var title = qDetailsLink.TextContent.Trim();
var link = new Uri(SiteLink + qDownloadLink.GetAttribute("href"));
var size = ReleaseInfo.GetBytes(qSize.TextContent);
var leechers = ParseUtil.CoerceInt(Row.QuerySelector("td:nth-child(11) > b").TextContent);
var publishDate = DateTimeUtil.FromFuzzyTime(timestr);
var forumLink = row.QuerySelector("td:nth-child(2) > a").GetAttribute("href");
var forumId = ParseUtil.GetArgumentFromQueryString(forumLink, "f");
var category = MapTrackerCatToNewznab(forumId);
var seedersStr = row.QuerySelector("td:nth-child(10) > b").TextContent;
var seeders = string.IsNullOrWhiteSpace(seedersStr) ? 0 : ParseUtil.CoerceInt(seedersStr);
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(11) > b").TextContent);
var release = new ReleaseInfo
{
MinimumRatio = 1,
MinimumSeedTime = 0,
Title = qDetailsLink.TextContent,
Guid = details,
Details = details,
Link = link,
Guid = details,
Size = size,
Title = _titleParser.Parse(title, category, configData.StripCyrillicLetters.Value),
Description = title,
Category = category,
Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(7)").TextContent),
Seeders = seeders,
Peers = leechers + seeders,
Grabs = 0, //ParseUtil.CoerceLong(Row.QuerySelector("td:nth-child(9)").TextContent);
PublishDate = publishDate,
Category = MapTrackerCatToNewznab(forumid),
PublishDate = DateTimeUtil.FromFuzzyTime(row.QuerySelector("td:nth-child(13)").TextContent),
DownloadVolumeFactor = 1,
UploadVolumeFactor = 1
UploadVolumeFactor = 1,
MinimumRatio = 1,
MinimumSeedTime = 0
};
// TODO cleanup
if (release.Category.Contains(TorznabCatType.TV.ID))
{
// extract season and episodes
var regex = new Regex(".+\\/\\s([^а-яА-я\\/]+)\\s\\/.+Сезон\\s*[:]*\\s+(\\d+).+(?:Серії|Епізод)+\\s*[:]*\\s+(\\d+-*\\d*).+,\\s+(.+)\\]\\s(.+)");
var title = regex.Replace(release.Title, "$1 - S$2E$3 - rus $4 $5");
title = Regex.Replace(title, "-Rip", "Rip", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "WEB-DLRip", "WEBDL", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "WEB-DL", "WEBDL", RegexOptions.IgnoreCase);
title = Regex.Replace(title, "HDTVRip", "HDTV", RegexOptions.IgnoreCase);
release.Title = title;
}
else if (configData.StripCyrillicLetters.Value)
{
var regex = new Regex(@"(\([А-Яа-яіІєЄїЇ\W]+\))|(^[А-Яа-яіІєЄїЇ\W\d]+\/ )|([а-яА-ЯіІєЄїЇ \-]+,+)|([а-яА-ЯіІєЄїЇ]+)");
release.Title = regex.Replace(release.Title, "");
}
releases.Add(release);
}
catch (Exception ex)
{
logger.Error(string.Format("{0}: Error while parsing row '{1}':\n\n{2}", Id, Row.OuterHtml, ex));
logger.Error($"{Id}: Error while parsing row '{row.OuterHtml}':\n\n{ex}");
}
}
}
@@ -344,5 +320,112 @@ namespace Jackett.Common.Indexers
return releases;
}
public class TitleParser
{
private static readonly List<Regex> _FindTagsInTitlesRegexList = new List<Regex>
{
new Regex(@"\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)"),
new Regex(@"\[(?>\[(?<c>)|[^\[\]]+|\](?<-c>))*(?(c)(?!))\]")
};
private readonly Regex _tvTitleCommaRegex = new Regex(@"\s(\d+),(\d+)", RegexOptions.Compiled);
private readonly Regex _tvTitleCyrillicXRegex = new Regex(@"([\s-])Х+([\)\]])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleMultipleSeasonsRegex = new Regex(@"(?:Сезон|Seasons?)\s*[:]*\s+(\d+-\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleUkrSeasonEpisodeOfRegex = new Regex(@"Сезон\s*[:]*\s+(\d+).+(?:Серії|Серія|Серій|Епізод)+\s*[:]*\s+(\d+(?:-\d+)?)\s*з\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleUkrSeasonEpisodeRegex = new Regex(@"Сезон\s*[:]*\s+(\d+).+(?:Серії|Серія|Серій|Епізод)+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleUkrSeasonRegex = new Regex(@"Сезон\s*[:]*\s+(\d+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleUkrEpisodeOfRegex = new Regex(@"(?:Серії|Серія|Серій|Епізод)+\s*[:]*\s+(\d+(?:-\d+)?)\s*з\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleUkrEpisodeRegex = new Regex(@"(?:Серії|Серія|Серій|Епізод)+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleEngSeasonEpisodeOfRegex = new Regex(@"Season\s*[:]*\s+(\d+).+(?:Episodes?)+\s*[:]*\s+(\d+(?:-\d+)?)\s*of\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleEngSeasonEpisodeRegex = new Regex(@"Season\s*[:]*\s+(\d+).+(?:Episodes?)+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleEngSeasonRegex = new Regex(@"Season\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleEngEpisodeOfRegex = new Regex(@"(?:Episodes?)+\s*[:]*\s+(\d+(?:-\d+)?)\s*of\s*([\w?])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _tvTitleEngEpisodeRegex = new Regex(@"(?:Episodes?)+\s*[:]+\s*[:]*\s+(\d+(?:-\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly Regex _stripCyrillicRegex = new Regex(@"(\([\p{IsCyrillic}\W]+\))|(^[\p{IsCyrillic}\W\d]+\/ )|([\p{IsCyrillic} \-]+,+)|([\p{IsCyrillic}]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public string Parse(string title, ICollection<int> category, bool stripCyrillicLetters = true)
{
// https://www.fileformat.info/info/unicode/category/Pd/list.htm
title = Regex.Replace(title, @"\p{Pd}", "-", RegexOptions.Compiled | RegexOptions.IgnoreCase);
if (IsAnyTvCategory(category))
{
title = _tvTitleCommaRegex.Replace(title, " $1-$2");
title = _tvTitleCyrillicXRegex.Replace(title, "$1XX$2");
// special case for multiple seasons
title = _tvTitleMultipleSeasonsRegex.Replace(title, "S$1");
title = _tvTitleUkrSeasonEpisodeOfRegex.Replace(title, "S$1E$2 of $3");
title = _tvTitleUkrSeasonEpisodeRegex.Replace(title, "S$1E$2");
title = _tvTitleUkrSeasonRegex.Replace(title, "S$1");
title = _tvTitleUkrEpisodeOfRegex.Replace(title, "E$1 of $2");
title = _tvTitleUkrEpisodeRegex.Replace(title, "E$1");
title = _tvTitleEngSeasonEpisodeOfRegex.Replace(title, "S$1E$2 of $3");
title = _tvTitleEngSeasonEpisodeRegex.Replace(title, "S$1E$2");
title = _tvTitleEngSeasonRegex.Replace(title, "S$1");
title = _tvTitleEngEpisodeOfRegex.Replace(title, "E$1 of $2");
title = _tvTitleEngEpisodeRegex.Replace(title, "E$1");
}
if (stripCyrillicLetters)
title = _stripCyrillicRegex.Replace(title, string.Empty).Trim(' ', '-');
title = Regex.Replace(title, @"\b-Rip\b", "Rip", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bHDTVRip\b", "HDTV", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEB-DLRip\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEBDLRip\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = Regex.Replace(title, @"\bWEBDL\b", "WEB-DL", RegexOptions.Compiled | RegexOptions.IgnoreCase);
title = MoveFirstTagsToEndOfReleaseTitle(title);
title = Regex.Replace(title, @"\(\s*\/\s*", "(", RegexOptions.Compiled);
title = Regex.Replace(title, @"\s*\/\s*\)", ")", RegexOptions.Compiled);
title = Regex.Replace(title, @"[\[\(]\s*[\)\]]", "", RegexOptions.Compiled);
title = title.Trim(' ', '&', ',', '.', '!', '?', '+', '-', '_', '|', '/', '\\', ':');
// replace multiple spaces with a single space
title = Regex.Replace(title, @"\s+", " ");
return title.Trim();
}
private static bool IsAnyTvCategory(ICollection<int> category) => category.Contains(TorznabCatType.TV.ID) || TorznabCatType.TV.SubCategories.Any(subCat => category.Contains(subCat.ID));
private static string MoveFirstTagsToEndOfReleaseTitle(string input)
{
var output = input;
foreach (var findTagsRegex in _FindTagsInTitlesRegexList)
{
var expectedIndex = 0;
foreach (Match match in findTagsRegex.Matches(output))
{
if (match.Index > expectedIndex)
{
var substring = output.Substring(expectedIndex, match.Index - expectedIndex);
if (string.IsNullOrWhiteSpace(substring))
expectedIndex = match.Index;
else
break;
}
var tag = match.ToString();
var regex = new Regex(Regex.Escape(tag));
output = $"{regex.Replace(output, string.Empty, 1)} {tag}".Trim();
expectedIndex += tag.Length;
}
}
return output.Trim();
}
}
}
}

View File

@@ -25,8 +25,7 @@ namespace Jackett.Common.Indexers
private string LoginUrl => SiteLink + "takelogin.php";
private string GetRSSKeyUrl => SiteLink + "getrss.php";
private string SearchUrl => SiteLink + "browse.php";
private readonly Regex _dateMatchRegex = new Regex(
@"\d{2}-\d{2}-\d{4} \d{2}:\d{2}", RegexOptions.Compiled);
private readonly Regex _dateMatchRegex = new Regex(@"\d{2}-\d{2}-\d{4} \d{2}:\d{2}", RegexOptions.Compiled);
private new ConfigurationDataBasicLoginWithRSSAndDisplay configData =>
(ConfigurationDataBasicLoginWithRSSAndDisplay)base.configData;
@@ -143,8 +142,35 @@ namespace Jackett.Common.Indexers
AddCategoryMapping("Wii Games", TorznabCatType.ConsoleWii);
AddCategoryMapping("Wrestling", TorznabCatType.TVSport);
AddCategoryMapping("Xbox Games", TorznabCatType.ConsoleXBox);
// Configure the sort selects
var sortBySelect = new SingleSelectConfigurationItem(
"Sort by",
new Dictionary<string, string>
{
{ "added", "created" },
{ "seeders", "seeders" },
{ "size", "size" },
{ "name", "title" }
})
{ Value = "added" };
configData.AddDynamic("sortrequestedfromsite", sortBySelect);
var orderSelect = new SingleSelectConfigurationItem(
"Order",
new Dictionary<string, string>
{
{ "desc", "descending" },
{ "asc", "ascending" }
})
{ Value = "desc" };
configData.AddDynamic("orderrequestedfromsite", orderSelect);
}
private string GetSortBy => ((SingleSelectConfigurationItem)configData.GetDynamic("sortrequestedfromsite")).Value;
private string GetOrder => ((SingleSelectConfigurationItem)configData.GetDynamic("orderrequestedfromsite")).Value;
public override async Task<ConfigurationData> GetConfigurationForSetup()
{
var loginPage = await RequestWithCookiesAsync(LandingUrl);
@@ -171,12 +197,11 @@ namespace Jackett.Common.Indexers
public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
{
LoadValuesFromJson(configJson);
var pairs = new Dictionary<string, string>
{
{"username", configData.Username.Value},
{"password", configData.Password.Value}
};
{
{ "username", configData.Username.Value },
{ "password", configData.Password.Value }
};
var captchaText = (StringConfigurationItem)configData.GetDynamic("CaptchaText");
if (captchaText != null)
@@ -184,26 +209,26 @@ namespace Jackett.Common.Indexers
//var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, null, SiteLink, true);
var result = await RequestLoginAndFollowRedirect(LoginUrl, pairs, null, true, SearchUrl, LandingUrl, true);
await ConfigureIfOK(result.Cookies, result.ContentString?.Contains("logout.php") == true,
() =>
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(result.ContentString);
var errorMessage = dom.QuerySelector(".left_side table:nth-of-type(1) tr:nth-of-type(2)")?.TextContent.Trim().Replace("\n\t", " ");
if (string.IsNullOrWhiteSpace(errorMessage))
errorMessage = dom.QuerySelector("div.notification-body").TextContent.Trim().Replace("\n\t", " ");
throw new ExceptionWithConfigData(errorMessage, configData);
});
await ConfigureIfOK(result.Cookies, result.ContentString?.Contains("logout.php") == true, () =>
{
var parser = new HtmlParser();
var dom = parser.ParseDocument(result.ContentString);
var errorMessage = dom.QuerySelector(".left_side table:nth-of-type(1) tr:nth-of-type(2)")?.TextContent.Trim().Replace("\n\t", " ");
if (string.IsNullOrWhiteSpace(errorMessage))
errorMessage = dom.QuerySelector("div.notification-body")?.TextContent.Trim().Replace("\n\t", " ");
throw new ExceptionWithConfigData(errorMessage ?? "Login failed.", configData);
});
try
{
// Get RSS key
var rssParams = new Dictionary<string, string>
{
{"feedtype", "download"},
{"timezone", "0"},
{"showrows", "50"}
};
{
{ "feedtype", "download" },
{ "timezone", "0" },
{ "showrows", "50" }
};
var rssPage = await RequestWithCookiesAsync(
GetRSSKeyUrl, result.Cookies, RequestType.POST, data: rssParams);
var match = Regex.Match(rssPage.ContentString, "(?<=secret_key\\=)([a-zA-z0-9]*)");
@@ -217,19 +242,24 @@ namespace Jackett.Common.Indexers
IsConfigured = false;
throw;
}
return IndexerConfigurationStatus.RequiresTesting;
}
protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
{
var releases = new List<ReleaseInfo>();
var searchString = query.GetQueryString();
var prevCook = CookieHeader + "";
var searchParams = new Dictionary<string, string> {
var categoryMapping = MapTorznabCapsToTrackers(query);
var searchParams = new Dictionary<string, string>
{
{ "do", "search" },
{ "category", "0" },
{ "include_dead_torrents", "no" }
{ "category", categoryMapping.FirstIfSingleOrDefault("0") }, // multi category search not supported
{ "include_dead_torrents", "yes" },
{ "sort", GetSortBy },
{ "order", GetOrder }
};
if (query.IsImdbQuery)
@@ -243,16 +273,16 @@ namespace Jackett.Common.Indexers
searchParams.Add("search_type", "t_name");
}
var searchPage = await RequestWithCookiesAndRetryAsync(
SearchUrl, CookieHeader, RequestType.POST, null, searchParams);
var searchPage = await RequestWithCookiesAndRetryAsync(SearchUrl, CookieHeader, RequestType.POST, null, searchParams);
// Occasionally the cookies become invalid, login again if that happens
if (searchPage.IsRedirect)
{
await ApplyConfiguration(null);
searchPage = await RequestWithCookiesAndRetryAsync(
SearchUrl, CookieHeader, RequestType.POST, null, searchParams);
searchPage = await RequestWithCookiesAndRetryAsync(SearchUrl, CookieHeader, RequestType.POST, null, searchParams);
}
var releases = new List<ReleaseInfo>();
try
{
var parser = new HtmlParser();
@@ -270,13 +300,13 @@ namespace Jackett.Common.Indexers
release.Guid = new Uri(row.QuerySelector("td:nth-of-type(3) a").GetAttribute("href"));
release.Link = release.Guid;
release.Details = new Uri(qDetails.GetAttribute("href"));
//08-08-2015 12:51
// 08-08-2015 12:51
// requests can be 'Pre Release Time: 25-04-2021 15:00 Uploaded: 3 Weeks, 2 Days, 23 Hours, 53 Minutes, 39 Seconds after Pre'
var dateMatch = _dateMatchRegex.Match(row.QuerySelectorAll("td:nth-of-type(2) div").Last().TextContent.Trim());
var dateMatch = _dateMatchRegex.Match(row.QuerySelector("td:nth-of-type(2) > div:last-child").TextContent.Trim());
if (dateMatch.Success)
release.PublishDate = DateTime.ParseExact(dateMatch.Value
, "dd-MM-yyyy H:mm",
CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
release.PublishDate = DateTime.ParseExact(dateMatch.Value, "dd-MM-yyyy HH:mm", CultureInfo.InvariantCulture);
release.Seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(7)").TextContent);
release.Peers = release.Seeders + ParseUtil.CoerceInt(row.QuerySelector("td:nth-of-type(8)").TextContent.Trim());
release.Size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-of-type(5)").TextContent.Trim());
@@ -285,15 +315,16 @@ namespace Jackett.Common.Indexers
if (qPoster != null)
release.Poster = new Uri(qPoster.GetAttribute("src"));
var cat = row.QuerySelector("td:nth-of-type(1) a").GetAttribute("href");
var catSplit = cat.LastIndexOf('=');
if (catSplit > -1)
cat = cat.Substring(catSplit + 1);
var categoryLink = row.QuerySelector("td:nth-of-type(1) a").GetAttribute("href");
var cat = ParseUtil.GetArgumentFromQueryString(categoryLink, "category");
release.Category = MapTrackerCatToNewznab(cat);
var grabs = row.QuerySelector("td:nth-child(6)").TextContent;
release.Grabs = ParseUtil.CoerceInt(grabs);
var cover = row.QuerySelector("td:nth-of-type(2) > div > img[src]")?.GetAttribute("src")?.Trim();
release.Poster = !string.IsNullOrEmpty(cover) && cover.StartsWith("http") ? new Uri(cover) : null;
if (row.QuerySelector("img[alt^=\"Free Torrent\"]") != null)
release.DownloadVolumeFactor = 0;
else if (row.QuerySelector("img[alt^=\"Silver Torrent\"]") != null)

View File

@@ -5,13 +5,13 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
[ExcludeFromCodeCoverage]
internal class ConfigurationDataFileList : ConfigurationDataUserPasskey
{
public BoolConfigurationItem IncludeRomanianReleases { get; private set; }
public DisplayInfoConfigurationItem CatWarning { get; private set; }
public BoolConfigurationItem Freeleech { get; set; }
public DisplayInfoConfigurationItem CatWarning { get; set; }
public ConfigurationDataFileList()
: base("Note this is <b>not</b> your <i>password</i>.<ul><li>Login to the FileList Website</li><li>Click on the <b>Profile</b> link</li><li>Scroll down to the <b>Reset Passkey</b> section</li><li>Copy the <b>passkey</b>.</li><li>Also be aware of not leaving a trailing blank at the end of the passkey after pasting it here.</li></ul>")
{
IncludeRomanianReleases = new BoolConfigurationItem("IncludeRomanianReleases") { Value = false };
Freeleech = new BoolConfigurationItem("Search freeleech only") { Value = false };
CatWarning = new DisplayInfoConfigurationItem("CatWarning", "When mapping TV ensure you add category 5000 in addition to 5030, 5040.");
}
}

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace Jackett.Common.Models.IndexerConfig.Bespoke
@@ -7,16 +8,30 @@ namespace Jackett.Common.Models.IndexerConfig.Bespoke
{
public StringConfigurationItem MamId { get; private set; }
public DisplayInfoConfigurationItem MamIdHint { get; private set; }
public BoolConfigurationItem ExcludeVip { get; private set; }
public DisplayInfoConfigurationItem Instructions { get; private set; }
public SingleSelectConfigurationItem SearchType { get; private set; }
public BoolConfigurationItem SearchInDescription { get; private set; }
public BoolConfigurationItem SearchInSeries { get; private set; }
public BoolConfigurationItem SearchInFilenames { get; private set; }
public ConfigurationDataMyAnonamouse()
{
MamId = new StringConfigurationItem("mam_id");
MamIdHint = new DisplayInfoConfigurationItem("mam_id instructions", "Go to your <a href=\"https://www.myanonamouse.net/preferences/index.php?view=security\" target=\"_blank\">security preferences</a> and create a new session for the IP used by the Jackett server. Then paste the resulting mam_id value into the mam_id field here.");
ExcludeVip = new BoolConfigurationItem("Exclude VIP torrents");
Instructions = new DisplayInfoConfigurationItem("", "For best results, change the 'Torrents per page' setting to 100 in your Profile => Torrent tab.");
SearchType = new SingleSelectConfigurationItem(
"Search Type",
new Dictionary<string, string>
{
{ "all", "All torrents" },
{ "active", "Only active" },
{ "fl", "Freeleech" },
{ "fl-VIP", "Freeleech or VIP" },
{ "VIP", "VIP torrents" },
{ "nVIP", "Torrents not VIP" },
})
{ Value = "all" };
SearchInDescription = new BoolConfigurationItem("Also search text in the description") { Value = false };
SearchInSeries = new BoolConfigurationItem("Also search text in the series") { Value = false };
SearchInFilenames = new BoolConfigurationItem("Also search text in the filenames") { Value = false };
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Diagnostics.CodeAnalysis;
namespace Jackett.Common.Models.IndexerConfig.Bespoke
{
[ExcludeFromCodeCoverage]
public class ConfigurationDataSpeedCD : ConfigurationDataBasicLogin
{
public BoolConfigurationItem Freeleech { get; set; }
public BoolConfigurationItem ExcludeArchives { get; set; }
public ConfigurationDataSpeedCD(string instructionMessageOptional = null)
: base(instructionMessageOptional)
{
Freeleech = new BoolConfigurationItem("Search freeleech only") { Value = false };
ExcludeArchives = new BoolConfigurationItem("Exclude torrents with RAR files") { Value = false };
}
}
}

View File

@@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using Jackett.Common.Models;
using NUnit.Framework;
namespace Jackett.Test.Common.Indexers.RuTracker
{
[TestFixture]
public class RuTrackerTests
{
[TestCaseSource(typeof(TitleParserTestData), nameof(TitleParserTestData.TestCases))]
public string TestTitleParsing(string title, ICollection<int> category, bool stripCyrillicLetters, bool moveAllTagsToEndOfReleaseTitle, bool moveFirstTagsToEndOfReleaseTitle)
{
var titleParser = new Jackett.Common.Indexers.RuTracker.TitleParser();
return titleParser.Parse(title, category, stripCyrillicLetters, moveAllTagsToEndOfReleaseTitle, moveFirstTagsToEndOfReleaseTitle);
}
}
public class TitleParserTestData
{
public static IEnumerable TestCases
{
get
{
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVSD.ID }, false, false, false).Returns("Терапия / Shrinking / S1E1-2 of 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DL] Dub (Iyuno-SDI Group) + Original + Sub Rus");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / сезон: 5 / Серии: 1-14 из ?? (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVForeign.ID }, false, false, false).Returns("Новичок / Новобранец / The Rookie / S5E1-14 of ?? (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DL] MVO (LostFilm) + Original");
yield return new TestCaseData("Красный яр / Сезон: 1-8 (Михаил Вассербаум) [2022, детектив, WEBRip-AVC]", new List<int> { TorznabCatType.TVOther.ID }, false, false, false).Returns("Красный яр / S1-8 (Михаил Вассербаум) [2022, детектив, WEBRip-AVC]");
yield return new TestCaseData("Просто Михалыч / Эпизод: 1-5 из ХХ (Евгений Корчагин) [2022, комедия, WEBRip 720p]", new List<int> { TorznabCatType.TVHD.ID }, false, false, false).Returns("Просто Михалыч / E1-5 of XX (Евгений Корчагин) [2022, комедия, WEBRip 720p]");
yield return new TestCaseData("Открывай, полиция! / Выпуски: 1,2 (Сергей Гинзбург) [2022, комедия, WEBRip]", new List<int> { TorznabCatType.TV.ID }, false, false, false).Returns("Открывай, полиция! / E1-2 (Сергей Гинзбург) [2022, комедия, WEBRip]");
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVHD.ID }, true, false, false).Returns("Shrinking / S1E1-2 of 10 [2023, WEB-DL] Dub (Iyuno-SDI Group) + Original + Sub Rus");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / сезон: 5 / Серии: 1-14 из ?? (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVForeign.ID }, true, false, false).Returns("The Rookie / S5E1-14 of ?? [2022, WEB-DL] MVO (LostFilm) + Original");
yield return new TestCaseData("Красный яр / Сезон: 1-8 (Михаил Вассербаум) [2022, детектив, WEBRip-AVC]", new List<int> { TorznabCatType.TVOther.ID }, true, false, false).Returns("S1-8 [2022, WEBRip-AVC]");
yield return new TestCaseData("Просто Михалыч / Эпизод: 1-5 из ХХ (Евгений Корчагин) [2022, комедия, WEBRip 720p]", new List<int> { TorznabCatType.TVHD.ID }, true, false, false).Returns("E1-5 of XX [2022, WEBRip 720p]");
yield return new TestCaseData("Открывай, полиция! / Выпуски: 1,2 (Сергей Гинзбург) [2022, комедия, WEBRip]", new List<int> { TorznabCatType.TV.ID }, true, false, false).Returns("E1-2 [2022, WEBRip]");
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / Серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVUHD.ID }, true, false, true).Returns("Shrinking / S1E1-2 of 10 [2023, WEB-DL] Dub (Iyuno-SDI Group) + Original + Sub Rus");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / Сезон: 5 / Серии: 1-14 из ?? (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVSport.ID }, true, false, true).Returns("The Rookie / S5E1-14 of ?? [2022, WEB-DL] MVO (LostFilm) + Original");
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / Серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVAnime.ID }, true, true, false).Returns("Shrinking / S1E1-2 of 10 Dub + Original + Sub Rus (Iyuno-SDI Group) [2023, WEB-DL]");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / Сезон: 5 / Серии: 1-14 из ХХ (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVDocumentary.ID }, true, true, false).Returns("The Rookie / S5E1-14 of XX MVO + Original (LostFilm) [2022, WEB-DL]");
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / Серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVAnime.ID }, true, true, true).Returns("Shrinking / S1E1-2 of 10 Dub + Original + Sub Rus (Iyuno-SDI Group) [2023, WEB-DL]");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / Сезон: 5 / Серии: 1,14 из ?? (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVDocumentary.ID }, true, true, true).Returns("The Rookie / S5E1-14 of ?? MVO + Original (LostFilm) [2022, WEB-DL]");
yield return new TestCaseData("Терапия / Shrinking / Сезон: 1 / Серии: 1-2 из 10 (Джеймс Понсольдт) [2023, США, комедия, WEB-DLRip] Dub (Iyuno-SDI Group) + Original + Sub Rus", new List<int> { TorznabCatType.TVHD.ID }, false, true, false).Returns("Терапия / Shrinking / S1E1-2 of 10 Dub + Original + Sub Rus (Джеймс Понсольдт) (Iyuno-SDI Group) [2023, США, комедия, WEB-DL]");
yield return new TestCaseData("Новичок / Новобранец / The Rookie / Сезон: 5 / Серии: 1,14 из ХХ (Майкл Гои, Билл Роу) [2022, США, боевик, драма, криминал, WEB-DLRip] MVO (LostFilm) + Original", new List<int> { TorznabCatType.TVForeign.ID }, false, true, false).Returns("Новичок / Новобранец / The Rookie / S5E1-14 of XX MVO + Original (Майкл Гои, Билл Роу) (LostFilm) [2022, США, боевик, драма, криминал, WEB-DL]");
}
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
using Jackett.Common.Models;
using NUnit.Framework;
namespace Jackett.Test.Common.Indexers.Toloka
{
[TestFixture]
public class TolokaTests
{
[TestCaseSource(typeof(TitleParserTestData), nameof(TitleParserTestData.TestCases))]
public string TestTitleParsing(string title, ICollection<int> category, bool stripCyrillicLetters)
{
var titleParser = new Jackett.Common.Indexers.Toloka.TitleParser();
return titleParser.Parse(title, category, stripCyrillicLetters);
}
}
public class TitleParserTestData
{
public static IEnumerable TestCases
{
get
{
yield return new TestCaseData("Правдива терапія (Сезон 1, серії 1-2) / Shrinking (Season 1, episodes 1-2) (2023) WEBRip 1080p Ukr/Eng", new List<int> { TorznabCatType.TV.ID }, true).Returns("Shrinking (S1E1-2) (2023) WEBRip 1080p Ukr/Eng (S1E1-2)");
yield return new TestCaseData("Ші-Ра та принцеси могутності (сезон 1-2, серій 14 з 20) / She-Ra and the Princesses of Power (seasons 1-2, episodes 14 of 20) (2018) WEBRip 1080p", new List<int> { TorznabCatType.TVHD.ID }, true).Returns("She-Ra and the Princesses of Power (S1-2, E14 of 20) (2018) WEBRip 1080p (S1-2, E14 of 20)");
yield return new TestCaseData("А інші сгорять у пеклі (Сезон 1, Серія 3) / Everyone Else Burns (Season 1, Episode 3) (2023) WEB-DL 1080p Ukr/Eng | Sub Ukr/Eng", new List<int> { TorznabCatType.TVOther.ID }, true).Returns("Everyone Else Burns (S1E3) (2023) WEB-DL 1080p Ukr/Eng | Sub Ukr/Eng (S1E3)");
yield return new TestCaseData("У тілі (Сезон 2, Епізод 1,2 з ХХ) / In the flesh (Season 2, episodes 1,2 of XX) (2014) 1080p BDRip Eng | sub Ukr", new List<int> { TorznabCatType.TVSport.ID }, true).Returns("In the flesh (S2E1-2 of XX) (2014) 1080p BDRip Eng | sub Ukr (S2E1-2 of XX)");
yield return new TestCaseData("Правдива терапія (Сезон 1, серії 1-2) / Shrinking (Season 1, episodes 1-2) (2023) WEBRip 1080p Ukr/Eng", new List<int> { TorznabCatType.TVHD.ID }, false).Returns("Правдива терапія (S1E1-2) / Shrinking (S1E1-2) (2023) WEBRip 1080p Ukr/Eng");
yield return new TestCaseData("Ші-Ра та принцеси могутності (сезон 1-2, серій 14 з 20) / She-Ra and the Princesses of Power (seasons 1-2, episodes 14 of 20) (2018) WEBRip 1080p", new List<int> { TorznabCatType.TVAnime.ID }, false).Returns("Ші-Ра та принцеси могутності (S1-2, E14 of 20) / She-Ra and the Princesses of Power (S1-2, E14 of 20) (2018) WEBRip 1080p");
yield return new TestCaseData("А інші сгорять у пеклі (Сезон 1, Серія 3) / Everyone Else Burns (Season 1, Episode 3) (2023) WEB-DL 1080p Ukr/Eng | Sub Ukr/Eng", new List<int> { TorznabCatType.TVDocumentary.ID }, false).Returns("А інші сгорять у пеклі (S1E3) / Everyone Else Burns (S1E3) (2023) WEB-DL 1080p Ukr/Eng | Sub Ukr/Eng");
yield return new TestCaseData("У тілі (Сезон 2, Епізод 1,2 з ХХ) / In the flesh (Season 2, episodes 1,2 of XX) (2014) 1080p BDRip Eng | sub Ukr", new List<int> { TorznabCatType.TV.ID }, false).Returns("У тілі (S2E1-2 of XX) / In the flesh (S2E1-2 of XX) (2014) 1080p BDRip Eng | sub Ukr");
}
}
}
}

View File

@@ -256,6 +256,7 @@ namespace Jackett.Updater
"Definitions/01torrent.yml",
"Definitions/24rolika.yml",
"Definitions/32pages.yml",
"Definitions/3evils.yml",
"Definitions/3evils-api.yml",
"Definitions/420files.yml",
"Definitions/7torrents.yml",