Compare commits

...

70 Commits

Author SHA1 Message Date
Weblate
8ac721a30b Translated using Weblate (Chinese (Simplified) (zh_CN))
Currently translated at 96.8% (427 of 441 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN))

Currently translated at 96.3% (425 of 441 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN))

Currently translated at 96.3% (425 of 441 strings)

Translated using Weblate (French)

Currently translated at 100.0% (441 of 441 strings)

Co-authored-by: Nackophilz <clement.wigy@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: muihiuwev <muihiuwev@outlook.com>
Co-authored-by: yulelong <yulelong@foxmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/zh_CN/
Translation: Servarr/Prowlarr
2021-10-23 10:31:57 -05:00
bakerboy448
3bbadb516d New: (DesiTorrents) Convert from Gazelle to UNIT3D
Fixes #532
2021-10-17 14:00:57 -05:00
Qstick
f5f0dd6fae New: Support server notifications 2021-10-17 13:10:06 -05:00
Qstick
0cfb7da411 New: (Indexer) BitHDTV 2021-10-17 12:27:23 -05:00
Qstick
b65f4205fc Fixed: Don't delete down indexers if they still exist in DB
Fixes #494
2021-10-17 11:58:48 -05:00
Qstick
3e243eafdd Bump FluentMigrator to 3.3.1 2021-10-16 12:49:55 -05:00
Qstick
327fd08059 New: (RuTracker) Search by Categories 2021-10-15 20:04:59 -05:00
Qstick
827741db17 Fixed: (TVVault) add delay between requests and fix search & download 2021-10-15 20:01:39 -05:00
bakerboy448
c21e323992 New: Improved Logging of Search for ID based searches 2021-10-11 11:00:40 -05:00
Shane M
e49d03ab7b Fixed: (RevolutionTT) Remove [REQ] prefix from torrent title (#545)
[REQ] is an automatically generated prefix added to fulfilled requests, and very often breaks parsing for title matching

Co-authored-by: Shane Moore <vales@users.noreply.github.com>
2021-10-10 19:14:03 -05:00
Nyuels
4347e1cf7a New: (Internet Archive) Add Torrent File Only option. (#459) 2021-10-10 17:21:39 -05:00
Qstick
d7e1043b79 Fixed: (Cardigann) Info field text pulling from DB instead of definition 2021-10-10 17:12:46 -05:00
Weblate
d4bdb73b7c Translated using Weblate (French)
Currently translated at 100.0% (441 of 441 strings)

Co-authored-by: Nackophilz <clement.wigy@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translation: Servarr/Prowlarr
2021-10-10 17:00:36 -05:00
Dmitry Chepurovskiy
eeebf3ecf0 Fixed: (Anilibria) Fix download torrent link (#529) 2021-10-10 16:58:59 -05:00
Qstick
76d73aa6a9 New: (Indexer) SpeedCD 2021-10-10 14:23:42 -05:00
Qstick
5dfe530cf3 Fixed: (BB) Detect when re-auth needed 2021-10-10 14:06:30 -05:00
Qstick
8b8b5ba1c8 Cleanup cardigann logging 2021-10-10 13:04:12 -05:00
bakerboy448
c5caf22375 New: Support for Prowlarr Definitions v2
New: Support for Updated yml Definitions
Fixes: #298
2021-10-10 13:04:12 -05:00
Qstick
293b32ea0e New: Improve size and number parsing 2021-10-10 13:04:12 -05:00
Qstick
25bb10d62b New: (Cardigann) AllowRawSearch Property 2021-10-10 13:04:12 -05:00
bakerboy448
9eba50d9db New: Log which DB is being migrated
(cherry picked from Sonarr commit 07c95f06d3b9b32aaeb923d4c678f10a2bf1141a)
2021-10-09 09:42:19 -05:00
Qstick
234995cbaf Rename indexer proxied HTTP methods for clarification 2021-10-06 19:33:47 -05:00
Qstick
918071903b New: Raw search engine support in caps 2021-10-06 19:32:22 -05:00
bakerboy448
dcfa3ad48e Update contributing [skip ci] (#528)
* update contributing [skip ci]

sonarr pulls / *arr pulls

* nuke contributing => wiki [skip ci]

* fix wiki link [skip ci]

* fixup! fix wiki link [skip ci] (lint)
2021-10-06 19:08:14 -05:00
dobbleg1000
5dd6cde61a Fixed: (Indexer) Changed BakaBT to default to SFW content
Update Bakabt for Adult Content Bool and change link
2021-10-06 19:07:48 -05:00
Weblate
d18ddcaa50 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/
Translation: Servarr/Prowlarr
2021-10-06 17:58:02 -05:00
bakerboy448
0cca9525a1 Fixed: (Indexers) Download requests not using the configured proxy
Fixes #520
2021-10-06 17:30:37 -05:00
Servarr
3455f3c92a Translations from Weblate
Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Arabic)

Currently translated at 81.6% (360 of 441 strings)

Translated using Weblate (Russian)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Icelandic)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Danish)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Slovak)

Currently translated at 10.6% (47 of 441 strings)

Translated using Weblate (German)

Currently translated at 95.4% (421 of 441 strings)

Translated using Weblate (Korean)

Currently translated at 60.3% (266 of 441 strings)

Translated using Weblate (Japanese)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Catalan)

Currently translated at 2.4% (11 of 441 strings)

Translated using Weblate (Bulgarian)

Currently translated at 75.7% (334 of 441 strings)

Translated using Weblate (Thai)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Romanian)

Currently translated at 81.8% (361 of 441 strings)

Translated using Weblate (Italian)

Currently translated at 85.7% (378 of 441 strings)

Translated using Weblate (Hindi)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Spanish)

Currently translated at 83.2% (367 of 441 strings)

Translated using Weblate (Hebrew)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Portuguese)

Currently translated at 84.1% (371 of 441 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 1.1% (5 of 441 strings)

Translated using Weblate (Swedish)

Currently translated at 81.8% (361 of 441 strings)

Translated using Weblate (Czech)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Greek)

Currently translated at 81.1% (358 of 441 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 12.4% (55 of 441 strings)

Translated using Weblate (Turkish)

Currently translated at 81.6% (360 of 441 strings)

Translated using Weblate (Vietnamese)

Currently translated at 81.4% (359 of 441 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN))

Currently translated at 90.9% (401 of 441 strings)

Translated using Weblate (Polish)

Currently translated at 81.4% (359 of 441 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/zh_Hans/
Translation: Servarr/Prowlarr

Co-authored-by: Weblate <noreply@weblate.org>
2021-10-05 08:34:23 -05:00
Weblate
af03c17892 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/
Translation: Servarr/Prowlarr
2021-10-02 11:18:49 -04:00
Weblate
6b39fa5ce6 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/
Translation: Servarr/Prowlarr
2021-10-02 10:36:09 -04:00
Servarr
1ee79f16fc Translated using Weblate (Turkish) (#516)
Currently translated at 16.5% (73 of 441 strings)

Translated using Weblate (French)

Currently translated at 99.7% (440 of 441 strings)

Co-authored-by: Francis Peixoto <peixoto.francis@gmail.com>
Co-authored-by: Micky <ad312@pm.me>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/tr/
Translation: Servarr/Prowlarr

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Francis Peixoto <peixoto.francis@gmail.com>
Co-authored-by: Micky <ad312@pm.me>
2021-10-01 15:17:59 -05:00
bakerboy448
e73f2466cc Fixed (Headphones): Add missing description 2021-10-01 15:17:35 -05:00
Weblate
2e56b7681e Translated using Weblate (Dutch)
Currently translated at 100.0% (441 of 441 strings)

Translated using Weblate (Bulgarian)

Currently translated at 10.2% (45 of 441 strings)

Translated using Weblate (Korean)

Currently translated at 17.9% (79 of 441 strings)

Translated using Weblate (Spanish)

Currently translated at 76.1% (336 of 441 strings)

Co-authored-by: COTMO <moermantom1@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: eslifos <eaaf_01@hotmail.com>
Co-authored-by: keysuck <joshkkim@gmail.com>
Co-authored-by: siankatabg <siankata91@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/nl/
Translation: Servarr/Prowlarr
2021-09-25 22:08:27 -05:00
Jayson Reis
87650c83c6 New: (Indexer) lat-team.com (#505)
* New: (Indexer) Torrent lat-team.com

* Add lat-team to ignore definitions list

* Fix lat-team name
2021-09-25 22:03:42 -05:00
Qstick
34a09af01e New: Advanced settings for Application category control 2021-09-25 09:46:31 -04:00
Qstick
5a3d429d52 Remove unused MovieMonitor input 2021-09-25 09:46:31 -04:00
Qstick
40c49bce9b Fixed: Spinning of Test All Indexer icon
#507
2021-09-23 20:42:17 -05:00
Qstick
063083a1f1 Fixed: Search from header on Search Page
Fixes #511
2021-09-23 20:28:22 -05:00
bakerboy448
dbbc913809 New: (InternetArchive) Add Support for TV Categories
ref API docs `movies: any item where the main media content is video files, like mpeg, mov, avi, etc`
2021-09-19 08:47:59 -05:00
Qstick
f0f2c88c4a Fixed: (qBittorrent) Don't fail if remove torrents is enabled
We don't care about this in Prowlarr

Fixes #499
2021-09-15 22:04:09 -05:00
Weblate
1bfcb99f31 Translated using Weblate (Slovak)
Currently translated at 7.4% (33 of 441 strings)

Added translation using Weblate (Slovak)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 9.2% (41 of 441 strings)

Added translation using Weblate (Norwegian Bokmål)

Translated using Weblate (Portuguese)

Currently translated at 83.6% (369 of 441 strings)

Translated using Weblate (Dutch)

Currently translated at 78.9% (348 of 441 strings)

Co-authored-by: 7even <henning@wikene.no>
Co-authored-by: Samuel Bartík <github.fundal@aleeas.com>
Co-authored-by: Stevie Robinson <stevie.robinson@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: sergioquiterio <serquiterio@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/sk/
Translation: Servarr/Prowlarr
2021-09-15 21:57:50 -05:00
bakerboy448
4ea0e6c016 add various missing descriptions 2021-09-15 21:57:26 -05:00
bakerboy448
1de845c8f5 (indexers) various - standardized language format with {ISO 639-1}-{ISO 3166-1 alpha-2} 2021-09-15 21:57:26 -05:00
bakerboy448
f3a33cf817 Fixed: Missing Proxy Validation Translations 2021-09-15 21:43:51 -05:00
bakerboy448
593a0e9658 Fixed: (MAM) Don't Parse HTTP Error Responses 2021-09-07 16:03:34 -05:00
Servarr
a854ce6f4e Translated using Weblate (Portuguese (Brazil)) (#484)
Currently translated at 100.0% (441 of 441 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN))

Currently translated at 62.5% (276 of 441 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (441 of 441 strings)

Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: angelsky11 <angelsky11@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/zh_CN/
Translation: Servarr/Prowlarr

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: angelsky11 <angelsky11@gmail.com>
2021-09-03 14:33:08 -05:00
bakerboy448
043b1a0e46 Fixed: Better Log Cleansing
Ref #280 YGG Comment
2021-08-31 19:51:37 -04:00
bakerboy448
4c7c7e8a62 Fixed: (BTN) Daily Episode Searches failing
Fixes #480
2021-08-31 19:50:46 -04:00
Qstick
89a4c03dd2 Fixed: Translations for Tags setting page
Fixes #471
2021-08-30 22:48:24 -04:00
Qstick
baed2960b6 Fixed: (TorrentSeeds) new Login
Fixes #460
2021-08-30 22:39:05 -04:00
Qstick
e4ef1c3af0 Fixed: Convert DesiTorrents to Gazelle
Fixes #220
2021-08-30 22:20:40 -04:00
bakerboy448
b4f8fb733f Fixed: Indexer Display Issue on Search Page
Fixes #177
2021-08-30 20:47:46 -04:00
bakerboy448
1a6ea21b9f Fixed: Increased Xthor Rate Limit Time
Fixes Logs of #472

> 2021-08-28 05:59:47.8|Error|Xthor|An error occurred while processing indexer feed. https://api.xthor.tk?passkey=(removed)&category=12%2B125%2B104%2B13%2B15%2B14%2B98%2B17%2B16%2B101%2B32%2B110%2B123%2B109%2B34%2B30&accent=1

[v0.1.1.875] System.Exception: Triggered AntiSpam Protection, please delay your requests!
   at NzbDrone.Core.Indexers.Definitions.Xthor.XthorParser.CheckApiState(XthorError state) in D:\a\1\s\src\NzbDrone.Core\Indexers\Definitions\Xthor\XthorParser.cs:line 112
   at NzbDrone.Core.Indexers.Definitions.Xthor.XthorParser.ParseResponse(IndexerResponse indexerResponse) in D:\a\1\s\src\NzbDrone.Core\Indexers\Definitions\Xthor\XthorParser.cs:line 32
   at NzbDrone.Core.Indexers.HttpIndexerBase`1.FetchPage(IndexerRequest request, IParseIndexerResponse parser) in D:\a\1\s\src\NzbDrone.Core\Indexers\HttpIndexerBase.cs:line 321
   at NzbDrone.Core.Indexers.HttpIndexerBase`1.FetchReleases(Func`2 pageableRequestChainSelector, Boolean isRecent) in D:\a\1\s\src\NzbDrone.Core\Indexers\HttpIndexerBase.cs:line 174
RequestUri: https://api.xthor.tk?passkey=(removed)&category=12%2B125%2B104%2B13%2B15%2B14%2B98%2B17%2B16%2B101%2B32%2B110%2B123%2B109%2B34%2B30&accent=1;StatusCode: OK;ContentType: application/json;ContentLength: 133;ContentSample: {
    "error": {
        "code": 8,
        "descr": "Protection Anti-Spam: A.P.I limitee a 1 requete toutes les 2 secondes."
    }
};FeedUrl: https://api.xthor.tk?passkey=(removed)&category=12%2B125%2B104%2B13%2B15%2B14%2B98%2B17%2B16%2B101%2B32%2B110%2B123%2B109%2B34%2B30&accent=1
2021-08-30 19:58:48 -04:00
bakerboy448
16834e0f24 Fixed: (TPB) No Results returned for Torznab searches
Fixed: (TPB) Missing Magnet Download Link
2021-08-30 19:43:20 -04:00
Qstick
658724b315 OpenAPI auto generation test 2021-08-29 23:12:59 -04:00
bakerboy448
a2c8cec27e bug template fix [skip ci] [common] 2021-08-27 20:46:58 -05:00
Nyuels
3c9fbeabaa Fixed: Use Gazelle freelech tokens. (#465) 2021-08-27 18:21:56 -04:00
Weblate
04e84f3a90 Translated using Weblate (Hungarian)
Currently translated at 100.0% (438 of 438 strings)

Translated using Weblate (French)

Currently translated at 97.7% (428 of 438 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (438 of 438 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/hu/
Translation: Servarr/Prowlarr
2021-08-25 23:23:36 -04:00
Steve Adams
77a76fe5a1 New: HDBits to parse IMDB using parser utils (#454) 2021-08-25 23:22:04 -04:00
ta264
1d20b9d429 Fixed: Don't delete tags used by indexer proxies 2021-08-23 22:12:40 +01:00
bakerboy448
46e1cce632 New: (TorrentLeech) Add VIP Expiration
Closes #419
bonus- sort en.json
2021-08-23 22:20:41 -05:00
Qstick
03f821f484 New: Make VIP Check Generic 2021-08-23 22:35:28 -04:00
Qstick
c72222a696 Mylar code cleanup 2021-08-22 22:24:46 -04:00
bakerboy448
f4cee1d5f4 Fixed: UserAgent Parsing
Fixes #448

New - add user agent parser tests
2021-08-22 15:00:59 -05:00
Qstick
ab7bc85368 fix mass delete 2021-08-22 15:53:30 -04:00
bakerboy448
d50e1d7cc0 update readme [skip ci] 2021-08-22 10:25:37 -05:00
Qstick
ab1545e834 Fixed: Mass Indexer delete fails with 415 2021-08-22 11:04:00 -04:00
Weblate
c8cc48229c Translated using Weblate (French)
Currently translated at 97.9% (429 of 438 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (438 of 438 strings)

Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: ProBatou <baptiste2105@hotmail.fr>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
Translation: Servarr/Prowlarr
2021-08-21 23:00:33 -04:00
Steve Adams
b513fac2f7 Fixed: (HDBits) Not parsing the search term for TV (#444)
* Fixing HDBits not parsing the search term

* remove debug vars

* Handle TVDB Properly
2021-08-21 22:59:53 -04:00
Qstick
368e0755a0 Update README.md
[skip ci]
2021-08-21 22:56:50 -04:00
161 changed files with 14786 additions and 6777 deletions

View File

@@ -68,6 +68,7 @@ body:
description: |
Trace Logs (https://wiki.servarr.com/prowlarr/troubleshooting#logging-and-log-files)
Links? References? Anything that will give us more context about the issue you are encountering!
***Generally speaking, all bug reports must have trace logs provided.***
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:

View File

@@ -1,55 +1,13 @@
# How to Contribute #
# How to Contribute
We're always looking for people to help make Prowlarr even better, there are a number of ways to contribute.
This file is updated on an ad-hoc basis, for the latest details please see the [contributing wiki page](https://wiki.servarr.com/prowlarr/contributing).
This file has been moved to the wiki for the latest details please see the [contributing wiki page](https://wiki.servarr.com/prowlarr/contributing).
## Documentation ##
Setup guides, FAQ, the more information we have on the [wiki](https://wiki.servarr.com/prowlarr) the better.
## Documentation
## Development ##
Setup guides, [FAQ](https://wiki.servarr.com/prowlarr/faq), the more information we have on the [wiki](https://wiki.servarr.com/prowlarr) the better.
### Tools required ###
- Visual Studio 2019 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
- HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc)
- [Git](https://git-scm.com/downloads)
- [NodeJS](https://nodejs.org/en/download/) (Node 12.X.X or higher)
- [Yarn](https://yarnpkg.com/)
- .NET Core 5.0.
## Development
### Getting started ###
1. Fork Prowlarr
2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github)
3. Install the required Node Packages `yarn install`
4. Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
5. Build the project in Visual Studio, Setting startup project to `Prowlarr.Console` and framework to `net5.0`
6. Debug the project in Visual Studio
7. Open http://localhost:9696
### Contributing Code ###
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Prowlarr/Prowlarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
- Rebase from Prowlarr's develop branch, don't merge
- Make meaningful commits, or squash them
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
- Reach out to us on the discord if you have any questions
- Add tests (unit/integration)
- Commit with *nix line endings for consistency (We checkout Windows and commit *nix)
- One feature/bug fix per pull request to keep things clean and easy to understand
- Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge)
### Contributing Indexers ###
- If you're contributing an indexer please phrase your commit as something like: `New: (Indexer) {Indexer Name}`, `New: (Indexer) {Usenet|Torrent} {Indexer Name}`, `New: (Indexer) {Torznab|Newznab} {Indexer Name}`
- If you're updating an indexer please phrase your commit as something like: `Fixed: (Indexer) {Indexer Name} {changes}` e.g. `Fixed: (Indexer) Changed BHD to use API`
### Pull Requesting ###
- Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it
- You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability
- We'll try to respond to pull requests as soon as possible, if its been a day or two, please reach out to us, we may have missed it
- Each PR should come from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork, it should have a meaningful branch name (what is being added/fixed)
- new-feature (Good)
- fix-bug (Good)
- patch (Bad)
- develop (Bad)
If you have any questions about any of this, please let us know.
See the [Wiki Page](https://wiki.servarr.com/prowlarr/contributing)

View File

@@ -7,7 +7,7 @@
[![Backers on Open Collective](https://opencollective.com/Prowlarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Prowlarr/sponsors/badge.svg)](#sponsors)
Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all).
Prowlarr is an indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports management of both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Lidarr, Mylar3, Radarr, Readarr, and Sonarr offering complete management of your indexers with no per app Indexer setup required (we do it all).
## Major Features Include:
- Usenet support for 24 indexers natively, including Headphones VIP, and support for any Newznab compatible indexer via "Generic Newznab"
@@ -36,9 +36,13 @@ Note: Prowlarr is currently early in life, thus bugs should be expected
- Request or vote on an existing request for a new tracker/indexer
## Contributors & Developers
This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md).
<a href="https://github.com/Prowlarr/Prowlarr/graphs/contributors"><img src="https://opencollective.com/Prowlarr/contributors.svg?width=890&button=false" /></a>
- [Contribute (GitHub)](CONTRIBUTING.md)
- [Contribution (Wiki Article)](https://wiki.servarr.com/prowlarr/contributing)
- [YML Indexer Defintion (Wiki Article)](https://wiki.servarr.com/prowlarr/cardigann-yml-definition)
This project exists thanks to all the people who contribute.
<a href="https://github.com/Prowlarr/Prowlarr/graphs/contributors"><img src="https://opencollective.com/Prowlarr/contributors.svg?width=890&button=false" /></a>
## Backers

View File

@@ -16,7 +16,6 @@ import FormInputHelpText from './FormInputHelpText';
import IndexerFlagsSelectInputConnector from './IndexerFlagsSelectInputConnector';
import InfoInput from './InfoInput';
import KeyValueListInput from './KeyValueListInput';
import MovieMonitoredSelectInput from './MovieMonitoredSelectInput';
import NumberInput from './NumberInput';
import OAuthInputConnector from './OAuthInputConnector';
import PasswordInput from './PasswordInput';
@@ -69,9 +68,6 @@ function getComponent(type) {
case inputTypes.PATH:
return PathInputConnector;
case inputTypes.MOVIE_MONITORED_SELECT:
return MovieMonitoredSelectInput;
case inputTypes.INDEXER_FLAGS_SELECT:
return IndexerFlagsSelectInputConnector;

View File

@@ -1,52 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import SelectInput from './SelectInput';
const monitorTypesOptions = [
{ key: 'true', value: 'True' },
{ key: 'false', value: 'False' }
];
function MovieMonitoredSelectInput(props) {
const values = [...monitorTypesOptions];
const {
includeNoChange,
includeMixed
} = props;
if (includeNoChange) {
values.unshift({
key: 'noChange',
value: 'No Change',
disabled: true
});
}
if (includeMixed) {
values.unshift({
key: 'mixed',
value: '(Mixed)',
disabled: true
});
}
return (
<SelectInput
{...props}
values={values}
/>
);
}
MovieMonitoredSelectInput.propTypes = {
includeNoChange: PropTypes.bool.isRequired,
includeMixed: PropTypes.bool.isRequired
};
MovieMonitoredSelectInput.defaultProps = {
includeNoChange: false,
includeMixed: false
};
export default MovieMonitoredSelectInput;

View File

@@ -53,7 +53,8 @@ function getSelectValues(selectOptions) {
result.push({
key: option.value,
value: option.name,
hint: option.hint
hint: option.hint,
parentKey: option.parentValue
});
return result;

View File

@@ -60,7 +60,7 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) {
return {
onGoToAddNewMovie(query) {
dispatch(setSearchDefault({ searchQuery: query, searchIndexerIds: [-1, -2] }));
dispatch(setSearchDefault({ searchQuery: query }));
dispatch(push(`${window.Prowlarr.urlBase}/search`));
}
};

View File

@@ -168,9 +168,9 @@ class SignalRConnector extends Component {
this.props.dispatchFetchIndexerStatus();
}
handleMovie = (body) => {
handleIndexer = (body) => {
const action = body.action;
const section = 'movies';
const section = 'indexers';
if (action === 'updated') {
this.props.dispatchUpdateItem({ section, ...body.resource });

View File

@@ -270,6 +270,7 @@ class IndexerIndex extends Component {
isSaving,
saveError,
isDeleting,
isTestingAll,
deleteError,
onScroll,
onSortSelect,
@@ -310,7 +311,7 @@ class IndexerIndex extends Component {
<PageToolbarButton
label={'Test All Indexers'}
iconName={icons.TEST}
spinningName={icons.TEST}
isSpinning={isTestingAll}
isDisabled={hasNoIndexer}
onPress={this.props.onTestAllPress}
/>
@@ -489,6 +490,7 @@ IndexerIndex.propTypes = {
isSaving: PropTypes.bool.isRequired,
saveError: PropTypes.object,
isDeleting: PropTypes.bool.isRequired,
isTestingAll: PropTypes.bool.isRequired,
deleteError: PropTypes.object,
onSortSelect: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired,

View File

@@ -52,6 +52,7 @@ class SearchFooter extends Component {
isFetching,
defaultIndexerIds,
defaultCategories,
defaultSearchQuery,
searchError
} = this.props;
@@ -62,6 +63,10 @@ class SearchFooter extends Component {
const newState = {};
if (defaultSearchQuery && defaultSearchQuery !== prevProps.defaultSearchQuery) {
newState.searchQuery = defaultSearchQuery;
}
if (searchIndexerIds !== defaultIndexerIds) {
newState.searchIndexerIds = defaultIndexerIds;
}

View File

@@ -85,21 +85,21 @@ class Tag extends Component {
{
!!indexerIds.length &&
<div>
{indexerIds.length} indexer{indexerIds.length > 1 && 's'}
{indexerIds.length} {indexerIds.length > 1 ? translate('Indexers') : translate('Indexer')}
</div>
}
{
!!notificationIds.length &&
<div>
{notificationIds.length} connection{notificationIds.length > 1 && 's'}
{notificationIds.length} {notificationIds.length > 1 ? translate('Notifications') : translate('Notification')}
</div>
}
{
!!indexerProxyIds.length &&
<div>
{indexerProxyIds.length} indexerProxy{indexerProxyIds.length > 1 && 's'}
{indexerProxyIds.length} {indexerProxyIds.length > 1 ? translate('IndexerProxies') : translate('IndexerProxy')}
</div>
}
</div>
@@ -108,7 +108,7 @@ class Tag extends Component {
{
!isTagUsed &&
<div>
No links
{translate('NoLinks')}
</div>
}

View File

@@ -7,18 +7,6 @@ function isRelative(ajaxOptions) {
return !absUrlRegex.test(ajaxOptions.url);
}
function moveBodyToQuery(ajaxOptions) {
if (ajaxOptions.data && ajaxOptions.type === 'DELETE') {
if (ajaxOptions.url.contains('?')) {
ajaxOptions.url += '&';
} else {
ajaxOptions.url += '?';
}
ajaxOptions.url += $.param(ajaxOptions.data);
delete ajaxOptions.data;
}
}
function addRootUrl(ajaxOptions) {
ajaxOptions.url = apiRoot + ajaxOptions.url;
}
@@ -32,7 +20,7 @@ function addContentType(ajaxOptions) {
if (
ajaxOptions.contentType == null &&
ajaxOptions.dataType === 'json' &&
(ajaxOptions.method === 'PUT' || ajaxOptions.method === 'POST')) {
(ajaxOptions.method === 'PUT' || ajaxOptions.method === 'POST' || ajaxOptions.method === 'DELETE')) {
ajaxOptions.contentType = 'application/json';
}
}
@@ -52,7 +40,6 @@ export default function createAjaxRequest(originalAjaxOptions) {
const ajaxOptions = { dataType: 'json', ...originalAjaxOptions };
if (isRelative(ajaxOptions)) {
moveBodyToQuery(ajaxOptions);
addRootUrl(ajaxOptions);
addApiKey(ajaxOptions);
addContentType(ajaxOptions);

View File

@@ -3,7 +3,6 @@
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="FluentMigrator" value="https://pkgs.dev.azure.com/fluentmigrator/fluentmigrator/_packaging/fluentmigrator/nuget/v3/index.json" />
<add key="dotnet-bsd-crossbuild" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/dotnet-bsd-crossbuild/nuget/v3/index.json" />
<add key="Mono.Posix.NETStandard" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/Mono.Posix.NETStandard/nuget/v3/index.json" />
<add key="SQLite" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/SQLite/nuget/v3/index.json" />

View File

@@ -0,0 +1,24 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test.Http
{
[TestFixture]
public class UserAgentParserFixture : TestBase
{
// Ref *Arr `_userAgent = $"{BuildInfo.AppName}/{BuildInfo.Version} ({osName} {osVersion})";`
// Ref Mylar `Mylar3/' +str(hash) +'(' +vers +') +http://www.github.com/mylar3/mylar3/`
[TestCase("Mylar3/ 3ee23rh23irqfq (13123123) http://www.github.com/mylar3/mylar3/", "Mylar3")]
[TestCase("Lidarr/1.0.0.2300 (ubuntu 20.04)", "Lidarr")]
[TestCase("Radarr/1.0.0.2300 (ubuntu 20.04)", "Radarr")]
[TestCase("Readarr/1.0.0.2300 (ubuntu 20.04)", "Readarr")]
[TestCase("Sonarr/3.0.6.9999 (ubuntu 20.04)", "Sonarr")]
[TestCase("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", "Other")]
public void should_parse_user_agent(string userAgent, string parsedAgent)
{
UserAgentParser.ParseSource(userAgent).Should().Be(parsedAgent);
}
}
}

View File

@@ -22,6 +22,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@" var authkey = ""2b51db35e1910123321025a12b9933d2"";")]
[TestCase(@"https://hd-space.org/index.php?page=login: uid=mySecret&pwd=mySecret")]
[TestCase(@"https://beyond-hd.me/api/torrents/2b51db35e1912ffc138825a12b9933d2")]
[TestCase(@"Req: [POST] https://www3.yggtorrent.nz/user/login: id=mySecret&pass=mySecret&ci_csrf_token=2b51db35e1912ffc138825a12b9933d2")]
// NzbGet
[TestCase(@"{ ""Name"" : ""ControlUsername"", ""Value"" : ""mySecret"" }, { ""Name"" : ""ControlPassword"", ""Value"" : ""mySecret"" }, ")]

View File

@@ -160,5 +160,16 @@ namespace NzbDrone.Common.Extensions
{
return new HashSet<T>(source, comparer);
}
public static T FirstIfSingleOrDefault<T>(this IEnumerable<T> source, T replace = default)
{
if (source is ICollection<T> collection)
{
return collection.Count == 1 ? collection.First() : replace;
}
var test = source.Take(2).ToList();
return test.Count == 1 ? test[0] : replace;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
{
public static class UserAgentParser
{
private static readonly Regex AppSourceRegex = new Regex(@"(?<agent>.*)\/.*(\(.*\))?",
private static readonly Regex AppSourceRegex = new Regex(@"(?<agent>[a-z0-9]*)\/.*(?:\(.*\))?",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static string SimplifyUserAgent(string userAgent)

View File

@@ -11,8 +11,8 @@ namespace NzbDrone.Common.Instrumentation
private static readonly Regex[] CleansingRules = new[]
{
// Url
new Regex(@"(?<=\?|&|: |;)(apikey|token|passkey|auth|authkey|user|uid|api|[a-z_]*apikey|account|passwd|pwd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=\?|&| )[^=]*?(_?(?<!use)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?&: ;])(apikey|token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?& ])[^=]*?(_?(?<!use)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"rss\.torrentleech\.org/(?!rss)(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"rss\.torrentleech\.org/rss/download/[0-9]+/(?<secret>[0-9a-z]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),

View File

@@ -19,6 +19,10 @@ namespace NzbDrone.Core.Test.HealthCheck
Mocker.SetConstant<IEnumerable<IProvideHealthCheck>>(new[] { _healthCheck });
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
Mocker.GetMock<IServerSideNotificationService>()
.Setup(v => v.GetServerChecks())
.Returns(new List<Core.HealthCheck.HealthCheck>());
}
[Test]

View File

@@ -2,6 +2,8 @@ using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.IndexerProxies;
using NzbDrone.Core.IndexerProxies.Http;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Test.Framework;
@@ -22,5 +24,30 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_used_tags()
{
var tags = Builder<Tag>
.CreateListOfSize(2)
.All()
.With(x => x.Id = 0)
.BuildList();
Db.InsertMany(tags);
var settings = Builder<HttpSettings>.CreateNew().Build();
var restrictions = Builder<IndexerProxyDefinition>.CreateListOfSize(2)
.All()
.With(x => x.Id = 0)
.With(x => x.Settings = settings)
.With(v => v.Tags.Add(tags[0].Id))
.BuildList();
Db.InsertMany(restrictions);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
}
}
}

View File

@@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
var recentFeed = ReadAllText(@"Files/Indexers/PrivateHD/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;

View File

@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
var recentFeed = ReadAllText(@"Files/Indexers/FileList/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;

View File

@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
var responseJson = ReadAllText(fileName);
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.POST), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.POST), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), responseJson)));
var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases;
@@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
var responseJson = new { status = 5, message = "Invalid authentication credentials" }.ToJson();
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.IsAny<HttpRequest>(), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.IsAny<HttpRequest>(), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), Encoding.UTF8.GetBytes(responseJson))));
var torrents = (await Subject.Fetch(_movieSearchCriteria)).Releases;

View File

@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
private void GivenCapsResponse(string caps)
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.Execute(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()))
.Setup(o => o.ExecuteProxied(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()))
.Returns<HttpRequest, IndexerDefinition>((r, d) => new HttpResponse(r, new HttpHeader(), new CookieCollection(), caps));
}
@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
Subject.GetCapabilities(_settings, _definition);
Mocker.GetMock<IIndexerHttpClient>()
.Verify(o => o.Execute(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()), Times.Once());
.Verify(o => o.ExecuteProxied(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()), Times.Once());
}
[Test]
@@ -85,7 +85,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
public void should_throw_if_failed_to_get()
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.Execute(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()))
.Setup(o => o.ExecuteProxied(It.IsAny<HttpRequest>(), It.IsAny<IndexerDefinition>()))
.Throws<Exception>();
Assert.Throws<Exception>(() => Subject.GetCapabilities(_settings, _definition));

View File

@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 }, Limit = 100, Offset = 0 })).Releases;

View File

@@ -37,11 +37,11 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
var responseJson = ReadAllText(fileName);
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.POST), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.POST), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), authStream.ToString())));
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader { ContentType = HttpAccept.Json.Value }, new CookieCollection(), responseJson)));
var torrents = (await Subject.Fetch(new MovieSearchCriteria())).Releases;

View File

@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
var recentFeed = ReadAllText(@"Files/Indexers/Rarbg/RecentFeed_v2.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;
@@ -65,7 +65,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
public async Task should_parse_error_20_as_empty_results()
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 20, error: \"some message\" }")));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;
@@ -77,7 +77,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
public async Task should_warn_on_unknown_error()
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), "{ error_code: 25, error: \"some message\" }")));
var releases = (await Subject.Fetch(new MovieSearchCriteria { Categories = new int[] { 2000 } })).Releases;

View File

@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases;
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases;
@@ -103,7 +103,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Setup(o => o.ExecuteProxiedAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET), Subject.Definition))
.Returns<HttpRequest, IndexerDefinition>((r, d) => Task.FromResult(new HttpResponse(r, new HttpHeader(), new CookieCollection(), recentFeed)));
var releases = (await Subject.Fetch(new MovieSearchCriteria())).Releases;

View File

@@ -0,0 +1,24 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class ParseUtilFixture : CoreTest
{
[TestCase("1023.4 KB", 1047961)]
[TestCase("1023.4 MB", 1073112704)]
[TestCase("1,023.4 MB", 1073112704)]
[TestCase("1.023,4 MB", 1073112704)]
[TestCase("1 023,4 MB", 1073112704)]
[TestCase("1.023.4 MB", 1073112704)]
[TestCase("1023.4 GB", 1098867408896)]
[TestCase("1023.4 TB", 1125240226709504)]
public void should_parse_size(string stringSize, long size)
{
ParseUtil.GetBytes(stringSize).Should().Be(size);
}
}
}

View File

@@ -6,5 +6,6 @@ namespace NzbDrone.Core.Annotations
public string Name { get; set; }
public int Order { get; set; }
public string Hint { get; set; }
public int? ParentValue { get; set; }
}
}

View File

@@ -159,9 +159,11 @@ namespace NzbDrone.Core.Applications
if (removeRemote)
{
var allIndexers = _indexerFactory.All();
foreach (var mapping in indexerMappings)
{
if (!indexers.Any(x => x.Id == mapping.IndexerId))
if (!allIndexers.Any(x => x.Id == mapping.IndexerId))
{
_logger.Info("Indexer with the ID {0} was found within {1} but is no longer defined within Prowlarr, this is being removed.", mapping.IndexerId, app.Name);
ExecuteAction(a => a.RemoveIndexer(mapping.IndexerId), app);

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Applications.Lidarr
@@ -26,8 +27,6 @@ namespace NzbDrone.Core.Applications.Lidarr
SyncCategories = new[] { 3000, 3010, 3030, 3040, 3050, 3060 };
}
public IEnumerable<int> SyncCategories { get; set; }
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Lidarr sees it, including http(s)://, port, and urlbase if needed")]
public string ProwlarrUrl { get; set; }
@@ -37,6 +36,9 @@ namespace NzbDrone.Core.Applications.Lidarr
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Lidarr in Settings/General")]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> SyncCategories { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -19,10 +19,10 @@ namespace NzbDrone.Core.Applications.Mylar
private readonly IMylarV3Proxy _mylarV3Proxy;
private readonly IConfigFileProvider _configFileProvider;
public Mylar(IMylarV3Proxy lidarrV1Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
public Mylar(IMylarV3Proxy mylarV3Proxy, IConfigFileProvider configFileProvider, IAppIndexerMapService appIndexerMapService, Logger logger)
: base(appIndexerMapService, logger)
{
_mylarV3Proxy = lidarrV1Proxy;
_mylarV3Proxy = mylarV3Proxy;
_configFileProvider = configFileProvider;
}
@@ -70,9 +70,9 @@ namespace NzbDrone.Core.Applications.Mylar
{
if (indexer.Capabilities.Categories.SupportedCategories(Settings.SyncCategories.ToArray()).Any())
{
var lidarrIndexer = BuildMylarIndexer(indexer, indexer.Protocol);
var mylarIndexer = BuildMylarIndexer(indexer, indexer.Protocol);
var remoteIndexer = _mylarV3Proxy.AddIndexer(lidarrIndexer, Settings);
var remoteIndexer = _mylarV3Proxy.AddIndexer(mylarIndexer, Settings);
_appIndexerMapService.Insert(new AppIndexerMap { AppId = Definition.Id, IndexerId = indexer.Id, RemoteIndexerName = $"{remoteIndexer.Type},{remoteIndexer.Name}" });
}
}
@@ -137,7 +137,7 @@ namespace NzbDrone.Core.Applications.Mylar
{
var schema = protocol == DownloadProtocol.Usenet ? MylarProviderType.Newznab : MylarProviderType.Torznab;
var lidarrIndexer = new MylarIndexer
var mylarIndexer = new MylarIndexer
{
Name = originalName ?? $"{indexer.Name} (Prowlarr)",
Altername = $"{indexer.Name} (Prowlarr)",
@@ -148,7 +148,7 @@ namespace NzbDrone.Core.Applications.Mylar
Type = schema,
};
return lidarrIndexer;
return mylarIndexer;
}
}
}

View File

@@ -1,9 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NzbDrone.Core.Applications.Mylar
{
public class MylarError

View File

@@ -1,22 +0,0 @@
namespace NzbDrone.Core.Applications.Mylar
{
public class MylarField
{
public int Order { get; set; }
public string Name { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public string HelpText { get; set; }
public string HelpLink { get; set; }
public object Value { get; set; }
public string Type { get; set; }
public bool Advanced { get; set; }
public string Section { get; set; }
public string Hidden { get; set; }
public MylarField Clone()
{
return (MylarField)MemberwiseClone();
}
}
}

View File

@@ -13,6 +13,7 @@ namespace NzbDrone.Core.Applications.Mylar
RuleFor(c => c.BaseUrl).IsValidUrl();
RuleFor(c => c.ProwlarrUrl).IsValidUrl();
RuleFor(c => c.ApiKey).NotEmpty();
RuleFor(c => c.SyncCategories).NotEmpty();
}
}
@@ -27,8 +28,6 @@ namespace NzbDrone.Core.Applications.Mylar
SyncCategories = new[] { NewznabStandardCategory.BooksComics.Id };
}
public IEnumerable<int> SyncCategories { get; set; }
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Mylar sees it, including http(s)://, port, and urlbase if needed")]
public string ProwlarrUrl { get; set; }
@@ -38,6 +37,9 @@ namespace NzbDrone.Core.Applications.Mylar
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Mylar in Settings/Web Interface")]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> SyncCategories { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using FluentValidation.Results;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Core.Applications.Mylar
{

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Applications.Radarr
@@ -12,6 +13,7 @@ namespace NzbDrone.Core.Applications.Radarr
RuleFor(c => c.BaseUrl).IsValidUrl();
RuleFor(c => c.ProwlarrUrl).IsValidUrl();
RuleFor(c => c.ApiKey).NotEmpty();
RuleFor(c => c.SyncCategories).NotEmpty();
}
}
@@ -26,8 +28,6 @@ namespace NzbDrone.Core.Applications.Radarr
SyncCategories = new[] { 2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080 };
}
public IEnumerable<int> SyncCategories { get; set; }
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Radarr sees it, including http(s)://, port, and urlbase if needed")]
public string ProwlarrUrl { get; set; }
@@ -37,6 +37,9 @@ namespace NzbDrone.Core.Applications.Radarr
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Radarr in Settings/General")]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> SyncCategories { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Applications.Readarr
@@ -12,6 +13,7 @@ namespace NzbDrone.Core.Applications.Readarr
RuleFor(c => c.BaseUrl).IsValidUrl();
RuleFor(c => c.ProwlarrUrl).IsValidUrl();
RuleFor(c => c.ApiKey).NotEmpty();
RuleFor(c => c.SyncCategories).NotEmpty();
}
}
@@ -26,8 +28,6 @@ namespace NzbDrone.Core.Applications.Readarr
SyncCategories = new[] { 3030, 7000, 7010, 7020, 7030, 7040, 7050, 7060 };
}
public IEnumerable<int> SyncCategories { get; set; }
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Readarr sees it, including http(s)://, port, and urlbase if needed")]
public string ProwlarrUrl { get; set; }
@@ -37,6 +37,9 @@ namespace NzbDrone.Core.Applications.Readarr
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Readarr in Settings/General")]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> SyncCategories { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Applications.Sonarr
@@ -27,9 +28,6 @@ namespace NzbDrone.Core.Applications.Sonarr
AnimeSyncCategories = new[] { 5070 };
}
public IEnumerable<int> SyncCategories { get; set; }
public IEnumerable<int> AnimeSyncCategories { get; set; }
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as Sonarr sees it, including http(s)://, port, and urlbase if needed")]
public string ProwlarrUrl { get; set; }
@@ -39,6 +37,12 @@ namespace NzbDrone.Core.Applications.Sonarr
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by Sonarr in Settings/General")]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> SyncCategories { get; set; }
[FieldDefinition(4, Label = "Anime Sync Categories", Type = FieldType.Select, SelectOptions = typeof(NewznabCategoryFieldConverter), Advanced = true, HelpText = "Only Indexers that support these categories will be synced")]
public IEnumerable<int> AnimeSyncCategories { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(13)]
public class desi_gazelle_to_unit3d : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Update.Table("Indexers").Set(new { ConfigContract = "Unit3dSettings", Enable = 0 }).Where(new { Implementation = "DesiTorrents" });
}
}
}

View File

@@ -41,15 +41,18 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
switch (MigrationContext.Current.MigrationType)
{
case MigrationType.Main:
_logger.Info("Starting migration to " + Version);
LogMigrationMessage(MigrationType.Main);
MainDbUpgrade();
return;
case MigrationType.Log:
_logger.Info("Starting migration to " + Version);
LogMigrationMessage(MigrationType.Log);
LogDbUpgrade();
return;
default:
LogMigrationMessage(MigrationType.Log);
LogDbUpgrade();
LogMigrationMessage(MigrationType.Main);
MainDbUpgrade();
return;
}
@@ -59,5 +62,10 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
{
throw new NotImplementedException();
}
private void LogMigrationMessage(MigrationType type)
{
_logger.Info("Starting migration of {0} DB to {1}", type.ToString(), Version);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
@@ -20,8 +20,9 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
ILogger<NzbDroneSQLiteProcessor> logger,
IOptionsSnapshot<ProcessorOptions> options,
IConnectionStringAccessor connectionStringAccessor,
IServiceProvider serviceProvider)
: base(factory, generator, logger, options, connectionStringAccessor, serviceProvider)
IServiceProvider serviceProvider,
SQLiteQuoter quoter)
: base(factory, generator, logger, options, connectionStringAccessor, serviceProvider, quoter)
{
}

View File

@@ -214,16 +214,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
DetailedDescription = "Prowlarr will not attempt to import completed downloads without a category."
};
}
// Complain if qBittorrent is configured to remove torrents on max ratio
var config = Proxy.GetConfig(Settings);
if ((config.MaxRatioEnabled || config.MaxSeedingTimeEnabled) && (config.MaxRatioAction == QBittorrentMaxRatioAction.Remove || config.MaxRatioAction == QBittorrentMaxRatioAction.DeleteFiles))
{
return new NzbDroneValidationFailure(string.Empty, "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit")
{
DetailedDescription = "Prowlarr will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'."
};
}
}
catch (DownloadClientAuthenticationException ex)
{

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Localization;
using NzbDrone.Core.ThingiProvider.Events;
@@ -12,11 +11,11 @@ namespace NzbDrone.Core.HealthCheck.Checks
[CheckOn(typeof(ProviderAddedEvent<IIndexer>))]
[CheckOn(typeof(ProviderUpdatedEvent<IIndexer>))]
[CheckOn(typeof(ProviderDeletedEvent<IIndexer>))]
public class NewznabVIPCheck : HealthCheckBase
public class IndexerVIPCheck : HealthCheckBase
{
private readonly IIndexerFactory _indexerFactory;
public NewznabVIPCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
public IndexerVIPCheck(IIndexerFactory indexerFactory, ILocalizationService localizationService)
: base(localizationService)
{
_indexerFactory = indexerFactory;
@@ -25,13 +24,20 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
var enabled = _indexerFactory.Enabled(false);
var newznabProviders = enabled.Where(i => i.Definition.Implementation == typeof(Newznab).Name);
var expiringProviders = new List<IIndexer>();
var expiredProviders = new List<IIndexer>();
foreach (var provider in newznabProviders)
foreach (var provider in enabled)
{
var expiration = ((NewznabSettings)provider.Definition.Settings).VipExpiration;
var settingsType = provider.Definition.Settings.GetType();
var vipProp = settingsType.GetProperty("VipExpiration");
if (vipProp == null)
{
continue;
}
var expiration = (string)vipProp.GetValue(provider.Definition.Settings);
if (expiration.IsNullOrWhiteSpace())
{
@@ -53,18 +59,18 @@ namespace NzbDrone.Core.HealthCheck.Checks
{
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiringClientMessage"),
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiringClientMessage"),
string.Join(", ", expiringProviders.Select(v => v.Definition.Name))),
"#newznab-vip-expiring");
"#indexer-vip-expiring");
}
if (!expiredProviders.Empty())
{
return new HealthCheck(GetType(),
HealthCheckResult.Warning,
string.Format(_localizationService.GetLocalizedString("NewznabVipCheckExpiredClientMessage"),
string.Format(_localizationService.GetLocalizedString("IndexerVipCheckExpiredClientMessage"),
string.Join(", ", expiredProviders.Select(v => v.Definition.Name))),
"#newznab-vip-expired");
"#indexer-vip-expired");
}
return new HealthCheck(GetType());

View File

@@ -25,6 +25,7 @@ namespace NzbDrone.Core.HealthCheck
private readonly IProvideHealthCheck[] _startupHealthChecks;
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
private readonly Dictionary<Type, IEventDrivenHealthCheck[]> _eventDrivenHealthChecks;
private readonly IServerSideNotificationService _serverSideNotificationService;
private readonly IEventAggregator _eventAggregator;
private readonly ICacheManager _cacheManager;
private readonly Logger _logger;
@@ -32,11 +33,13 @@ namespace NzbDrone.Core.HealthCheck
private readonly ICached<HealthCheck> _healthCheckResults;
public HealthCheckService(IEnumerable<IProvideHealthCheck> healthChecks,
IServerSideNotificationService serverSideNotificationService,
IEventAggregator eventAggregator,
ICacheManager cacheManager,
Logger logger)
{
_healthChecks = healthChecks.ToArray();
_serverSideNotificationService = serverSideNotificationService;
_eventAggregator = eventAggregator;
_cacheManager = cacheManager;
_logger = logger;
@@ -72,6 +75,8 @@ namespace NzbDrone.Core.HealthCheck
var results = healthChecks.Select(c => c.Check())
.ToList();
results.AddRange(_serverSideNotificationService.GetServerChecks());
foreach (var result in results)
{
if (result.Type == HealthCheckResult.Ok)

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.HealthCheck
{
public interface IServerSideNotificationService
{
public List<HealthCheck> GetServerChecks();
}
public class ServerSideNotificationService : IServerSideNotificationService
{
private readonly IHttpClient _client;
private readonly IConfigFileProvider _configFileProvider;
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
private readonly Logger _logger;
public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IProwlarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
{
_client = client;
_configFileProvider = configFileProvider;
_cloudRequestBuilder = cloudRequestBuilder.Services;
_logger = logger;
}
public List<HealthCheck> GetServerChecks()
{
var request = _cloudRequestBuilder.Create()
.Resource("/notification")
.AddQueryParam("version", BuildInfo.Version)
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
.AddQueryParam("branch", _configFileProvider.Branch)
.Build();
try
{
_logger.Trace("Getting server side health notifications");
var response = _client.Execute(request);
var result = Json.Deserialize<List<ServerNotificationResponse>>(response.Content);
return result.Select(x => new HealthCheck(GetType(), x.Type, x.Message, x.WikiUrl)).ToList();
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to retrieve server notifications");
return new List<HealthCheck>();
}
}
}
public class ServerNotificationResponse
{
public HealthCheckResult Type { get; set; }
public string Message { get; set; }
public string WikiUrl { get; set; }
}
}

View File

@@ -19,7 +19,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
{
using (var mapper = _database.OpenConnection())
{
var usedTags = new[] { "Notifications" }
var usedTags = new[] { "Notifications", "IndexerProxies", "Indexers", "Applications" }
.SelectMany(v => GetUsedTags(v, mapper))
.Distinct()
.ToArray();

View File

@@ -7,14 +7,15 @@ using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.IndexerProxies.FlareSolverr
{
public class FlareSolverr : HttpIndexerProxyBase<FlareSolverrSettings>
{
public FlareSolverr(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger)
: base(cloudRequestBuilder, httpClient, logger)
public FlareSolverr(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger, ILocalizationService localizationService)
: base(cloudRequestBuilder, httpClient, logger, localizationService)
{
}
@@ -133,13 +134,13 @@ namespace NzbDrone.Core.IndexerProxies.FlareSolverr
if (response.StatusCode == HttpStatusCode.BadRequest)
{
_logger.Error("Proxy Health Check failed: {0}", response.StatusCode);
failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckBadRequestMessage"));
failures.Add(new NzbDroneValidationFailure("Host", string.Format(_localizationService.GetLocalizedString("ProxyCheckBadRequestMessage"), response.StatusCode)));
}
}
catch (Exception ex)
{
_logger.Error(ex, "Proxy Health Check failed");
failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckFailedToTestMessage"));
failures.Add(new NzbDroneValidationFailure("Host", string.Format(_localizationService.GetLocalizedString("ProxyCheckFailedToTestMessage"), request.Url.Host)));
}
return new ValidationResult(failures);

View File

@@ -3,13 +3,14 @@ using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.IndexerProxies.Http
{
public class Http : HttpIndexerProxyBase<HttpSettings>
{
public Http(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger)
: base(cloudRequestBuilder, httpClient, logger)
public Http(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger, ILocalizationService localizationService)
: base(cloudRequestBuilder, httpClient, logger, localizationService)
{
}

View File

@@ -6,6 +6,7 @@ using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.Http;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.IndexerProxies
@@ -16,12 +17,14 @@ namespace NzbDrone.Core.IndexerProxies
protected readonly IHttpClient _httpClient;
protected readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
protected readonly Logger _logger;
protected readonly ILocalizationService _localizationService;
public HttpIndexerProxyBase(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger)
public HttpIndexerProxyBase(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger, ILocalizationService localizationService)
{
_httpClient = httpClient;
_logger = logger;
_cloudRequestBuilder = cloudRequestBuilder.Services;
_localizationService = localizationService;
}
public override ValidationResult Test()
@@ -31,7 +34,7 @@ namespace NzbDrone.Core.IndexerProxies
var addresses = Dns.GetHostAddresses(Settings.Host);
if (!addresses.Any())
{
failures.Add(new NzbDroneValidationFailure("Host", "ProxyCheckResolveIpMessage"));
failures.Add(new NzbDroneValidationFailure("Host", string.Format(_localizationService.GetLocalizedString("ProxyCheckResolveIpMessage"), addresses)));
}
var request = PreRequest(_cloudRequestBuilder.Create()

View File

@@ -8,13 +8,14 @@ using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.IndexerProxies.Socks4
{
public class Socks4 : HttpIndexerProxyBase<Socks4Settings>
{
public Socks4(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger)
: base(cloudRequestBuilder, httpClient, logger)
public Socks4(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger, ILocalizationService localizationService)
: base(cloudRequestBuilder, httpClient, logger, localizationService)
{
}

View File

@@ -8,13 +8,14 @@ using NLog;
using NzbDrone.Common.Cloud;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Localization;
namespace NzbDrone.Core.IndexerProxies.Socks5
{
public class Socks5 : HttpIndexerProxyBase<Socks5Settings>
{
public Socks5(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger)
: base(cloudRequestBuilder, httpClient, logger)
public Socks5(IProwlarrCloudRequestBuilder cloudRequestBuilder, IHttpClient httpClient, Logger logger, ILocalizationService localizationService)
: base(cloudRequestBuilder, httpClient, logger, localizationService)
{
}

View File

@@ -1,3 +1,4 @@
using System.Text;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Parser;
@@ -23,5 +24,42 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
}
public string FullImdbId => ParseUtil.GetFullImdbId(ImdbId);
public override string SearchQuery
{
get
{
var searchQueryTerm = $"Term: []";
if (SearchTerm.IsNotNullOrWhiteSpace())
{
searchQueryTerm = $"Term: [{SearchTerm}]";
}
if (!ImdbId.IsNotNullOrWhiteSpace() && !TmdbId.HasValue && !TraktId.HasValue)
{
return searchQueryTerm;
}
var builder = new StringBuilder(searchQueryTerm);
builder = builder.Append(" | ID(s):");
if (ImdbId.IsNotNullOrWhiteSpace())
{
builder = builder.Append($" IMDbId:[{ImdbId}]");
}
if (TmdbId.HasValue)
{
builder = builder.Append($" TMDbId:[{TmdbId}]");
}
if (TraktId.HasValue)
{
builder = builder.Append($" TraktId:[{TraktId}]");
}
return builder.ToString().Trim();
}
}
}
}

View File

@@ -21,9 +21,17 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
public string Source { get; set; }
public string Host { get; set; }
public virtual string SearchQuery
{
get
{
return $"Term: [{SearchTerm}]";
}
}
public override string ToString()
{
return $"{{Term: {SearchTerm}, Offset: {Offset ?? 0}, Limit: {Limit ?? 0}, Categories: [{string.Join(", ", Categories)}]}}";
return $"{SearchQuery}, Offset: {Offset ?? 0}, Limit: {Limit ?? 0}, Categories: [{string.Join(", ", Categories)}]";
}
public virtual bool RssSearch

View File

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Text;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Parser;
@@ -34,6 +35,50 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
}
}
public override string SearchQuery
{
get
{
var searchQueryTerm = $"Term: []";
var searchEpisodeTerm = $" for Season / Episode:[{EpisodeSearchString}]";
if (SearchTerm.IsNotNullOrWhiteSpace())
{
searchQueryTerm = $"Term: [{SearchTerm}]";
}
if (!ImdbId.IsNotNullOrWhiteSpace() && !TvdbId.HasValue && !RId.HasValue && !TraktId.HasValue)
{
return $"{searchQueryTerm}{searchEpisodeTerm}";
}
var builder = new StringBuilder(searchQueryTerm);
builder = builder.Append(" | ID(s):");
if (ImdbId.IsNotNullOrWhiteSpace())
{
builder.Append($" IMDbId:[{ImdbId}]");
}
if (TvdbId.HasValue)
{
builder.Append($" TVDbId:[{TvdbId}]");
}
if (RId.HasValue)
{
builder.Append($" TVRageId:[{RId}]");
}
if (TraktId.HasValue)
{
builder.Append($" TraktId:[{TraktId}]");
}
builder = builder.Append(searchEpisodeTerm);
return builder.ToString().Trim();
}
}
private string GetEpisodeSearchString()
{
if (Season == null || Season == 0)

View File

@@ -147,7 +147,7 @@ namespace NzbDrone.Core.IndexerSearch
.ToList();
}
_logger.ProgressInfo("Searching indexers: [{0}] for {1}", string.Join(", ", indexers.Select(i => i.Definition.Name).ToList()), criteriaBase.ToString());
_logger.ProgressInfo("Searching indexer(s): [{0}] for {1}", string.Join(", ", indexers.Select(i => i.Definition.Name).ToList()), criteriaBase.ToString());
var tasks = indexers.Select(x => DispatchIndexer(searchAction, x, criteriaBase));
@@ -155,7 +155,7 @@ namespace NzbDrone.Core.IndexerSearch
var reports = batch.SelectMany(x => x).ToList();
_logger.Debug("Total of {0} reports were found for {1} from {2} indexers", reports.Count, criteriaBase, indexers.Count);
_logger.Debug("Total of {0} reports were found for {1} from {2} indexer(s)", reports.Count, criteriaBase, indexers.Count);
return reports;
}

View File

@@ -24,7 +24,9 @@ namespace NzbDrone.Core.IndexerVersions
public class IndexerDefinitionUpdateService : IIndexerDefinitionUpdateService, IExecute<IndexerDefinitionUpdateCommand>
{
private const int DEFINITION_VERSION = 1;
/* Update Service will fall back if version # does not exist for an indexer per Ta */
private const int DEFINITION_VERSION = 2;
private readonly List<string> _defintionBlocklist = new List<string>()
{
"aither",
@@ -33,8 +35,10 @@ namespace NzbDrone.Core.IndexerVersions
"beyond-hd",
"beyond-hd-oneurl",
"danishbytes",
"desitorrents",
"hdbits",
"shareisland"
"shareisland",
"lat-team"
};
private readonly IHttpClient _httpClient;

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "Aither";
public override string[] IndexerUrls => new string[] { "https://aither.cc/" };
public override string Description => "Aither is a Private Torrent Tracker for HD MOVIES / TV";
public override string Language => "en-us";
public override string Language => "en-US";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public Aither(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "Anidub";
public override string[] IndexerUrls => new string[] { "https://tr.anidub.com/" };
public override string Description => "Anidub is russian anime voiceover group and eponymous anime tracker.";
public override string Language => "ru-ru";
public override string Language => "ru-RU";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.SemiPublic;
@@ -405,7 +405,7 @@ namespace NzbDrone.Core.Indexers.Definitions
const string SizeSelector = ".list.down > .red";
var sizeStr = tabNode.QuerySelector(SizeSelector).TextContent;
return ReleaseInfo.GetBytes(sizeStr);
return ParseUtil.GetBytes(sizeStr);
}
private string GetReleaseLink(AngleSharp.Dom.IElement tabNode)

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "Anilibria";
public override string[] IndexerUrls => new string[] { "https://anilibria.tv/" };
public override string Description => "Anilibria is russian anime voiceover group and eponymous anime tracker.";
public override string Language => "ru-ru";
public override string Language => "ru-RU";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
@@ -172,6 +172,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{
var torrentInfos = new List<ReleaseInfo>();
var queryResponseItems = JsonConvert.DeserializeObject<List<AnilibriaTitle>>(indexerResponse.Content);
var wwwUrl = Regex.Replace(_settings.BaseUrl, @"(https?:\/\/)(.*)", "$1www.$2/");
foreach (var tl in queryResponseItems)
{
@@ -190,8 +191,8 @@ namespace NzbDrone.Core.Indexers.Definitions
// API provides timestamp in UTC+3 timezone, so we need to substract 3 hours
PublishDate = DateTimeUtil.UnixTimestampToDateTime(tr.UploadedTimestamp).AddHours(-3),
Guid = _settings.BaseUrl + tr.Url,
DownloadUrl = _settings.BaseUrl + tr.Url,
Guid = wwwUrl + tr.Url,
DownloadUrl = wwwUrl + tr.Url,
Size = tr.TotalSize,
Resolution = tr.Quality.Resolution,
Codec = tr.Quality.Encoder

View File

@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "AnimeBytes";
public override string[] IndexerUrls => new string[] { "https://animebytes.tv/" };
public override string Description => "AnimeBytes (AB) is the largest private torrent tracker that specialises in anime and anime-related content.";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;

View File

@@ -276,7 +276,7 @@ namespace NzbDrone.Core.Indexers.Definitions
}
var sizeStr = row.QuerySelector("td:nth-of-type(6)").TextContent;
release.Size = ReleaseInfo.GetBytes(sizeStr);
release.Size = ParseUtil.GetBytes(sizeStr);
var connections = row.QuerySelector("td:nth-of-type(8)").TextContent.Trim().Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "AnimeWorld";
public override string[] IndexerUrls => new string[] { "https://animeworld.cx/" };
public override string Description => "AnimeWorld (AW) is a GERMAN Private site for ANIME / MANGA / HENTAI";
public override string Language => "de-de";
public override string Language => "de-DE";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public AnimeWorld(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)

View File

@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "Animedia";
public override string[] IndexerUrls => new string[] { "https://tt.animedia.tv/" };
public override string Description => "Animedia is russian anime voiceover group and eponymous anime tracker.";
public override string Language => "ru-ru";
public override string Language => "ru-RU";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
@@ -210,7 +210,7 @@ namespace NzbDrone.Core.Indexers.Definitions
private long getReleaseSize(AngleSharp.Dom.IElement tr)
{
var sizeStr = tr.QuerySelector("div.tracker_info_left").TextContent;
return ReleaseInfo.GetBytes(SizeInfoQueryRegex.Match(sizeStr).Groups[1].Value.Trim());
return ParseUtil.GetBytes(SizeInfoQueryRegex.Match(sizeStr).Groups[1].Value.Trim());
}
private DateTime getReleaseDate(AngleSharp.Dom.IElement tr)

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string[] IndexerUrls => new string[] { "https://anthelion.me/" };
private string LoginUrl => Settings.BaseUrl + "login.php";
public override string Description => "A movies tracker";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -242,7 +242,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var files = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(3)").TextContent);
var publishDate = DateTimeUtil.FromTimeAgo(row.QuerySelector("td:nth-child(4)").TextContent);
var size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(5)").FirstChild.TextContent);
var grabs = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(6)").TextContent);
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(7)").TextContent);
var leechers = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(8)").TextContent);

View File

@@ -25,8 +25,8 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "BB";
public override string[] IndexerUrls => new string[] { StringUtil.FromBase64("aHR0cHM6Ly9iYWNvbmJpdHMub3JnLw==") };
private string LoginUrl => Settings.BaseUrl + "login.php";
public override string Description => "bB is a Private Torrent Tracker for 0DAY / GENERAL";
public override string Language => "en-us";
public override string Description => "BB is a Private Torrent Tracker for 0DAY / GENERAL";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -103,6 +103,11 @@ namespace NzbDrone.Core.Indexers.Definitions
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
{
if (!httpResponse.Content.Contains("logout.php"))
{
return true;
}
return false;
}
@@ -279,7 +284,7 @@ namespace NzbDrone.Core.Indexers.Definitions
release.PublishDate = DateTimeUtil.FromTimeAgo(dateStr);
var sizeStr = row.Children[4].TextContent;
release.Size = ReleaseInfo.GetBytes(sizeStr);
release.Size = ParseUtil.GetBytes(sizeStr);
release.Files = ParseUtil.CoerceInt(row.Children[2].TextContent.Trim());
release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent.Trim());

View File

@@ -15,6 +15,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Validation;
@@ -52,7 +53,7 @@ namespace NzbDrone.Core.Indexers.Definitions
.SetCookies(GetCookies() ?? new Dictionary<string, string>())
.Build();
var response = await _httpClient.ExecuteAsync(request, Definition);
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
var parser = new HtmlParser();
var dom = parser.ParseDocument(response.Content);
@@ -170,7 +171,11 @@ namespace NzbDrone.Core.Indexers.Definitions
private IEnumerable<IndexerRequest> GetPagedRequests(string term)
{
var searchString = term;
var searchUrl = Settings.BaseUrl + "browse.php?only=0&hentai=1&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&reorder=1&q=";
var searchUrl = Settings.BaseUrl + " browse.php?only=0&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&reorder=1&q=";
if (Settings.AdultContent)
{
searchUrl = Settings.BaseUrl + "browse.php?only=0&hentai=1&incomplete=1&lossless=1&hd=1&multiaudio=1&bonus=1&reorder=1&q=";
}
var match = Regex.Match(term, @".*(?=\s(?:[Ee]\d+|\d+)$)");
if (match.Success)
@@ -338,7 +343,7 @@ namespace NzbDrone.Core.Indexers.Definitions
release.MinimumSeedTime = 172800; // 48 hours
var size = row.QuerySelector(".size").TextContent;
release.Size = ReleaseInfo.GetBytes(size);
release.Size = ParseUtil.GetBytes(size);
//22 Jul 15
var dateStr = row.QuerySelector(".added").TextContent.Replace("'", string.Empty);
@@ -440,7 +445,10 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(5, Label = "Append Season", Type = FieldType.Checkbox, HelpText = "Append Season for Sonarr Compatibility")]
public bool AppendSeason { get; set; }
[FieldDefinition(6)]
[FieldDefinition(6, Label = "Adult Content", Type = FieldType.Checkbox, HelpText = "Allow Adult Content (Must be enabled in BakaBT settings)")]
public bool AdultContent { get; set; }
[FieldDefinition(7)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "BinSearch";
public override string[] IndexerUrls => new string[] { "https://binsearch.info/" };
public override string Description => "The binary Usenet search engine";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;
@@ -194,7 +194,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{
Guid = guid,
Title = parsedTitle.Groups["title"].Value,
Size = ReleaseInfo.GetBytes(string.Format("{0} {1}", size.Groups["size"].Value, size.Groups["unit"].Value)),
Size = ParseUtil.GetBytes(string.Format("{0} {1}", size.Groups["size"].Value, size.Groups["unit"].Value)),
PublishDate = publishDate,
Categories = new List<IndexerCategory> { NewznabStandardCategory.Other },
InfoUrl = infoUrl,

View File

@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;
using AngleSharp.Html.Parser;
using FluentValidation;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Indexers.Definitions
{
public class BitHDTV : TorrentIndexerBase<BitHDTVSettings>
{
public override string Name => "BitHDTV";
public override string[] IndexerUrls => new string[] { "https://www.bit-hdtv.com/" };
public override string Description => "BIT-HDTV - Home of High Definition";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override IndexerCapabilities Capabilities => SetCapabilities();
public BitHDTV(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IIndexerRequestGenerator GetRequestGenerator()
{
return new BitHDTVRequestGenerator() { Settings = Settings, Capabilities = Capabilities };
}
public override IParseIndexerResponse GetParser()
{
return new BitHDTVParser(Settings, Capabilities.Categories);
}
protected override IDictionary<string, string> GetCookies()
{
return CookieUtil.CookieHeaderToDictionary(Settings.Cookie);
}
protected override bool CheckIfLoginNeeded(HttpResponse httpResponse)
{
return false;
}
private IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVAnime, "Anime");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.MoviesBluRay, "Movies/Blu-ray");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.TVDocumentary, "Documentaries");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.AudioLossless, "HQ Audio");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.AudioVideo, "Music Videos");
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.Other, "Other");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVSport, "Sports");
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.TV, "TV");
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.TV, "TV/Seasonpack");
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.XXX, "XXX");
return caps;
}
}
public class BitHDTVRequestGenerator : IIndexerRequestGenerator
{
public BitHDTVSettings Settings { get; set; }
public IndexerCapabilities Capabilities { get; set; }
public BitHDTVRequestGenerator()
{
}
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
{
var searchUrl = string.Format("{0}/torrents.php", Settings.BaseUrl.TrimEnd('/'));
var qc = new NameValueCollection
{
{ "cat", Capabilities.Categories.MapTorznabCapsToTrackers(categories, true).FirstIfSingleOrDefault("0") }
};
var search = new UriBuilder(searchUrl);
if (imdbId.IsNotNullOrWhiteSpace())
{
qc.Add("search", imdbId);
qc.Add("options", "4"); //Search URL field for IMDB link
search.Query = qc.GetQueryString();
yield return new IndexerRequest(search.ToString(), HttpAccept.Html);
qc["Options"] = "1"; //Search Title and Description
search.Query = qc.GetQueryString();
yield return new IndexerRequest(search.ToString(), HttpAccept.Html);
}
else
{
//Site handles empty string on search param. No need to check for IsNullOrEmpty()
qc.Add("search", term);
qc.Add("options", "0"); //Search Title Only
search.Query = qc.GetQueryString();
yield return new IndexerRequest(search.ToString(), HttpAccept.Html);
}
}
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories, searchCriteria.FullImdbId));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedTvSearchString), searchCriteria.Categories, searchCriteria.FullImdbId));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
{
var pageableRequests = new IndexerPageableRequestChain();
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SanitizedSearchTerm), searchCriteria.Categories));
return pageableRequests;
}
public Func<IDictionary<string, string>> GetCookies { get; set; }
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
public class BitHDTVParser : IParseIndexerResponse
{
private readonly BitHDTVSettings _settings;
private readonly IndexerCapabilitiesCategories _categories;
public BitHDTVParser(BitHDTVSettings settings, IndexerCapabilitiesCategories categories)
{
_settings = settings;
_categories = categories;
}
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
var parser = new HtmlParser();
var dom = parser.ParseDocument(indexerResponse.Content);
foreach (var child in dom.QuerySelectorAll("#needseed"))
{
child.Remove();
}
var table = dom.QuerySelector("table[align=center] + br + table > tbody");
// No results, so skip this search
if (table == null)
{
return torrentInfos;
}
foreach (var row in table.Children.Skip(1))
{
var release = new TorrentInfo();
var qLink = row.Children[2].QuerySelector("a");
release.MinimumRatio = 1;
release.MinimumSeedTime = 172800; // 48 hours
release.Title = qLink.GetAttribute("title");
var detailsLink = new Uri(qLink.GetAttribute("href"));
//Skip irrelevant and duplicate entries
if (torrentInfos.Any(r => r.Guid == detailsLink.AbsoluteUri))
{
continue;
}
release.Files = ParseUtil.CoerceInt(row.Children[3].TextContent);
release.Grabs = ParseUtil.CoerceInt(row.Children[7].TextContent);
release.Guid = detailsLink.AbsoluteUri;
release.InfoUrl = release.Guid;
release.DownloadUrl = new Uri(_settings.BaseUrl + row.QuerySelector("a[href^=\"download.php\"]").GetAttribute("href")).AbsoluteUri;
var catUrl = new Uri(_settings.BaseUrl + row.Children[1].FirstElementChild.GetAttribute("href"));
var catQuery = HttpUtility.ParseQueryString(catUrl.Query);
var catNum = catQuery["cat"];
release.Categories = _categories.MapTrackerCatToNewznab(catNum);
var dateString = row.Children[5].TextContent.Trim();
var pubDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);
release.PublishDate = DateTime.SpecifyKind(pubDate, DateTimeKind.Local);
var sizeStr = row.Children[6].TextContent;
release.Size = ParseUtil.GetBytes(sizeStr);
release.Seeders = ParseUtil.CoerceInt(row.Children[8].TextContent.Trim());
release.Peers = ParseUtil.CoerceInt(row.Children[9].TextContent.Trim()) + release.Seeders;
switch (row.GetAttribute("bgcolor"))
{
case "#DDDDDD":
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 2;
break;
case "#FFFF99":
release.DownloadVolumeFactor = 0;
release.UploadVolumeFactor = 1;
break;
case "#CCFF99":
release.DownloadVolumeFactor = 0;
release.UploadVolumeFactor = 2;
break;
default:
release.DownloadVolumeFactor = 1;
release.UploadVolumeFactor = 1;
break;
}
torrentInfos.Add(release);
}
return torrentInfos.ToArray();
}
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
}
public class BitHDTVSettingsValidator : AbstractValidator<BitHDTVSettings>
{
public BitHDTVSettingsValidator()
{
RuleFor(c => c.Cookie).NotEmpty();
}
}
public class BitHDTVSettings : IIndexerSettings
{
private static readonly BitHDTVSettingsValidator Validator = new BitHDTVSettingsValidator();
public BitHDTVSettings()
{
Cookie = "";
}
[FieldDefinition(1, Label = "Base Url", HelpText = "Select which baseurl Prowlarr will use for requests to the site", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls")]
public string BaseUrl { get; set; }
[FieldDefinition(2, Label = "Cookie", HelpText = "Login cookie from website")]
public string Cookie { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.IndexerSearch.Definitions;
@@ -69,17 +71,26 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
}
// If only the season/episode is searched for then change format to match expected format
if (searchCriteria.Season > 0 && searchCriteria.Episode == null)
if (searchCriteria.Season > 0 && searchCriteria.Episode.IsNullOrWhiteSpace())
{
// Season Only
parameters.Name = string.Format("Season {0}%", searchCriteria.Season.Value);
parameters.Category = "Season";
}
else if (searchCriteria.Season > 0 && int.Parse(searchCriteria.Episode) > 0)
else if (searchCriteria.Season > 0 && Regex.IsMatch(searchCriteria.EpisodeSearchString, "(\\d{4}\\.\\d{2}\\.\\d{2})"))
{
// Daily Episode
parameters.Name = searchCriteria.EpisodeSearchString;
parameters.Category = "Episode";
}
else if (searchCriteria.Season > 0 && int.Parse(searchCriteria.Episode) > 0)
{
// Standard (S/E) Episode
parameters.Name = string.Format("S{0:00}E{1:00}", searchCriteria.Season.Value, int.Parse(searchCriteria.Episode));
parameters.Category = "Episode";
}
// Neither a season only search nor daily nor standard, fall back to query
parameters.Search = searchString.Replace(" ", "%");
pageableRequests.Add(GetPagedRequests(parameters, btnResults, btnOffset));

View File

@@ -177,7 +177,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
try
{
var response = await _httpClient.ExecuteAsync(request, Definition);
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
downloadBytes = response.ResponseData;
}
catch (HttpException ex)

View File

@@ -39,6 +39,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
public List<string> Links { get; set; }
public List<string> Legacylinks { get; set; }
public bool Followredirect { get; set; } = false;
public bool TestLinkTorrent { get; set; } = true;
public List<string> Certificates { get; set; }
public CapabilitiesBlock Caps { get; set; }
public LoginBlock Login { get; set; }
@@ -70,6 +71,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
public Dictionary<string, string> Categories { get; set; }
public List<CategorymappingBlock> Categorymappings { get; set; }
public Dictionary<string, List<string>> Modes { get; set; }
public bool AllowRawSearch { get; set; }
}
public class CaptchaBlock
@@ -166,11 +168,30 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
public class DownloadBlock
{
public List<SelectorField> Selectors { get; set; }
public string Method { get; set; }
public BeforeBlock Before { get; set; }
public InfohashBlock Infohash { get; set; }
}
public class InfohashBlock
{
public SelectorField Hash { get; set; }
public SelectorField Title { get; set; }
public bool UseBeforeResponse { get; set; }
}
public class SelectorField
{
public string Selector { get; set; }
public string Attribute { get; set; }
public bool UseBeforeResponse { get; set; }
public List<FilterBlock> Filters { get; set; }
public string Method { get; set; }
public RequestBlock Before { get; set; }
}
public class BeforeBlock : RequestBlock
{
public SelectorField Pathselector { get; set; }
}
}

View File

@@ -209,7 +209,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
value = release.Categories.ToString();
break;
case "size":
release.Size = ReleaseInfo.GetBytes(value);
release.Size = ParseUtil.GetBytes(value);
value = release.Size.ToString();
break;
case "leechers":

View File

@@ -194,7 +194,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.Headers.Add("Referer", SiteLink);
var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition);
var response = await HttpClient.ExecuteProxiedAsync(requestBuilder.Build(), Definition);
Cookies = response.GetCookies();
@@ -331,7 +331,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.Headers.Add("Referer", loginUrl);
var simpleCaptchaResult = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition);
var simpleCaptchaResult = await HttpClient.ExecuteProxiedAsync(requestBuilder.Build(), Definition);
var simpleCaptchaJSON = JObject.Parse(simpleCaptchaResult.Content);
var captchaSelection = simpleCaptchaJSON["images"][0]["hash"].ToString();
@@ -411,7 +411,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
var request = requestBuilder.Build();
request.SetContent(body);
loginResult = await HttpClient.ExecuteAsync(request, Definition);
loginResult = await HttpClient.ExecuteProxiedAsync(request, Definition);
}
else
{
@@ -431,7 +431,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.AddFormParameter(pair.Key, pair.Value);
}
loginResult = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition);
loginResult = await HttpClient.ExecuteProxiedAsync(requestBuilder.Build(), Definition);
}
Cookies = loginResult.GetCookies();
@@ -466,7 +466,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.Headers.Add("Referer", SiteLink);
var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition);
var response = await HttpClient.ExecuteProxiedAsync(requestBuilder.Build(), Definition);
Cookies = response.GetCookies();
@@ -490,7 +490,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
requestBuilder.Headers.Add("Referer", SiteLink);
var response = await HttpClient.ExecuteAsync(requestBuilder.Build(), Definition);
var response = await HttpClient.ExecuteProxiedAsync(requestBuilder.Build(), Definition);
Cookies = response.GetCookies();
@@ -569,7 +569,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
var request = requestBuilder.Build();
landingResult = await HttpClient.ExecuteAsync(request, Definition);
landingResult = await HttpClient.ExecuteProxiedAsync(request, Definition);
Cookies = landingResult.GetCookies();
@@ -590,7 +590,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
if (captcha != null && automaticlogin)
{
_logger.Error(string.Format("CardigannIndexer ({0}): Found captcha during automatic login, aborting", _definition.Id));
_logger.Error("CardigannIndexer ({0}): Found captcha during automatic login, aborting", _definition.Id);
}
return captcha;
@@ -613,7 +613,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
.SetHeader("Referer", loginUrl.AbsoluteUri)
.Build();
var response = await HttpClient.ExecuteAsync(request, Definition);
var response = await HttpClient.ExecuteProxiedAsync(request, Definition);
return new Captcha
{
@@ -623,7 +623,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
else
{
_logger.Debug(string.Format("CardigannIndexer ({0}): No captcha image found", _definition.Id));
_logger.Debug("CardigannIndexer ({0}): No captcha image found", _definition.Id);
}
}
else
@@ -650,7 +650,8 @@ namespace NzbDrone.Core.Indexers.Cardigann
protected async Task<HttpResponse> HandleRequest(RequestBlock request, Dictionary<string, object> variables = null, string referer = null)
{
var requestLinkStr = ResolvePath(ApplyGoTemplateText(request.Path, variables)).ToString();
_logger.Debug($"CardigannIndexer ({_definition.Id}): handleRequest() requestLinkStr= {requestLinkStr}");
_logger.Debug("CardigannIndexer ({0}): handleRequest() requestLinkStr= {1}", _definition.Id, requestLinkStr);
Dictionary<string, string> pairs = null;
var queryCollection = new NameValueCollection();
@@ -703,9 +704,9 @@ namespace NzbDrone.Core.Indexers.Cardigann
}
}
var response = await HttpClient.ExecuteAsync(httpRequest.Build(), Definition);
var response = await HttpClient.ExecuteProxiedAsync(httpRequest.Build(), Definition);
_logger.Debug($"CardigannIndexer ({_definition.Id}): handleRequest() remote server returned {response.StatusCode.ToString()}");
_logger.Debug("CardigannIndexer ({0}): handleRequest() remote server returned {1}", _definition.Id, response.StatusCode);
return response;
}
@@ -721,9 +722,26 @@ namespace NzbDrone.Core.Indexers.Cardigann
AddTemplateVariablesFromUri(variables, link, ".DownloadUri");
if (download.Before != null)
var headers = ParseCustomHeaders(_definition.Search?.Headers, variables);
HttpResponse response = null;
var request = new HttpRequestBuilder(link.ToString())
.SetCookies(Cookies ?? new Dictionary<string, string>())
.SetHeaders(headers ?? new Dictionary<string, string>())
.Build();
request.AllowAutoRedirect = true;
var beforeBlock = download.Before;
if (beforeBlock != null)
{
await HandleRequest(download.Before, variables, link.ToString());
if (beforeBlock.Pathselector != null)
{
response = await HttpClient.ExecuteProxiedAsync(request, Definition);
beforeBlock.Path = MatchSelector(response, beforeBlock.Pathselector, variables);
}
response = await HandleRequest(beforeBlock, variables, link.ToString());
}
if (download.Method == "post")
@@ -731,49 +749,105 @@ namespace NzbDrone.Core.Indexers.Cardigann
method = HttpMethod.POST;
}
if (download.Selector != null)
if (download.Infohash != null)
{
var selector = ApplyGoTemplateText(download.Selector, variables);
var headers = ParseCustomHeaders(_definition.Search?.Headers, variables);
var request = new HttpRequestBuilder(link.ToString())
.SetCookies(Cookies ?? new Dictionary<string, string>())
.SetHeaders(headers ?? new Dictionary<string, string>())
.Build();
request.AllowAutoRedirect = true;
var response = await HttpClient.ExecuteAsync(request, Definition);
var results = response.Content;
var searchResultParser = new HtmlParser();
var searchResultDocument = searchResultParser.ParseDocument(results);
var downloadElement = searchResultDocument.QuerySelector(selector);
if (downloadElement != null)
try
{
_logger.Debug(string.Format("CardigannIndexer ({0}): Download selector {1} matched:{2}", _definition.Id, selector, downloadElement.ToHtmlPretty()));
headers = ParseCustomHeaders(_definition.Search?.Headers, variables);
var href = "";
if (download.Attribute != null)
if (!download.Infohash.UseBeforeResponse || download.Before == null || response == null)
{
href = downloadElement.GetAttribute(download.Attribute);
response = await HttpClient.ExecuteProxiedAsync(request, Definition);
}
var hash = MatchSelector(response, download.Infohash.Hash, variables);
if (hash == null)
{
throw new Exception($"InfoHash selectors didn't match");
}
var title = MatchSelector(response, download.Infohash.Title, variables);
if (title == null)
{
throw new Exception($"InfoHash selectors didn't match");
}
var magnet = MagnetLinkBuilder.BuildPublicMagnetLink(hash, title);
var torrentLink = ResolvePath(magnet, link);
var hashDownloadRequest = new HttpRequestBuilder(torrentLink.AbsoluteUri)
.SetCookies(Cookies ?? new Dictionary<string, string>())
.Build();
hashDownloadRequest.Method = method;
return hashDownloadRequest;
}
catch (Exception)
{
_logger.Error("CardigannIndexer ({0}): Exception with InfoHash block with hashSelector {1} and titleSelector {2}",
_definition.Id,
download.Infohash.Hash.Selector,
download.Infohash.Title.Selector);
}
}
else if (download.Selectors != null)
{
headers = ParseCustomHeaders(_definition.Search?.Headers, variables);
foreach (var selector in download.Selectors)
{
var queryselector = ApplyGoTemplateText(selector.Selector, variables);
try
{
if (!selector.UseBeforeResponse || download.Before == null || response == null)
{
response = await HttpClient.ExecuteProxiedAsync(request, Definition);
}
var href = MatchSelector(response, selector, variables, debugMatch: true);
if (href == null)
{
throw new Exception(string.Format("Attribute \"{0}\" is not set for element {1}", download.Attribute, downloadElement.ToHtmlPretty()));
continue;
}
}
else
{
href = downloadElement.TextContent;
}
href = ApplyFilters(href, download.Filters, variables);
link = ResolvePath(href, link);
}
else
{
_logger.Error(string.Format("CardigannIndexer ({0}): Download selector {1} didn't match:\n{2}", _definition.Id, download.Selector, results));
throw new Exception(string.Format("Download selector {0} didn't match", download.Selector));
var torrentLink = ResolvePath(href, link);
if (torrentLink.Scheme != "magnet" && _definition.TestLinkTorrent)
{
// Test link
var testLinkRequest = new HttpRequestBuilder(torrentLink.ToString())
.SetCookies(Cookies ?? new Dictionary<string, string>())
.SetHeaders(headers ?? new Dictionary<string, string>())
.Build();
response = await HttpClient.ExecuteProxiedAsync(testLinkRequest, Definition);
var content = response.Content;
if (content.Length >= 1 && content[0] != 'd')
{
_logger.Debug("CardigannIndexer ({0}): Download selector {1}'s torrent file is invalid, retrying with next available selector", _definition.Id, queryselector);
continue;
}
}
link = torrentLink;
var selectorDownloadRequest = new HttpRequestBuilder(link.AbsoluteUri)
.SetCookies(Cookies ?? new Dictionary<string, string>())
.Build();
selectorDownloadRequest.Method = method;
return selectorDownloadRequest;
}
catch (Exception e)
{
_logger.Error("{0} CardigannIndexer ({1}): An exception occurred while trying selector {2}, retrying with next available selector", e, _definition.Id, queryselector);
throw new Exception(string.Format("An exception occurred while trying selector {0}", queryselector));
}
}
}
}
@@ -787,6 +861,44 @@ namespace NzbDrone.Core.Indexers.Cardigann
return downloadRequest;
}
protected string MatchSelector(HttpResponse response, SelectorField selector, Dictionary<string, object> variables, bool debugMatch = false)
{
var selectorText = ApplyGoTemplateText(selector.Selector, variables);
var parser = new HtmlParser();
var results = response.Content;
var resultDocument = parser.ParseDocument(results);
var element = resultDocument.QuerySelector(selectorText);
if (element == null)
{
_logger.Debug($"CardigannIndexer ({_definition.Id}): Selector {selectorText} could not match any elements.");
return null;
}
if (debugMatch)
{
_logger.Debug($"CardigannIndexer ({_definition.Id}): Download selector {selector} matched:{element.ToHtmlPretty()}");
}
string val;
if (selector.Attribute != null)
{
val = element.GetAttribute(selector.Attribute);
if (val == null)
{
throw new Exception($"Attribute \"{selector.Attribute}\" is not set for element {element.ToHtmlPretty()}");
}
}
else
{
val = element.TextContent;
}
val = ApplyFilters(val, selector.Filters, variables);
return val;
}
public bool CheckIfLoginIsNeeded(HttpResponse response)
{
if (response.HasHttpRedirect)

View File

@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Indexers.Definitions
{
public class DesiTorrents : Unit3dBase
{
public override string Name => "DesiTorrents";
public override string Language => "en-US";
public override string[] IndexerUrls => new[] { "https://desitorrents.tv/" };
public override string Description => "Desitorrents is a Private Torrent Tracker for BOLLYWOOD / TOLLYWOOD / GENERAL";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public DesiTorrents(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
public override IParseIndexerResponse GetParser()
{
return new DesiTorrentsParser(Settings, Capabilities.Categories);
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
BookSearchParams = new List<BookSearchParam>
{
BookSearchParam.Q
}
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Movies");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Music");
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.BooksEBook, "ebooks");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVSport, "Sports");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PCGames, "Games");
return caps;
}
}
public class DesiTorrentsParser : Unit3dParser
{
public DesiTorrentsParser(Unit3dSettings settings, IndexerCapabilitiesCategories categories)
: base(settings, categories)
{
}
public override IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var releases = base.ParseResponse(indexerResponse);
foreach (TorrentInfo release in releases)
{
release.MinimumRatio = 0.6;
release.MinimumSeedTime = 259200;
}
return releases;
}
}
}

View File

@@ -10,7 +10,7 @@ namespace NzbDrone.Core.Indexers.FileList
{
public override string Name => "FileList.io";
public override string[] IndexerUrls => new string[] { "https://filelist.io" };
public override string Description => "";
public override string Description => "FileList (FL) is a ROMANIAN Private Torrent Tracker for 0DAY / GENERAL";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override bool SupportsRss => true;

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
public virtual IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
var torrentInfos = new List<ReleaseInfo>();
@@ -141,7 +141,7 @@ namespace NzbDrone.Core.Indexers.Gazelle
var url = new HttpUri(_settings.BaseUrl)
.CombinePath("/torrents.php")
.AddQueryParam("action", "download")
.AddQueryParam("useToken", _settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("usetoken", _settings.UseFreeleechToken ? "1" : "0")
.AddQueryParam("id", torrentId);
return url.FullUri;

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "GazelleGames";
public override string[] IndexerUrls => new string[] { "https://gazellegames.net/" };
public override string Description => "GazelleGames (GGn) is a Private Torrent Tracker for GAMES";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -361,7 +361,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var details = _settings.BaseUrl + qDetailsLink.GetAttribute("href");
var grabs = ParseUtil.CoerceInt(qGrabs.TextContent);
var leechers = ParseUtil.CoerceInt(qLeechers.TextContent);
var size = ReleaseInfo.GetBytes(sizeString);
var size = ParseUtil.GetBytes(sizeString);
var release = new TorrentInfo
{

View File

@@ -5,6 +5,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Indexers.HDBits
{
@@ -17,26 +18,22 @@ namespace NzbDrone.Core.Indexers.HDBits
{
var pageableRequests = new IndexerPageableRequestChain();
var query = new TorrentQuery();
var imdbId = ParseUtil.GetImdbID(searchCriteria.ImdbId).GetValueOrDefault(0);
if (searchCriteria.Categories?.Length > 0)
{
query.Category = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Select(int.Parse).ToArray();
}
if (searchCriteria.ImdbId.IsNullOrWhiteSpace() && searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
if (imdbId == 0 && searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
{
query.Search = searchCriteria.SanitizedSearchTerm;
}
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace())
if (imdbId != 0)
{
var imdbId = int.Parse(searchCriteria.ImdbId.Substring(2));
if (imdbId != 0)
{
query.ImdbInfo = query.ImdbInfo ?? new ImdbInfo();
query.ImdbInfo.Id = imdbId;
}
query.ImdbInfo = query.ImdbInfo ?? new ImdbInfo();
query.ImdbInfo.Id = imdbId;
}
pageableRequests.Add(GetRequest(query));
@@ -78,19 +75,19 @@ namespace NzbDrone.Core.Indexers.HDBits
{
var pageableRequests = new IndexerPageableRequestChain();
var query = new TorrentQuery();
var tvdbId = searchCriteria.TvdbId.GetValueOrDefault(0);
var imdbId = ParseUtil.GetImdbID(searchCriteria.ImdbId).GetValueOrDefault(0);
if (searchCriteria.Categories?.Length > 0)
{
query.Category = Capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories).Select(int.Parse).ToArray();
}
if (searchCriteria.TvdbId == 0 && searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
if (tvdbId == 0 && imdbId == 0 && searchCriteria.SearchTerm.IsNotNullOrWhiteSpace())
{
query.Search = searchCriteria.SanitizedTvSearchString;
}
var tvdbId = searchCriteria.TvdbId;
if (tvdbId != 0)
{
query.TvdbInfo = query.TvdbInfo ?? new TvdbInfo();
@@ -99,6 +96,12 @@ namespace NzbDrone.Core.Indexers.HDBits
query.TvdbInfo.Episode = searchCriteria.Episode;
}
if (imdbId != 0)
{
query.ImdbInfo = query.ImdbInfo ?? new ImdbInfo();
query.ImdbInfo.Id = imdbId;
}
pageableRequests.Add(GetRequest(query));
return pageableRequests;

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string[] IndexerUrls => new string[] { "https://hd-space.org/" };
private string LoginUrl => Settings.BaseUrl + "index.php?page=login";
public override string Description => "HD-Space (HDS) is a Private Torrent Tracker for HD MOVIES / TV";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -276,7 +276,7 @@ namespace NzbDrone.Core.Indexers.Definitions
//"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23"
release.PublishDate = DateTimeUtil.FromUnknown(dateStr);
var sizeStr = row.Children[5].TextContent;
release.Size = ReleaseInfo.GetBytes(sizeStr);
release.Size = ParseUtil.GetBytes(sizeStr);
release.Seeders = ParseUtil.CoerceInt(row.Children[7].TextContent);
release.Peers = ParseUtil.CoerceInt(row.Children[8].TextContent) + release.Seeders;
var grabs = row.QuerySelector("td:nth-child(10)").TextContent;

View File

@@ -259,7 +259,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var link = new Uri(_settings.BaseUrl + row.Children[4].FirstElementChild.GetAttribute("href"));
var description = row.Children[2].QuerySelector("span").TextContent;
var size = ReleaseInfo.GetBytes(row.Children[7].TextContent);
var size = ParseUtil.GetBytes(row.Children[7].TextContent);
var dateTag = row.Children[6].FirstElementChild;
var dateString = string.Join(" ", dateTag.Attributes.Select(attr => attr.Name));

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Indexers.Headphones
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override string[] IndexerUrls => new string[] { "https://indexer.codeshy.com" };
public override string Description => "";
public override string Description => "A Private Usenet indexer for music";
public override IndexerCapabilities Capabilities => SetCapabilities();
public override IIndexerRequestGenerator GetRequestGenerator()
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Indexers.Headphones
try
{
var response = await _httpClient.ExecuteAsync(request, Definition);
var response = await _httpClient.ExecuteProxiedAsync(request, Definition);
downloadBytes = response.ResponseData;
}
catch (Exception)

View File

@@ -304,7 +304,7 @@ namespace NzbDrone.Core.Indexers.Definitions
// Torrents - Category column == Icons
var cat = _categories.MapTrackerCatToNewznab(catIcon.GetAttribute("href").Substring(1));
var size = ReleaseInfo.GetBytes(row.Children[5].TextContent);
var size = ParseUtil.GetBytes(row.Children[5].TextContent);
var colIndex = 6;
int? files = null;

View File

@@ -280,7 +280,7 @@ namespace NzbDrone.Core.Indexers.Definitions
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);
release.Size = ParseUtil.GetBytes(sizeStr);
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;

View File

@@ -71,10 +71,12 @@ namespace NzbDrone.Core.Indexers.Definitions
};
// c.f. https://archive.org/services/docs/api/metadata-schema/index.html?highlight=mediatype#mediatype
// "Movies" is a catch all category for videos
caps.Categories.AddCategoryMapping("texts", NewznabStandardCategory.Books);
caps.Categories.AddCategoryMapping("etree", NewznabStandardCategory.Audio);
caps.Categories.AddCategoryMapping("audio", NewznabStandardCategory.Audio);
caps.Categories.AddCategoryMapping("movies", NewznabStandardCategory.Movies);
caps.Categories.AddCategoryMapping("movies", NewznabStandardCategory.TV);
caps.Categories.AddCategoryMapping("software", NewznabStandardCategory.PC);
caps.Categories.AddCategoryMapping("image", NewznabStandardCategory.OtherMisc);
caps.Categories.AddCategoryMapping("data", NewznabStandardCategory.Other);
@@ -241,7 +243,7 @@ namespace NzbDrone.Core.Indexers.Definitions
UploadVolumeFactor = 1
};
if (searchResult.InfoHash != null)
if (!_settings.TorrentFileOnly && searchResult.InfoHash != null)
{
release.MagnetUrl = MagnetLinkBuilder.BuildPublicMagnetLink(searchResult.InfoHash, title);
}
@@ -292,7 +294,10 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(4, Label = "Title Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Whether to search in title only.")]
public bool TitleOnly { get; set; }
[FieldDefinition(5)]
[FieldDefinition(5, Label = "Torrent File Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Only use torrent files, not magnet links.")]
public bool TorrentFileOnly { get; set; }
[FieldDefinition(6)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public InternetArchiveSettings()
@@ -300,6 +305,7 @@ namespace NzbDrone.Core.Indexers.Definitions
SortBy = (int)InternetArchiveSort.PublicDate;
SortOrder = (int)InternetArchiveSortOrder.Descending;
TitleOnly = false;
TorrentFileOnly = false;
}
public NzbDroneValidationResult Validate()

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Definitions.UNIT3D;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers.Definitions
{
public class LatTeam : Unit3dBase
{
public override string Name => "Lat-Team";
public override string Language => "es";
public override string[] IndexerUrls => new[] { "https://lat-team.com/" };
public override string Description => "Lat-Team is a Private Torrent Tracker for HD MOVIES / TV";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public LatTeam(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
: base(httpClient, eventAggregator, indexerStatusService, configService, logger)
{
}
protected override IndexerCapabilities SetCapabilities()
{
var caps = new IndexerCapabilities
{
TvSearchParams = new List<TvSearchParam>
{
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId, TvSearchParam.TvdbId
},
MovieSearchParams = new List<MovieSearchParam>
{
MovieSearchParam.Q, MovieSearchParam.ImdbId, MovieSearchParam.TmdbId
},
MusicSearchParams = new List<MusicSearchParam>
{
MusicSearchParam.Q
},
};
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.Movies, "Peliculas");
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.MoviesOther, "Retro Pelicula");
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.TVAnime, "Anime");
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TV, "TV Series");
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVOther, "Retro Serie TV");
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.TVForeign, "Telenovelas y Teleseries");
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.Audio, "Musica");
return caps;
}
}
}

View File

@@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
using FluentValidation;
using Newtonsoft.Json;
using NLog;
using NzbDrone.Common.Http;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
@@ -269,6 +271,23 @@ namespace NzbDrone.Core.Indexers.Definitions
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
{
// Throw common http errors here before we try to parse
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response status {indexerResponse.HttpResponse.StatusCode} code from API request");
}
if (!indexerResponse.HttpResponse.Headers.ContentType.Contains(HttpAccept.Json.Value))
{
// Remove cookie cache
CookiesUpdater(null, null);
throw new IndexerException(indexerResponse, $"Unexpected response header {indexerResponse.HttpResponse.Headers.ContentType} from API request, expected {HttpAccept.Json.Value}");
}
var torrentInfos = new List<TorrentInfo>();
var jsonResponse = JsonConvert.DeserializeObject<MyAnonamouseResponse>(indexerResponse.Content);
@@ -341,7 +360,7 @@ namespace NzbDrone.Core.Indexers.Definitions
release.Seeders = item.Seeders;
release.Peers = item.Leechers + release.Seeders;
var size = item.Size;
release.Size = ReleaseInfo.GetBytes(size);
release.Size = ParseUtil.GetBytes(size);
release.DownloadVolumeFactor = item.Free ? 0 : 1;
release.UploadVolumeFactor = 1;

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string[] IndexerUrls => new string[] { "https://nebulance.io/" };
private string LoginUrl => Settings.BaseUrl + "login.php";
public override string Description => "Nebulance (NBL) is a ratioless Private Torrent Tracker for TV";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -224,7 +224,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var link = _settings.BaseUrl + row.QuerySelector("a[href*='action=download']").GetAttribute("href");
var qColSize = row.QuerySelector("td:nth-child(3)");
var size = ReleaseInfo.GetBytes(qColSize.Children[0].TextContent);
var size = ParseUtil.GetBytes(qColSize.Children[0].TextContent);
var files = ParseUtil.CoerceInt(qColSize.Children[1].TextContent.Split(':')[1].Trim());
var qPublishdate = row.QuerySelector("td:nth-child(4) span");

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public override string Name => "Newznab";
public override string[] IndexerUrls => GetBaseUrlFromSettings();
public override string Description => "";
public override string Description => "Newznab is an API search specification for Usenet";
public override bool FollowRedirect => true;
public override bool SupportsRedirect => true;

View File

@@ -56,7 +56,7 @@ namespace NzbDrone.Core.Indexers.Newznab
try
{
response = _httpClient.Execute(request, definition);
response = _httpClient.ExecuteProxied(request, definition);
}
catch (Exception ex)
{

View File

@@ -11,7 +11,7 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
{
public override string Name => "PassThePopcorn";
public override string[] IndexerUrls => new string[] { "https://passthepopcorn.me" };
public override string Description => "";
public override string Description => "PassThePopcorn (PTP) is a Private site for MOVIES / TV";
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public override bool SupportsRss => true;

View File

@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string[] IndexerUrls => new string[] { "https://pretome.info/" };
public override string Description => "PreToMe is a ratioless 0Day/General tracker.";
private string LoginUrl => Settings.BaseUrl + "takelogin.php";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -342,7 +342,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var dateStr = Regex.Replace(row.Children[5].InnerHtml, @"\<br[\s]{0,1}[\/]{0,1}\>", " ");
var publishDate = DateTimeUtil.FromTimeAgo(dateStr);
var files = ParseUtil.CoerceInt(row.Children[3].TextContent);
var size = ReleaseInfo.GetBytes(row.Children[7].TextContent);
var size = ParseUtil.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);

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "Redacted";
public override string[] IndexerUrls => new string[] { "https://redacted.ch/" };
public override string Description => "REDActed (Aka.PassTheHeadPhones) is one of the most well-known music trackers.";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;

View File

@@ -269,6 +269,12 @@ namespace NzbDrone.Core.Indexers.Definitions
var details = _settings.BaseUrl + qDetails.GetAttribute("href");
var title = qDetails.QuerySelector("b").TextContent;
// Remove auto-generated [REQ] tag from fulfilled requests
if (title.StartsWith("[REQ] "))
{
title = title.Substring(6);
}
var qLink = row.QuerySelector("td:nth-child(4) > a");
if (qLink == null)
{
@@ -281,7 +287,7 @@ namespace NzbDrone.Core.Indexers.Definitions
var dateString = row.QuerySelector("td:nth-child(6) nobr").TextContent.Trim();
var publishDate = DateTime.ParseExact(dateString, "yyyy-MM-ddHH:mm:ss", CultureInfo.InvariantCulture);
var size = ReleaseInfo.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim());
var size = ParseUtil.GetBytes(row.QuerySelector("td:nth-child(7)").InnerHtml.Split('<').First().Trim());
var files = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(7) > a").TextContent);
var grabs = ParseUtil.GetLongFromString(row.QuerySelector("td:nth-child(8)").TextContent);
var seeders = ParseUtil.CoerceInt(row.QuerySelector("td:nth-child(9)").TextContent);

View File

@@ -1443,6 +1443,11 @@ namespace NzbDrone.Core.Indexers.Definitions
queryCollection.Add("nm", searchString);
}
if (categories.Length > 0)
{
queryCollection.Add("f", string.Join(",", Capabilities.Categories.MapTorznabCapsToTrackers(categories)));
}
searchUrl = searchUrl + "?" + queryCollection.GetQueryString();
var request = new IndexerRequest(searchUrl, HttpAccept.Html);
@@ -1672,7 +1677,7 @@ namespace NzbDrone.Core.Indexers.Definitions
private long GetSizeOfRelease(in IElement row)
{
var qSize = row.QuerySelector("td.tor-size");
var size = ReleaseInfo.GetBytes(qSize.GetAttribute("data-ts_text"));
var size = ParseUtil.GetBytes(qSize.GetAttribute("data-ts_text"));
return size;
}

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "SceneTime";
public override string[] IndexerUrls => new[] { "https://www.scenetime.com/" };
public override string Description => "Always on time";
public override string Language => "en-us";
public override string Language => "en-US";
public override Encoding Encoding => Encoding.GetEncoding("iso-8859-1");
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
@@ -254,7 +254,7 @@ namespace NzbDrone.Core.Indexers.Definitions
DownloadUrl = string.Format("{0}/download.php/{1}/download.torrent", _settings.BaseUrl, torrentId),
Guid = details,
PublishDate = DateTimeUtil.FromTimeAgo(qDescCol.ChildNodes.Last().TextContent),
Size = ReleaseInfo.GetBytes(sizeStr),
Size = ParseUtil.GetBytes(sizeStr),
Seeders = seeders,
Peers = ParseUtil.CoerceInt(row.Children[leechersIndex].TextContent.Trim()) + seeders,
DownloadVolumeFactor = row.QuerySelector("font > b:contains(Freeleech)") != null ? 0 : 1,

View File

@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "ShareIsland";
public override string[] IndexerUrls => new string[] { "https://shareisland.org/" };
public override string Description => "A general italian tracker.";
public override string Language => "it-it";
public override string Language => "it-IT";
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
public ShareIsland(IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)

View File

@@ -23,7 +23,7 @@ namespace NzbDrone.Core.Indexers.Definitions
public override string Name => "ShizaProject";
public override string[] IndexerUrls => new string[] { "https://shiza-project.com/" };
public override string Description => "Shizaproject is russian anime voiceover group and eponymous anime tracker.";
public override string Language => "ru-ru";
public override string Language => "ru-RU";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
public override IndexerPrivacy Privacy => IndexerPrivacy.Public;

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Indexers.Definitions
{
public override string Name => "ShowRSS";
public override string[] IndexerUrls => new string[] { "https://showrss.info/" };
public override string Language => "en-us";
public override string Language => "en-US";
public override string Description => "showRSS is a service that allows you to keep track of your favorite TV shows";
public override Encoding Encoding => Encoding.UTF8;
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;

Some files were not shown because too many files have changed in this diff Show More