Compare commits

...

186 Commits

Author SHA1 Message Date
ta264
0e05d86a58 Fixed: Handle wonky timestamps from Orpheus 2022-04-08 04:14:26 +01:00
bakerboy448
4f83116413 Fixed: (BHD) TMDb Parsing Exception 2022-04-03 12:04:39 -05:00
missingfile
d440bc079f Fixed: MoreThanTV indexer from browse page layout changes (#922) 2022-04-02 16:50:35 -05:00
Qstick
a5c7c6cbcb We don't have two Radarrs 2022-04-02 16:30:29 -05:00
Qstick
710c3d6deb Fix indent from 37c393a659 2022-04-02 15:34:36 -05:00
bakerboy448
1959efbd09 Fixed: (HDBits) Treat 403 as Query Limit 2022-04-02 14:58:58 -05:00
bakerboy448
c4468b9cbb Fixed: (PTP) Treat 403 as Query Limit 2022-04-02 14:58:58 -05:00
bakerboy448
8762d94dda New: (BTN) Rate Limit to 1 Query per 5 Seconds 2022-04-02 14:50:06 -05:00
bakerboy448
e07ea80977 Fixed: (BTN) Handle Query Limit Error 2022-04-02 14:50:06 -05:00
bakerboy448
37c393a659 New: (Lidarr/Radarr/Readarr/Sonarr) Improved Errors 2022-04-02 14:26:03 -05:00
ta264
b4b779df5c Fixed: Loading old commands from database
Fixes #926

(cherry picked from commit 0f87cb72e559a19bddc6c9d4387ec7d9428291f8)
2022-04-02 14:22:00 -05:00
Qstick
1dbf35deb5 Fixed: Cleanup Temp files after backup creation
Fixes #925

(cherry picked from commit 7f0b708cb9f16a0116199625a2a5b4e67049be6a)
2022-04-02 14:22:00 -05:00
Qstick
6ab226c43a Add Support 2022-04-02 14:20:02 -05:00
Weblate
3a896fc43e Translated using Weblate (Finnish)
Currently translated at 97.7% (436 of 446 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fi/
Translation: Servarr/Prowlarr
2022-03-31 20:53:42 -05:00
bakerboy448
a81d632878 Fixed: Indexer Infobox Error (#920)
* Fixed: Indexer Infobox Error

* fixup! Fixed: Indexer Infobox Error

* fixup! Fixed: Indexer Infobox Error
2022-03-30 09:22:17 -05:00
bakerboy448
cb8c0d4aa7 New: Indexer Description in Add Indexer Modal 2022-03-29 23:24:48 -05:00
bakerboy448
788a5a3e24 Fixed: Missing Translates 2022-03-29 23:24:48 -05:00
bakerboy448
4f056bf228 New: Add Search Capabilities to Indexer API & InfoBox 2022-03-29 23:24:48 -05:00
Robin Dadswell
6ce9c779c1 Fixed: Update from version in logs
Closes #873
Closes #877

(cherry picked from commit bc3e3714b97dec19091e45324d9f2b550f1dbbd5)
(cherry picked from commit b34f4fde1bbe611bb5c886890e3be7cec2b96e6b)
2022-03-29 23:23:04 -05:00
Servarr
32fc2ec365 Automated API Docs update 2022-03-29 23:22:23 -05:00
Weblate
9ddd38a334 Translated using Weblate (Chinese (Simplified) (zh_CN))
Currently translated at 98.5% (422 of 428 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (428 of 428 strings)

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: libsu <libsu@qq.com>
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
2022-03-29 23:06:34 -05:00
Weblate
b45e5a5e38 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (428 of 428 strings)

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
Translation: Servarr/Prowlarr
2022-03-29 20:21:49 -05:00
Qstick
135efe6d94 Fixed: Validation when testing indexers, connections and download clients
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2022-03-28 19:55:35 -05:00
Qstick
69de6d18eb Fixed: Prevent delete of last profile 2022-03-25 18:53:29 -05:00
Qstick
627da14a32 New: Load more (page) results on Search UI 2022-03-24 22:19:00 -05:00
Qstick
83bf3620c0 Update webpack packages 2022-03-24 21:02:06 -05:00
Qstick
fa41bf3c58 Frontend Package Updates 2022-03-24 20:30:56 -05:00
Qstick
31e7a101ef Backend Package Updates 2022-03-24 20:30:56 -05:00
Qstick
22f9e9e6b7 Bump dotnet to 6.0.3 2022-03-24 20:30:56 -05:00
Weblate
8834431ba6 Translated using Weblate (Spanish)
Currently translated at 77.5% (332 of 428 strings)

Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/es/
Translation: Servarr/Prowlarr
2022-03-24 18:47:24 -05:00
bakerboy448
01cc9b3d07 Fixed: (Gazelle) Replace Periods for Space in Search Term
based on Jackett b8b816f953ad9d0508f95a9c31f9ff4385ebdd73
2022-03-23 21:18:39 -05:00
bakerboy448
a0cbe1de5d Fixed: (HDSpace) Replace Periods for Space in Search Term
based on jackett 22efff93e7f1df858b341b3c2756d8204cae6f4a
2022-03-23 21:18:39 -05:00
bakerboy448
75792c0760 Fixed: (Anthelion) Replace Periods for Space in Search Term
based on jackett 22efff93e7f1df858b341b3c2756d8204cae6f4a
2022-03-23 21:18:39 -05:00
bakerboy448
83ca724120 Fixed: (Redacted) Map Categories Comedy & E-Learning Videos to 'Other'
indexer does not actually have movies and tv
2022-03-23 21:16:22 -05:00
bakerboy448
aee6ee1a00 Fixed: No longer require first run as admin on windows (#885)
* Fixed: No longer require first run as admin on windows

(cherry picked from commit 8d9302da7761752189b2d2820915d7d13197ce47)
(cherry picked from commit daa9ee30a2c5fc2511e34167ce642c1bfe2a8b41)

Co-Authored By: ta264 <ta264@users.noreply.github.com>

* Build installer from build.sh

(cherry picked from commit 6db135877a8394244a95142347b99ea651e0cceb)
(cherry picked from commit 86102349c54f7e20f2d1e5ab104505a4ea0d68bc)

* Fixed: (macOS) Prowlarr Bundle Identifier using .video

* fixup! Fixed: No longer require first run as admin on windows

* fixup! Build installer from build.sh

Co-authored-by: ta264 <ta264@users.noreply.github.com>
2022-03-23 20:58:42 -05:00
Weblate
e66c69a292 Translated using Weblate (Chinese (Simplified) (zh_CN))
Currently translated at 98.3% (421 of 428 strings)

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

Currently translated at 98.1% (420 of 428 strings)

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

Currently translated at 98.1% (420 of 428 strings)

Translated using Weblate (French)

Currently translated at 98.5% (422 of 428 strings)

Co-authored-by: EthanChoy <ethanchoy@163.com>
Co-authored-by: KevoM <lilmarsu@gmail.com>
Co-authored-by: Vincent <intelligentvincent@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: wzw1wzw <opinionspirit222@gmail.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
2022-03-23 19:31:41 -05:00
JigSawFr
930370729b indexer(xthor): moved to YAML definition v5 2022-03-21 19:45:57 -05:00
bakerboy448
2f5b55013a Fixed: '/indexers' URL Base breaking UI navigation
(cherry picked from commit 140547e42a32cbaf80e7a794c5be9bfb574e97a2)
2022-03-21 18:01:39 -05:00
Weblate
cb74fade18 Translated using Weblate (French)
Currently translated at 98.5% (422 of 428 strings)

Co-authored-by: KevoM <lilmarsu@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
Translation: Servarr/Prowlarr
2022-03-21 18:00:16 -05:00
nitsua
e0da422b0e Fix app settings delete modal not closing and reloading app profiles 2022-03-21 15:31:30 +00:00
Weblate
77caaa2a55 Translated using Weblate (French)
Currently translated at 97.1% (416 of 428 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 1.6% (7 of 428 strings)

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

Currently translated at 95.5% (409 of 428 strings)

Co-authored-by: Ayi <4ayixd@gmail.com>
Co-authored-by: Kakise <sam.taa@icloud.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 杰森 <john@johnrosen1.com>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fr/
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
2022-03-18 14:39:40 -05:00
Qstick
55d03967ec Bump Swashbuckle to 6.3.0 2022-03-14 22:33:08 -05:00
Weblate
684c30893a Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Portuguese)

Currently translated at 82.0% (351 of 428 strings)

Translated using Weblate (French)

Currently translated at 96.7% (414 of 428 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: José Eduardo Veiga <vitruxpt@vitruxbot.com>
Co-authored-by: KevoM <lilmarsu@gmail.com>
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/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/pt_BR/
Translation: Servarr/Prowlarr
2022-03-11 01:46:34 -06:00
bakerboy448
3b51a3a618 fixup! New: (DanishBytes) Move to YML 2022-03-06 21:23:35 -06:00
bakerboy448
09bd8137fc New: (DanishBytes) Move to YML 2022-03-06 21:23:35 -06:00
Weblate
495daf7967 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translated using Weblate (Chinese (Traditional) (zh_TW))

Currently translated at 0.4% (2 of 428 strings)

Translated using Weblate (Arabic)

Currently translated at 76.1% (326 of 428 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Vietnamese)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Turkish)

Currently translated at 75.7% (324 of 428 strings)

Translated using Weblate (Thai)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Swedish)

Currently translated at 93.4% (400 of 428 strings)

Translated using Weblate (Russian)

Currently translated at 77.1% (330 of 428 strings)

Translated using Weblate (Romanian)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Portuguese)

Currently translated at 78.0% (334 of 428 strings)

Translated using Weblate (Polish)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Dutch)

Currently translated at 93.6% (401 of 428 strings)

Translated using Weblate (Dutch)

Currently translated at 93.6% (401 of 428 strings)

Translated using Weblate (Korean)

Currently translated at 56.5% (242 of 428 strings)

Translated using Weblate (Japanese)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Italian)

Currently translated at 80.3% (344 of 428 strings)

Translated using Weblate (Icelandic)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Hindi)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Hebrew)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (French)

Currently translated at 93.6% (401 of 428 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Spanish)

Currently translated at 76.8% (329 of 428 strings)

Translated using Weblate (Greek)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (German)

Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Danish)

Currently translated at 75.9% (325 of 428 strings)

Translated using Weblate (Czech)

Currently translated at 76.4% (327 of 428 strings)

Translated using Weblate (Bulgarian)

Currently translated at 70.7% (303 of 428 strings)

Translated using Weblate (Portuguese)

Currently translated at 78.0% (334 of 428 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.3% (421 of 428 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.3% (421 of 428 strings)

Translated using Weblate (German)

Currently translated at 91.3% (391 of 428 strings)

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

Currently translated at 93.6% (401 of 428 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (428 of 428 strings)

Added translation using Weblate (Ukrainian)

Added translation using Weblate (Persian)

Added translation using Weblate (Bengali)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Csaba <csab0825@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Nuno Filipe de Vilhena Santos <nunovilhenasantos@msn.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Rico_Walker <ricardo.walker1203@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Zalhera <tobias.bechen@gmail.com>
Co-authored-by: nopetw <lubduphaur@gmail.com>
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/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/fr/
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/hu/
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/nl/
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/pt_BR/
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/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_TW/
Translation: Servarr/Prowlarr
2022-03-06 21:23:19 -06:00
gaizaharduz
bb3821c254 New: (RuTracker.org) add .bet mirror (#876) 2022-03-06 14:21:15 -06:00
bakerboy448
855f8d35f2 Fixed:(pornolab) language formatting 2022-02-28 18:47:50 -06:00
Qstick
15dab381af New: Housekeeper for ApplicationStatus 2022-02-27 23:14:02 -06:00
bakerboy448
9c5a88e2e7 Fixed: Cleanse Tracker api_token from logs 2022-02-27 22:34:37 -06:00
Qstick
80d295cce5 New: (HDTorrents) Add hd-torrents.org as Url option
Fixes #853
2022-02-27 22:27:13 -06:00
Qstick
76afb70b01 New: (Cardigann) Allow JSON filters
Fixes #844
2022-02-27 22:19:19 -06:00
Qstick
c29fba3a2b Fixed: Convert List<HistoryEventTypes> to Int before passing to DB
Fixes #861
2022-02-27 19:10:49 -06:00
Qstick
68e41f0860 Fixed: WhereBuilder for Postgres 2022-02-27 18:09:40 -06:00
Weblate
4c68645175 Translated using Weblate (Finnish)
Currently translated at 100.0% (428 of 428 strings)

Translated using Weblate (Finnish)

Currently translated at 100.0% (428 of 428 strings)

Added translation using Weblate (Ukrainian)

Added translation using Weblate (Persian)

Added translation using Weblate (Bengali)

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/prowlarr/fi/
Translation: Servarr/Prowlarr
2022-02-23 21:45:57 -06:00
ta264
aaef8fb29c Fixed: Make authentication cookie name unique to Prowlarr 2022-02-20 21:38:07 +00:00
Pfuenzle
b2e300b6da Update Categories 2022-02-20 11:09:02 -06:00
bakerboy448
77c840b03a Fixed: Enable response compression over https
(cherry picked from commit cb0ae3603309daae4d6b026de15f26cb4b59096d)
2022-02-16 14:04:38 -06:00
bakerboy448
98c3408909 Fixed: (RuTracker) Update Cats
(cherry picked from jackett commit 7a7144bd9d6b4fc4c1ace4886d3bc62ec37b35e6)

Fixes #836
2022-02-15 19:15:13 -06:00
Zak Saunders
40f6c2e59d Fixed: Clarify App Sync Settings (#847) 2022-02-15 16:35:12 -06:00
James White
64c9bb4231 Set version header to X-Application-Version (missing hyphen)
Not sure if this is intentional or really harms anything but to be consistent with other Arr apps like Sonarr and Radarr, this changes the version header to X-Application-Version.
2022-02-06 21:05:27 -06:00
Qstick
94ef3ea88f Go to http if def exists on def server 2022-02-06 16:11:42 -05:00
bakerboy448
dab4500b16 Fixed: (BHD) Handle API Auth Errors 2022-02-05 19:54:26 -06:00
bakerboy448
d951943c67 Fixed: (Immortalseed) Keywordless Search 2022-02-05 17:46:04 -06:00
Qstick
bba6f9349b Fixed: (Cardigann) TraktId was mapping to TvRageId 2022-02-05 14:16:05 -06:00
Qstick
7e0f88ad7a New: (Cardigann) - Cardigann v4 Support for Genre, Year, and TraktID 2022-02-05 13:30:12 -06:00
bakerboy448
88c6cbf943 New: (Cardigann) - Cardigann v4 Support for categorydesc
based on Jackett 02e43bd6a9838562a14c6bd6ea10cfed5a293d59
based on Jackett 60d2c425e1038a28f54bda658a9f543f3a197a66

Fixes #782
2022-02-05 13:30:12 -06:00
bakerboy448
69d31f96de New: (Cardigann) - Cardigann v4 Add Support for MapTrackerCatDescToNewznab 2022-02-05 13:30:12 -06:00
bakerboy448
a5718ad937 New: (Cardigann) - Cardigann v4 Improved Search Logging 2022-02-05 13:30:12 -06:00
bakerboy448
505d9c151d Fixed: Corrected Query Limit and Grab Limit HelpText 2022-02-05 12:44:13 -06:00
Qstick
a637677ec4 New: (Avistaz) Better error reporting for unauthorized tests
Fixes #822
2022-02-04 22:51:55 -06:00
bakerboy448
a0d5421dc8 Fixed: (Cardigann) Requests Failing for Definitions without LegacyLinks 2022-02-04 13:17:07 -06:00
dependabot[bot]
95f62be50c Bump SharpZipLib from 1.3.1 to 1.3.3 in /src/NzbDrone.Common
Bumps [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) from 1.3.1 to 1.3.3.
- [Release notes](https://github.com/icsharpcode/SharpZipLib/releases)
- [Changelog](https://github.com/icsharpcode/SharpZipLib/blob/master/docs/Changes.txt)
- [Commits](https://github.com/icsharpcode/SharpZipLib/compare/v1.3.1...v1.3.3)

---
updated-dependencies:
- dependency-name: SharpZipLib
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-04 10:23:26 -06:00
Qstick
d7b5100e35 Fixed: (Cardigann) Smarter redirect domain compare
Fixes #808
2022-02-03 23:32:25 -06:00
Qstick
64c1e1fa54 Fixed: (Cardigann) Treat "Refresh" header as redirect
Fixes #812
2022-02-03 23:16:20 -06:00
Qstick
74663ea077 Fixed: (Cardigann) Replace legacy links with default link when making requests 2022-02-03 22:29:46 -06:00
Qstick
bc1e397ce3 Sync Indexers on app start, go to http if not sync'd yet 2022-01-31 12:10:52 -06:00
Qstick
17608cf915 Misc definition handling improvements 2022-01-30 20:57:06 -06:00
PearsonFlyer
a3de574de5 Fixed: Updated ruTorrent stopped state helptext 2022-01-26 09:53:07 -06:00
Robin Dadswell
22161e6d57 Fixed: Added missing translate for Database 2022-01-25 17:24:29 -06:00
Zippy79
c46ed33544 Fixed: Download limit check was using the query limit instead of the grab limit. 2022-01-25 08:56:54 -06:00
Qstick
7388655e6d Fixed: ToString() to AbsoluteUri for MagnetUrls as well 2022-01-23 19:41:20 -06:00
Qstick
5b5c186d0c Revert "Replace WebUtility.UrlEncode to Uri.EscapeDataString "
This reverts commit a7b1ef19f5.
2022-01-23 19:34:59 -06:00
Qstick
ae5d93d6dd Revert "Remove extra replacing (already done by Uri.EscapeDataString)"
This reverts commit 6880f38635.
2022-01-23 19:34:54 -06:00
Qstick
62f6670a21 Optimize Indexer updates 2022-01-21 21:30:17 -06:00
Qstick
c9951e7eba New: Add AppName to system status response 2022-01-18 23:29:30 -06:00
Qstick
2da22c08b0 Fixed: Log DB write connection opening 2022-01-18 21:06:12 -06:00
Qstick
e480f53f7f More mono cleaning 2022-01-18 20:17:01 -06:00
ta264
8701e67b1e Fixed: Close all database connections on shutdown 2022-01-18 20:07:33 -06:00
Qstick
97f4a2e651 Fixed: Use rolling 24 hours for indexer limits
Fixes #789
2022-01-18 19:56:17 -06:00
Servarr
3c3272cb25 Automated API Docs update 2022-01-17 21:42:48 -06:00
Qstick
fa626a53e6 Fixed: Smarter Int normalization
Fixes #787
2022-01-17 21:27:22 -06:00
Qstick
76daee3a1b New: Identify indexers that are already setup in add list 2022-01-17 19:24:52 -06:00
Qstick
1cbf61f4db New: Show definition name on add/edit screen for Cardigann 2022-01-17 19:23:08 -06:00
Qstick
34e57f27ff New: Don't close Indexer add list when adding Indexers 2022-01-17 18:45:40 -06:00
bakerboy448
de17ae9969 Fixed: (RuTracker org) Update Privacy to Semi-Private 2022-01-17 17:44:32 -06:00
bakerboy448
06913a2975 Fixed: (PornoLab) Update Privacy to Semi-Private 2022-01-17 17:44:21 -06:00
Qstick
dad16f2c57 Bump version to 0.2 2022-01-16 14:22:32 -06:00
François-Xavier Payet
2f22e7295c Changes Uri.ToString() to Uri.AbsoluteUri to prevent unescaping characters 2022-01-16 11:22:16 -06:00
François-Xavier Payet
6880f38635 Remove extra replacing (already done by Uri.EscapeDataString) 2022-01-16 11:22:16 -06:00
François-Xavier Payet
a7b1ef19f5 Replace WebUtility.UrlEncode to Uri.EscapeDataString 2022-01-16 11:22:16 -06:00
Yukine
aa59da2f22 Fixed: (MoreThanTV) use www url to fix cookie/redirect issues 2022-01-16 01:20:47 -06:00
Yukine
a62a4360e3 New: (MoreThanTV) Add MoreThanTV (#771)
* New: (MoreThanTV) Add MoreThanTV

* fix(ParseUtil): revert change since its already implemented elsewhere

* docs: clarify how to get cookies

* fix: set correct FieldDefinition order in Settings
2022-01-15 21:29:17 -06:00
Robin Dadswell
9e9e666204 Fixed: Postgres default port 2022-01-08 17:19:30 -06:00
bakerboy448
8d23cbf52b New: (DanishBytes) Add .org Alt Link
based on jackett bac28c030295bddeb2cc7934da6257dd013d8f60
2022-01-08 15:26:54 -06:00
Qstick
d925b37066 Update createSentryMiddleware.js 2022-01-08 15:12:07 -06:00
Qstick
9dadb35b98 Fixed: Don't die when definition doesn't exist
Fixes #596
2022-01-03 18:02:41 -06:00
Qstick
79e3e31028 Only dispatch search to enabled, non-failed, indexers
Fixes #712
2022-01-03 17:23:21 -06:00
Qstick
0af8e84d2d Remove unused sortByName import from indexerIndexActions 2022-01-03 16:43:22 -06:00
Qstick
573dde97e5 Default Method in HttpRequest to HttpMethod.Get 2022-01-03 14:13:29 -06:00
Qstick
08d112a96f New: Privacy custom filter option
Fixes #759
2022-01-03 12:35:05 -06:00
Qstick
76b6b0dead Use native HttpMethod 2022-01-03 00:28:20 -06:00
Qstick
e04133d34a More Mono Cleaning 2022-01-02 23:19:47 -06:00
Qstick
07575ae239 Fixed: (LazyLibrarian) Test indexer pull on setup to validate 2022-01-02 21:50:44 -06:00
Qstick
8e7acd8946 Fixed: (Mylar) Test indexer pull on setup to validate Mylar functionality 2022-01-02 21:50:44 -06:00
Qstick
3ecc926298 Fixed: (HDTorrents) Use Sanitized search string on all search types 2022-01-02 19:43:46 -06:00
Qstick
1e532624af Fixed: (Redacted) Guid and FL Parsing in line with Gazelle 2022-01-02 16:45:34 -06:00
Agneev Mukherjee
8a5194e604 Update index.ejs 2022-01-02 13:10:07 -06:00
Agneev Mukherjee
8a73cf72c2 Set login.html theme-color to color of logo 2022-01-02 13:10:07 -06:00
Qstick
76982c5988 Fixed: (Gazelle) Freeleech detection for releases 2022-01-01 20:08:59 -06:00
Qstick
b9dfe5e359 Fixed: (Gazelle) Use InfoUrl for GUID to avoid global duplicates 2022-01-01 20:01:40 -06:00
Qstick
a5e13ca776 New: Genre parameter for Music search 2022-01-01 15:04:05 -06:00
Qstick
e2ddfbff9c New: Genre parameter for Movie search 2022-01-01 14:49:01 -06:00
Qstick
66b4c7891d New: TmdbId Parameter for TV Search 2022-01-01 14:43:49 -06:00
Qstick
480a76c290 New: Support for language metadata 2022-01-01 14:23:44 -06:00
Servarr
1373ab255d Automated API Docs update 2021-12-31 19:41:04 -06:00
Qstick
1dc00eb445 More powerful label actions 2021-12-31 19:34:28 -06:00
Qstick
a366bec684 New: Reenable TV Id search for PrivateHD 2021-12-31 19:34:28 -06:00
Qstick
ecca6e9f49 New: TVDB and TMDB Search for AvistaZ
Ref #717
2021-12-31 19:34:13 -06:00
Yukine
03db7a9bbd Fixed: (SpeedApp) correct categories in query string of requests 2021-12-31 15:09:03 -06:00
Qstick
9cb04466c1 DbType param on update check 2021-12-31 13:19:22 -06:00
bakerboy448
2bae37d0c5 Fixed: (TorrentLeech) Calculating Incorrect Age
Closes #720
2021-12-31 11:04:31 -06:00
bakerboy448
0dbd23c52b Fixed: Various Translations 2021-12-30 23:34:48 -06:00
bakerboy448
66a6311dcc Fixed: SemiPublic => SemiPrivate 2021-12-30 23:00:19 -06:00
Servarr
c5b111530c Automated API Docs update 2021-12-30 22:37:20 -06:00
Qstick
77724a50a4 Really fix Github token for API PR 2021-12-30 21:42:46 -06:00
Qstick
22cbd01c57 Bump to 0.1.10 2021-12-30 19:03:04 -06:00
Yukine
fd55a624a7 Fixed: (AnimeBytes) Do not Page requests 2021-12-30 19:01:42 -06:00
Qstick
75984e954e Update LocalizationController.cs 2021-12-30 18:41:56 -06:00
Qstick
3fce120578 Fixed: (SpeedApp) Map Categories instead of building
Fixes #574
2021-12-30 18:30:58 -06:00
Qstick
6e8fb22c71 New: Additional logging for InvalidModel BadRequest API calls
[common]
2021-12-30 17:51:34 -06:00
Qstick
8ec7a4898d Maintain PrimaryKey and AutoIncrement on some schemas
[common]
2021-12-30 17:51:11 -06:00
Qstick
642848d331 API Annotations 2021-12-30 15:51:51 -06:00
Qstick
c9e6a0339e Fixed: (Cardigann) Indexer privacy tweaks, Semi-Public fixes
Fixes #744
2021-12-29 18:18:09 -06:00
Qstick
25620e8670 Fix GitHub token variable usage 2021-12-29 18:16:36 -06:00
Mouton99
5b804e8f3a New: (TorrentSeeds) Migrate to API & YML 2021-12-29 18:14:22 -06:00
Servarr
548db6a5cd Automated API Docs update 2021-12-29 17:48:53 -06:00
Qstick
7f28f64cbe Fix server settings on API docs 2021-12-28 18:47:44 -06:00
Qstick
9bad31af84 Eliminate PR Extension from pipeline 2021-12-28 18:26:51 -06:00
Qstick
01c7a05841 Only push to api-docs when changes 2021-12-28 17:52:15 -06:00
Qstick
9859b4a3d9 force it 2021-12-28 17:37:30 -06:00
Gabriel Sjöberg
177084fe8b Fixed: (Indexer) Update RARBG API query options
* Added app_id to captcha check to avoid 403 forbidden error
* Migrated app_id from hard coded to BuildInfo.AppName
2021-12-28 17:04:55 -06:00
Qstick
c57a91bc64 Skip build of doc only change, ignore PR errors for docs 2021-12-28 17:01:31 -06:00
Servarr
ca67a40c72 Automated API Docs update 2021-12-28 15:39:14 -06:00
Qstick
de7505bbe6 correctly push upstream 2021-12-28 15:27:54 -06:00
Qstick
97956ce951 Branch and push prior to PR 2021-12-28 15:10:56 -06:00
Qstick
8a38e124fd Speed up Checkout for Docs job 2021-12-28 14:45:28 -06:00
Qstick
38fcffe871 Identify user for git 2021-12-28 14:31:12 -06:00
Qstick
4c7b5a47d3 Autogenerated API docs 2021-12-28 13:43:45 -06:00
Qstick
34597e6ecb Boolean default should be a boolean
Fixes #729
2021-12-24 14:23:09 -06:00
bakerboy448
735be4f467 New: (TvVault) Mark as Obsolete per Site Bot Ban
Closes #573
2021-12-24 14:08:00 -06:00
bakerboy448
1c737d77fb Bump to 0.1.9 2021-12-24 14:07:42 -06:00
bakerboy448
55788ac04d Fixed: (Usenet) (DrunkenSlug) Update URL
https://api.drunkenslug.com gives a 301
2021-12-24 14:07:17 -06:00
bakerboy448
d108ab0339 Fix misleading Tags helptext [skip ci] 2021-12-24 14:06:42 -06:00
bakerboy448
5928eea83e Fixed: (PornoLab) Add new 2022 Categories
Based on Jackett f61a2b47400e68422ba6620a5ef2f5b4d0a929a3
2021-12-24 12:58:51 -06:00
bakerboy448
27898aa3b5 Fixed: (DanishBytes) Update Domain to .club
Based on Jackett f890ddd119a35eb4ba40b407aa65461c713b5e5d
2021-12-24 12:58:51 -06:00
bakerboy448
5e3322c538 New: OnApplicationUpdate Notifications
(based on Radarr Commits
9e175e28efcfc6ac3e414649b955a10fb0e951e7
4f5f9ff77ee4de05ba04cc677eb7baf4df726af5
4ebcbc28aa3e3268ecc37c5fc2b5565da8f13305
)
Fixes #723

Co-authored-by: Qstick <qstick@gmail.com>
2021-12-22 19:07:07 -06:00
bakerboy448
80c31e8660 fixup add rationale for Obsolete of C# Indexer Implementations 2021-12-18 09:34:51 -06:00
bakerboy448
46401ee187 Fixed: (BB) Remove '.' from Search String
based on jackett fbb1f15d7014b2d8c23c6ee94c2bcf37612066db
2021-12-18 09:27:26 -06:00
Davo1624
3610becc64 Fixed: (Orpheus) Drop Caps Support for Movie & TV Search
Drop TV and Movie search as no Movie/TV categories (#713)

e-learning results are how to play guitar videos, etc
comedy are comedy/standup recordings
2021-12-15 16:30:26 -06:00
Davo1624
06d9c157d8 Fixed: (Orpheus) Map Categories Comedy & E-Learning Videos to 'Other'
indexer does not actually have movies and tv
2021-12-15 16:27:50 -06:00
bakerboy448
d0d1f40128 Fixed: (Anilibria) Duplicate entries
Mark C# Anilibria as obsolete
2021-12-12 17:28:07 -06:00
bakerboy448
383d5464b7 New: (FlareSolverr Proxy) Configurable Request Timeout
Closes #696
2021-12-11 22:10:06 -06:00
Qstick
62d15536df Fixed: NullRef in SchemaBuilder when sending payload without optional Provider.Settings fields 2021-12-11 13:38:25 -06:00
ta264
147cdf2cce Fixed: Forms login persists across restarts in docker
(cherry picked from commit a219b4a1b869863b2ef47d4bdf33d308cb261ba3)

Fixes #409
2021-12-10 03:14:55 -06:00
bakerboy448
dd27d69e97 fix erroneous logging for windows service on non-windows 2021-12-10 03:14:23 -06:00
Robin Dadswell
32fd0911a2 New: Application Placeholders instead of default values 2021-12-09 15:19:11 -06:00
Robin Dadswell
0e6ec58a83 New: Placeholders in notification fields 2021-12-09 15:19:11 -06:00
Robin Dadswell
69f5963f6f New: Frontend Placeholders from the Backend 2021-12-09 15:19:11 -06:00
PearsonFlyer
6ca708f523 Fixed: (HDTorrents) Remove . from searches 2021-12-08 14:26:46 -06:00
ta264
9e7af8369e Fixed: Support older glibc in libMonoPosixHelper 2021-12-08 19:14:25 +00:00
Qstick
b05d8c930d Date Routines Test Cases 2021-12-07 20:19:05 -06:00
Qstick
6b886b938c New: Better Fuzzy DateTime Parse 2021-12-07 18:48:46 -06:00
ta264
4a7bf39723 Fixed: Speed up parsing DateTime 2021-12-06 21:57:22 +00:00
bakerboy448
7fcd320e23 Fixed: (PrivateHD) Drop support for IMDB search
imdb search does not support S/E params
this matches the behavior for TorrentLeech
Fixes #119
2021-12-06 21:12:37 -06:00
Qstick
88677ce236 Bump to 0.1.8 2021-12-06 17:19:04 -06:00
338 changed files with 7452 additions and 5501 deletions

16
.github/label-actions.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
# Configuration for Label Actions - https://github.com/dessant/label-actions
'Type: Support':
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
or [Subreddit](https://reddit.com/r/prowlarr)
close: true
'Type: Indexer Request':
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a indexer request. Please use our Indexer request [site](https://requests.prowlarr.com/)
close: true

23
.github/workflows/label-actions.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: 'Label Actions'
on:
issues:
types: [labeled, unlabeled]
pull_request:
types: [labeled, unlabeled]
discussion:
types: [labeled, unlabeled]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/label-actions@v2
with:
process-only: 'issues, prs'

View File

@@ -1,21 +0,0 @@
name: 'Support requests'
on:
issues:
types: [labeled, unlabeled, reopened]
jobs:
support:
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v2
with:
github-token: ${{ github.token }}
support-label: 'Type: Support'
issue-comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://prowlarr.com/discord)
or [Subreddit](https://reddit.com/r/prowlarr)
close-issue: true
lock-issue: false

4
.gitignore vendored
View File

@@ -188,6 +188,10 @@ packages.config.md5sum
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink
node_modules
node_modules.nosync
# API doc generation
.config/

View File

@@ -7,13 +7,15 @@ variables:
outputFolder: './_output'
artifactsFolder: './_artifacts'
testsFolder: './_tests'
majorVersion: '0.1.7'
majorVersion: '0.2.0'
minorVersion: $[counter('minorVersion', 1)]
prowlarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.100'
dotnetVersion: '6.0.201'
innoVersion: '6.2.0'
nodeVersion: '16.x'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
trigger:
@@ -29,6 +31,7 @@ pr:
paths:
exclude:
- src/NzbDrone.Core/Localization/Core
- src/Prowlarr.API.*/openapi.json
stages:
- stage: Setup
@@ -154,7 +157,7 @@ stages:
- task: NodeTool@0
displayName: Set Node.js version
inputs:
versionSpec: '12.x'
versionSpec: $(nodeVersion)
- checkout: self
submodules: true
fetchDepth: 1
@@ -199,16 +202,11 @@ stages:
artifactName: WindowsFrontend
targetPath: _output
displayName: Fetch Frontend
- bash: ./build.sh --packages
displayName: Create Packages
- bash: |
distribution/windows/setup/inno/ISCC.exe distribution/windows/setup/prowlarr.iss //DFramework=net6.0 //DRuntime=win-x86
cp distribution/windows/setup/output/Prowlarr.*windows.net6.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x86-installer.exe
displayName: Create x86 .NET Core Windows installer
- bash: |
distribution/windows/setup/inno/ISCC.exe distribution/windows/setup/prowlarr.iss //DFramework=net6.0 //DRuntime=win-x64
cp distribution/windows/setup/output/Prowlarr.*windows.net6.0.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe
displayName: Create x64 .NET Core Windows installer
./build.sh --packages --installer
cp distribution/windows/setup/output/Prowlarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x64-installer.exe
cp distribution/windows/setup/output/Prowlarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Prowlarr.${BUILDNAME}.windows-core-x86-installer.exe
displayName: Create Installers
- publish: $(Build.ArtifactStagingDirectory)
artifact: 'WindowsInstaller'
displayName: Publish Installer
@@ -705,17 +703,17 @@ stages:
osName: 'Linux'
imageName: 'ubuntu-18.04'
pattern: 'Prowlarr.*.linux-core-x64.tar.gz'
failBuild: false
failBuild: true
Mac:
osName: 'Mac'
imageName: 'macos-10.15'
pattern: 'Prowlarr.*.osx-core-x64.tar.gz'
failBuild: false
failBuild: true
Windows:
osName: 'Windows'
imageName: 'windows-2019'
pattern: 'Prowlarr.*.windows-core-x64.zip'
failBuild: false
failBuild: true
pool:
vmImage: $(imageName)
@@ -806,7 +804,7 @@ stages:
- task: NodeTool@0
displayName: Set Node.js version
inputs:
versionSpec: '12.x'
versionSpec: $(nodeVersion)
- checkout: self
submodules: true
fetchDepth: 1
@@ -823,6 +821,59 @@ stages:
FORCE_COLOR: 0
YARN_CACHE_FOLDER: $(yarnCacheFolder)
- job: Api_Docs
displayName: API Docs
dependsOn: Prepare
condition: |
and
(
and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
)
pool:
vmImage: windows-2019
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 1
- bash: ./docs.sh Windows
displayName: Create openapi.json
- bash: |
git config --global user.email "development@lidarr.audio"
git config --global user.name "Servarr"
git checkout -b api-docs
git add .
if git status | grep -q modified
then
git commit -am 'Automated API Docs update'
git push -f --set-upstream origin api-docs
curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/prowlarr/prowlarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
else
echo "No changes since last run"
fi
displayName: Commit API Doc Change
continueOnError: true
env:
GITHUBTOKEN: $(githubToken)
- task: CopyFiles@2
displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: |
**/*openapi.json
TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
- publish: $(Build.ArtifactStagingDirectory)/api_docs
artifact: 'APIDocs'
displayName: Publish API Docs Bundle
condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
- job: Analyze_Backend
displayName: Backend
dependsOn: Prepare
@@ -890,7 +941,7 @@ stages:
- job:
displayName: Discord Notification
pool:
vmImage: 'windows-2019'
vmImage: 'ubuntu-18.04'
steps:
- task: DownloadPipelineArtifact@2
continueOnError: true

View File

@@ -234,6 +234,32 @@ Package()
esac
}
BuildInstaller()
{
local framework="$1"
local runtime="$2"
./_inno/ISCC.exe distribution/windows/setup/prowlarr.iss "//DFramework=$framework" "//DRuntime=$runtime"
}
InstallInno()
{
ProgressStart "Installing portable Inno Setup"
rm -rf _inno
curl -s --output innosetup.exe "https://files.jrsoftware.org/is/6/innosetup-${INNOVERSION:-6.2.0}.exe"
mkdir _inno
./innosetup.exe //portable=1 //silent //currentuser //dir=.\\_inno
rm innosetup.exe
ProgressEnd "Installed portable Inno Setup"
}
RemoveInno()
{
rm -rf _inno
}
PackageTests()
{
local framework="$1"
@@ -265,6 +291,7 @@ if [ $# -eq 0 ]; then
BACKEND=YES
FRONTEND=YES
PACKAGES=YES
INSTALLER=NO
LINT=YES
ENABLE_BSD=NO
fi
@@ -300,6 +327,10 @@ case $key in
PACKAGES=YES
shift # past argument
;;
--installer)
INSTALLER=YES
shift # past argument
;;
--lint)
LINT=YES
shift # past argument
@@ -383,3 +414,11 @@ then
Package "$FRAMEWORK" "$RID"
fi
fi
if [ "$INSTALLER" = "YES" ];
then
InstallInno
BuildInstaller "net6.0" "win-x64"
BuildInstaller "net6.0" "win-x86"
RemoveInno
fi

View File

@@ -15,7 +15,7 @@
<key>CFBundleIconFile</key>
<string>prowlarr.icns</string>
<key>CFBundleIdentifier</key>
<string>com.osx.prowlarr.video</string>
<string>com.osx.prowlarr.com</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@@ -1,3 +0,0 @@
#SET BUILD_NUMBER=1
#SET branch=develop
inno\ISCC.exe prowlarr.iss

View File

@@ -1,336 +0,0 @@
; *** Inno Setup version 5.5.3+ English messages ***
;
; To download user-contributed translations of this file, go to:
; http://www.jrsoftware.org/files/istrans/
;
; Note: When translating this text, do not add periods (.) to the end of
; messages that didn't have them already, because on those messages Inno
; Setup adds the periods automatically (appending a period would result in
; two periods being displayed).
[LangOptions]
; The following three entries are very important. Be sure to read and
; understand the '[LangOptions] section' topic in the help file.
LanguageName=English
LanguageID=$0409
LanguageCodePage=0
; If the language you are translating to requires special font faces or
; sizes, uncomment any of the following entries and change them accordingly.
;DialogFontName=
;DialogFontSize=8
;WelcomeFontName=Verdana
;WelcomeFontSize=12
;TitleFontName=Arial
;TitleFontSize=29
;CopyrightFontName=Arial
;CopyrightFontSize=8
[Messages]
; *** Application titles
SetupAppTitle=Setup
SetupWindowTitle=Setup - %1
UninstallAppTitle=Uninstall
UninstallAppFullTitle=%1 Uninstall
; *** Misc. common
InformationTitle=Information
ConfirmTitle=Confirm
ErrorTitle=Error
; *** SetupLdr messages
SetupLdrStartupMessage=This will install %1. Do you wish to continue?
LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted
LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted
; *** Startup error messages
LastErrorMessage=%1.%n%nError %2: %3
SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program.
SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program.
SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program.
InvalidParameter=An invalid parameter was passed on the command line:%n%n%1
SetupAlreadyRunning=Setup is already running.
WindowsVersionNotSupported=This program does not support the version of Windows your computer is running.
WindowsServicePackRequired=This program requires %1 Service Pack %2 or later.
NotOnThisPlatform=This program will not run on %1.
OnlyOnThisPlatform=This program must be run on %1.
OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1
MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1.
WinVersionTooLowError=This program requires %1 version %2 or later.
WinVersionTooHighError=This program cannot be installed on %1 version %2 or later.
AdminPrivilegesRequired=You must be logged in as an administrator when installing this program.
PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program.
SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
; *** Misc. errors
ErrorCreatingDir=Setup was unable to create the directory "%1"
ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files
; *** Setup common messages
ExitSetupTitle=Exit Setup
ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup?
AboutSetupMenuItem=&About Setup...
AboutSetupTitle=About Setup
AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4
AboutSetupNote=
TranslatorNote=
; *** Buttons
ButtonBack=< &Back
ButtonNext=&Next >
ButtonInstall=&Install
ButtonOK=OK
ButtonCancel=Cancel
ButtonYes=&Yes
ButtonYesToAll=Yes to &All
ButtonNo=&No
ButtonNoToAll=N&o to All
ButtonFinish=&Finish
ButtonBrowse=&Browse...
ButtonWizardBrowse=B&rowse...
ButtonNewFolder=&Make New Folder
; *** "Select Language" dialog messages
SelectLanguageTitle=Select Setup Language
SelectLanguageLabel=Select the language to use during the installation:
; *** Common wizard text
ClickNext=Click Next to continue, or Cancel to exit Setup.
BeveledLabel=
BrowseDialogTitle=Browse For Folder
BrowseDialogLabel=Select a folder in the list below, then click OK.
NewFolderName=New Folder
; *** "Welcome" wizard page
WelcomeLabel1=Welcome to the [name] Setup Wizard
WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing.
; *** "Password" wizard page
WizardPassword=Password
PasswordLabel1=This installation is password protected.
PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive.
PasswordEditLabel=&Password:
IncorrectPassword=The password you entered is not correct. Please try again.
; *** "License Agreement" wizard page
WizardLicense=License Agreement
LicenseLabel=Please read the following important information before continuing.
LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation.
LicenseAccepted=I &accept the agreement
LicenseNotAccepted=I &do not accept the agreement
; *** "Information" wizard pages
WizardInfoBefore=Information
InfoBeforeLabel=Please read the following important information before continuing.
InfoBeforeClickLabel=When you are ready to continue with Setup, click Next.
WizardInfoAfter=Information
InfoAfterLabel=Please read the following important information before continuing.
InfoAfterClickLabel=When you are ready to continue with Setup, click Next.
; *** "User Information" wizard page
WizardUserInfo=User Information
UserInfoDesc=Please enter your information.
UserInfoName=&User Name:
UserInfoOrg=&Organization:
UserInfoSerial=&Serial Number:
UserInfoNameRequired=You must enter a name.
; *** "Select Destination Location" wizard page
WizardSelectDir=Select Destination Location
SelectDirDesc=Where should [name] be installed?
SelectDirLabel3=Setup will install [name] into the following folder.
SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
DiskSpaceMBLabel=At least [mb] MB of free disk space is required.
CannotInstallToNetworkDrive=Setup cannot install to a network drive.
CannotInstallToUNCPath=Setup cannot install to a UNC path.
InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share
InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another.
DiskSpaceWarningTitle=Not Enough Disk Space
DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway?
DirNameTooLong=The folder name or path is too long.
InvalidDirName=The folder name is not valid.
BadDirName32=Folder names cannot include any of the following characters:%n%n%1
DirExistsTitle=Folder Exists
DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway?
DirDoesntExistTitle=Folder Does Not Exist
DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created?
; *** "Select Components" wizard page
WizardSelectComponents=Select Components
SelectComponentsDesc=Which components should be installed?
SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue.
FullInstallation=Full installation
; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
CompactInstallation=Compact installation
CustomInstallation=Custom installation
NoUninstallWarningTitle=Components Exist
NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway?
ComponentSize1=%1 KB
ComponentSize2=%1 MB
ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space.
; *** "Select Additional Tasks" wizard page
WizardSelectTasks=Select Additional Tasks
SelectTasksDesc=Which additional tasks should be performed?
SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next.
; *** "Select Start Menu Folder" wizard page
WizardSelectProgramGroup=Select Start Menu Folder
SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts?
SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder.
SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse.
MustEnterGroupName=You must enter a folder name.
GroupNameTooLong=The folder name or path is too long.
InvalidGroupName=The folder name is not valid.
BadGroupName=The folder name cannot include any of the following characters:%n%n%1
NoProgramGroupCheck2=&Don't create a Start Menu folder
; *** "Ready to Install" wizard page
WizardReady=Ready to Install
ReadyLabel1=Setup is now ready to begin installing [name] on your computer.
ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings.
ReadyLabel2b=Click Install to continue with the installation.
ReadyMemoUserInfo=User information:
ReadyMemoDir=Destination location:
ReadyMemoType=Setup type:
ReadyMemoComponents=Selected components:
ReadyMemoGroup=Start Menu folder:
ReadyMemoTasks=Additional tasks:
; *** "Preparing to Install" wizard page
WizardPreparing=Preparing to Install
PreparingDesc=Setup is preparing to install [name] on your computer.
PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name].
CannotContinue=Setup cannot continue. Please click Cancel to exit.
ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications.
ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications.
CloseApplications=&Automatically close the applications
DontCloseApplications=&Do not close the applications
ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing.
; *** "Installing" wizard page
WizardInstalling=Installing
InstallingLabel=Please wait while Setup installs [name] on your computer.
; *** "Setup Completed" wizard page
FinishedHeadingLabel=Completing the [name] Setup Wizard
FinishedLabelNoIcons=Setup has finished installing [name] on your computer.
FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons.
ClickFinish=Click Finish to exit Setup.
FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now?
FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now?
ShowReadmeCheck=Yes, I would like to view the README file
YesRadio=&Yes, restart the computer now
NoRadio=&No, I will restart the computer later
; used for example as 'Run MyProg.exe'
RunEntryExec=Run %1
; used for example as 'View Readme.txt'
RunEntryShellExec=View %1
; *** "Setup Needs the Next Disk" stuff
ChangeDiskTitle=Setup Needs the Next Disk
SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse.
PathLabel=&Path:
FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder.
SelectDirectoryLabel=Please specify the location of the next disk.
; *** Installation phase messages
SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again.
EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation.
; *** Installation status messages
StatusClosingApplications=Closing applications...
StatusCreateDirs=Creating directories...
StatusExtractFiles=Extracting files...
StatusCreateIcons=Creating shortcuts...
StatusCreateIniEntries=Creating INI entries...
StatusCreateRegistryEntries=Creating registry entries...
StatusRegisterFiles=Registering files...
StatusSavingUninstall=Saving uninstall information...
StatusRunProgram=Finishing installation...
StatusRestartingApplications=Restarting applications...
StatusRollback=Rolling back changes...
; *** Misc. errors
ErrorInternal2=Internal error: %1
ErrorFunctionFailedNoCode=%1 failed
ErrorFunctionFailed=%1 failed; code %2
ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3
ErrorExecutingProgram=Unable to execute file:%n%1
; *** Registry errors
ErrorRegOpenKey=Error opening registry key:%n%1\%2
ErrorRegCreateKey=Error creating registry key:%n%1\%2
ErrorRegWriteKey=Error writing to registry key:%n%1\%2
; *** INI errors
ErrorIniEntry=Error creating INI entry in file "%1".
; *** File copying errors
FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation.
FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation.
SourceIsCorrupted=The source file is corrupted
SourceDoesntExist=The source file "%1" does not exist
ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation.
ErrorReadingExistingDest=An error occurred while trying to read the existing file:
FileExists=The file already exists.%n%nWould you like Setup to overwrite it?
ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file?
ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file:
ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory:
ErrorReadingSource=An error occurred while trying to read the source file:
ErrorCopying=An error occurred while trying to copy a file:
ErrorReplacingExistingFile=An error occurred while trying to replace the existing file:
ErrorRestartReplace=RestartReplace failed:
ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory:
ErrorRegisterServer=Unable to register the DLL/OCX: %1
ErrorRegSvr32Failed=RegSvr32 failed with exit code %1
ErrorRegisterTypeLib=Unable to register the type library: %1
; *** Post-installation errors
ErrorOpeningReadme=An error occurred while trying to open the README file.
ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually.
; *** Uninstaller messages
UninstallNotFound=File "%1" does not exist. Cannot uninstall.
UninstallOpenError=File "%1" could not be opened. Cannot uninstall
UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall
UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log
ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components?
UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows.
OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges.
UninstallStatusLabel=Please wait while %1 is removed from your computer.
UninstalledAll=%1 was successfully removed from your computer.
UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually.
UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now?
UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall
; *** Uninstallation phase messages
ConfirmDeleteSharedFileTitle=Remove Shared File?
ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm.
SharedFileNameLabel=File name:
SharedFileLocationLabel=Location:
WizardUninstalling=Uninstall Status
StatusUninstalling=Uninstalling %1...
; *** Shutdown block reasons
ShutdownBlockReasonInstallingApp=Installing %1.
ShutdownBlockReasonUninstallingApp=Uninstalling %1.
; The custom messages below aren't used by Setup itself, but if you make
; use of them in your scripts, you'll want to translate them.
[CustomMessages]
NameAndVersion=%1 version %2
AdditionalIcons=Additional icons:
CreateDesktopIcon=Create a &desktop icon
CreateQuickLaunchIcon=Create a &Quick Launch icon
ProgramOnTheWeb=%1 on the Web
UninstallProgram=Uninstall %1
LaunchProgram=Launch %1
AssocFileExtension=&Associate %1 with the %2 file extension
AssocingFileExtension=Associating %1 with the %2 file extension...
AutoStartProgramGroupDescription=Startup:
AutoStartProgram=Automatically start %1
AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway?

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -3,13 +3,12 @@
#define AppName "Prowlarr"
#define AppPublisher "Team Prowlarr"
#define AppURL "https://prowlarr.video/"
#define ForumsURL "https://forums.prowlarr.video/"
#define AppURL "https://prowlarr.com/"
#define ForumsURL "https://prowlarr.com/discord/"
#define AppExeName "Prowlarr.exe"
#define BaseVersion GetEnv('MAJORVERSION')
#define BuildNumber GetEnv('MINORVERSION')
#define BuildVersion GetEnv('PROWLARRVERSION')
#define BranchName GetEnv('BUILD_SOURCEBRANCHNAME')
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
@@ -22,15 +21,15 @@ AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#ForumsURL}
AppUpdatesURL={#AppURL}
DefaultDirName={commonappdata}\Prowlarr\bin
DefaultDirName={commonappdata}\Prowlarr
DisableDirPage=yes
DefaultGroupName={#AppName}
DisableProgramGroupPage=yes
OutputBaseFilename=Prowlarr.{#BranchName}.{#BuildVersion}.windows.{#Framework}
OutputBaseFilename=Prowlarr.{#BuildVersion}.{#Runtime}
SolidCompression=yes
AppCopyright=Creative Commons 3.0 License
AllowUNCPath=False
UninstallDisplayIcon={app}\Prowlarr.exe
UninstallDisplayIcon={app}\bin\Prowlarr.exe
DisableReadyPage=True
CompressionThreads=2
Compression=lzma2/normal
@@ -38,6 +37,7 @@ AppContact={#ForumsURL}
VersionInfoVersion={#BaseVersion}.{#BuildNumber}
SetupLogging=yes
OutputDir=output
WizardStyle=modern
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -48,28 +48,31 @@ Name: "windowsService"; Description: "Install Windows Service (Starts when the c
Name: "startupShortcut"; Description: "Create shortcut in Startup folder (Starts when you log into Windows)"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
Name: "none"; Description: "Do not start automatically"; GroupDescription: "Start automatically"; Flags: exclusive unchecked
[Dirs]
Name: "{app}"; Permissions: users-modify
[Files]
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\Prowlarr.exe"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "..\..\..\_artifacts\{#Runtime}\{#Framework}\Prowlarr\*"; Excludes: "Prowlarr.Update"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Parameters: "/icon"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Parameters: "/icon"; Tasks: desktopIcon
Name: "{userstartup}\{#AppName}"; Filename: "{app}\Prowlarr.exe"; WorkingDir: "{app}"; Tasks: startupShortcut
Name: "{group}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\bin\{#AppExeName}"; Parameters: "/icon"; Tasks: desktopIcon
Name: "{userstartup}\{#AppName}"; Filename: "{app}\bin\Prowlarr.exe"; WorkingDir: "{app}\bin"; Tasks: startupShortcut
[InstallDelete]
Name: "{app}"; Type: filesandordirs
Name: "{app}\bin"; Type: filesandordirs
[Run]
Filename: "{app}\Prowlarr.Console.exe"; StatusMsg: "Removing previous Windows Service"; Parameters: "/u /exitimmediately"; Flags: runhidden waituntilterminated;
Filename: "{app}\Prowlarr.Console.exe"; Description: "Enable Access from Other Devices"; StatusMsg: "Enabling Remote access"; Parameters: "/registerurl /exitimmediately"; Flags: postinstall runascurrentuser runhidden waituntilterminated; Tasks: startupShortcut none;
Filename: "{app}\Prowlarr.Console.exe"; StatusMsg: "Installing Windows Service"; Parameters: "/i /exitimmediately"; Flags: runhidden waituntilterminated; Tasks: windowsService
Filename: "{app}\Prowlarr.exe"; Description: "Open Prowlarr Web UI"; Flags: postinstall skipifsilent nowait; Tasks: windowsService;
Filename: "{app}\Prowlarr.exe"; Description: "Start Prowlarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
Filename: "{app}\bin\Prowlarr.Console.exe"; StatusMsg: "Removing previous Windows Service"; Parameters: "/u /exitimmediately"; Flags: runhidden waituntilterminated;
Filename: "{app}\bin\Prowlarr.Console.exe"; Description: "Enable Access from Other Devices"; StatusMsg: "Enabling Remote access"; Parameters: "/registerurl /exitimmediately"; Flags: postinstall runascurrentuser runhidden waituntilterminated; Tasks: startupShortcut none;
Filename: "{app}\bin\Prowlarr.Console.exe"; StatusMsg: "Installing Windows Service"; Parameters: "/i /exitimmediately"; Flags: runhidden waituntilterminated; Tasks: windowsService
Filename: "{app}\bin\Prowlarr.exe"; Description: "Open Prowlarr Web UI"; Flags: postinstall skipifsilent nowait; Tasks: windowsService;
Filename: "{app}\bin\Prowlarr.exe"; Description: "Start Prowlarr"; Flags: postinstall skipifsilent nowait; Tasks: startupShortcut none;
[UninstallRun]
Filename: "{app}\prowlarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
Filename: "{app}\bin\prowlarr.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist
[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;

38
docs.sh Normal file
View File

@@ -0,0 +1,38 @@
PLATFORM=$1
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
elif [ "$PLATFORM" = "Linux" ]; then
WHERE="linux-x64"
elif [ "$PLATFORM" = "Mac" ]; then
WHERE="osx-x64"
else
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
exit 1
fi
outputFolder='_output'
testPackageFolder='_tests'
rm -rf $outputFolder
rm -rf $testPackageFolder
slnFile=src/Prowlarr.sln
platform=Posix
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
dotnet tool install --version 6.3.0 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Prowlarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/prowlarr.console.dll" v1 &
sleep 30
kill %1
exit 0

View File

@@ -77,7 +77,9 @@ function AppUpdatedModalContent(props) {
<div>
{
!update.changes &&
<div className={styles.maintenance}>Maintenance release</div>
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
}
{

View File

@@ -166,7 +166,9 @@ class FilterBuilderModalContent extends Component {
</div>
</div>
<div className={styles.label}>Filters</div>
<div className={styles.label}>
{translate('Filters')}
</div>
<div className={styles.rows}>
{

View File

@@ -8,6 +8,7 @@ import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
import PrivacyFilterBuilderRowValue from './PrivacyFilterBuilderRowValue';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
import TagFilterBuilderRowValueConnector from './TagFilterBuilderRowValueConnector';
import styles from './FilterBuilderRow.css';
@@ -63,6 +64,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.PROTOCOL:
return ProtocolFilterBuilderRowValue;
case filterBuilderValueTypes.PRIVACY:
return PrivacyFilterBuilderRowValue;
case filterBuilderValueTypes.TAG:
return TagFilterBuilderRowValueConnector;

View File

@@ -0,0 +1,20 @@
import React from 'react';
import translate from 'Utilities/String/translate';
import FilterBuilderRowValue from './FilterBuilderRowValue';
const privacyTypes = [
{ id: 'public', name: translate('Public') },
{ id: 'private', name: translate('Private') },
{ id: 'semiPrivate', name: translate('SemiPrivate') }
];
function PrivacyFilterBuilderRowValue(props) {
return (
<FilterBuilderRowValue
tagList={privacyTypes}
{...props}
/>
);
}
export default PrivacyFilterBuilderRowValue;

View File

@@ -68,6 +68,7 @@ function ProviderFieldFormGroup(props) {
label,
helpText,
helpLink,
placeholder,
value,
type,
advanced,
@@ -100,6 +101,7 @@ function ProviderFieldFormGroup(props) {
label={label}
helpText={helpText}
helpLink={helpLink}
placeholder={placeholder}
value={value}
values={getSelectValues(selectOptions)}
errors={errors}
@@ -125,6 +127,7 @@ ProviderFieldFormGroup.propTypes = {
label: PropTypes.string,
helpText: PropTypes.string,
helpLink: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.any,
type: PropTypes.string.isRequired,
advanced: PropTypes.bool.isRequired,

View File

@@ -47,10 +47,6 @@ class Link extends Component {
el = 'a';
linkProps.href = to;
linkProps.target = target || '_self';
} else if (to.startsWith(`${window.Prowlarr.urlBase}/`)) {
el = RouterLink;
linkProps.to = to;
linkProps.target = target;
} else {
el = RouterLink;
linkProps.to = `${window.Prowlarr.urlBase}/${to.replace(/^\//, '')}`;

View File

@@ -37,7 +37,8 @@ function ModalError(props) {
{translate('Close')}
</Button>
</ModalFooter>
</ModalContent>);
</ModalContent>
);
}
ModalError.propTypes = {

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Scrollbars } from 'react-custom-scrollbars';
import { Scrollbars } from 'react-custom-scrollbars-2';
import { scrollDirections } from 'Helpers/Props';
import styles from './OverlayScroller.css';

View File

@@ -4,6 +4,7 @@ export const DATE = 'date';
export const DEFAULT = 'default';
export const INDEXER = 'indexer';
export const PROTOCOL = 'protocol';
export const PRIVACY = 'privacy';
export const APP_PROFILE = 'appProfile';
export const MOVIE_STATUS = 'movieStatus';
export const TAG = 'tag';

View File

@@ -41,7 +41,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Query Results'}
title={translate('QueryResults')}
data={queryResults ? queryResults : '-'}
/>
}
@@ -49,7 +49,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Categories'}
title={translate('Categories')}
data={categories ? categories : '-'}
/>
}
@@ -57,7 +57,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Source'}
title={translate('Source')}
data={source}
/>
}
@@ -65,7 +65,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Url'}
title={translate('Url')}
data={url ? <Link to={url}>{translate('Link')}</Link> : '-'}
/>
}
@@ -93,7 +93,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Source'}
title={translate('Source')}
data={source ? source : '-'}
/>
}
@@ -101,7 +101,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Title'}
title={translate('Title')}
data={title ? title : '-'}
/>
}
@@ -109,7 +109,7 @@ function HistoryDetails(props) {
{
!!data &&
<DescriptionListItem
title={'Url'}
title={translate('Url')}
data={url ? <Link to={url}>{translate('Link')}</Link> : '-'}
/>
}

View File

@@ -4,6 +4,7 @@ import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
class HistoryOptions extends Component {
@@ -56,14 +57,14 @@ class HistoryOptions extends Component {
return (
<Fragment>
<FormGroup>
<FormLabel>History Cleanup</FormLabel>
<FormLabel>{translate('HistoryCleanup')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="historyCleanupDays"
value={historyCleanupDays}
helpText="Set to 0 to disable automatic cleanup"
helpTextWarning="History items older than the selected number of days will be cleaned up automatically"
helpText={translate('HistoryCleanupDaysHelpText')}
helpTextWarning={translate('HistoryCleanupDaysHelpTextWarning')}
onChange={this.onGlobalInputChange}
/>
</FormGroup>

View File

@@ -4,7 +4,7 @@ import Modal from 'Components/Modal/Modal';
import AddIndexerModalContentConnector from './AddIndexerModalContentConnector';
import styles from './AddIndexerModal.css';
function AddIndexerModal({ isOpen, onModalClose, ...otherProps }) {
function AddIndexerModal({ isOpen, onModalClose, onSelectIndexer, ...otherProps }) {
return (
<Modal
isOpen={isOpen}
@@ -14,6 +14,7 @@ function AddIndexerModal({ isOpen, onModalClose, ...otherProps }) {
<AddIndexerModalContentConnector
{...otherProps}
onModalClose={onModalClose}
onSelectIndexer={onSelectIndexer}
/>
</Modal>
);
@@ -21,7 +22,8 @@ function AddIndexerModal({ isOpen, onModalClose, ...otherProps }) {
AddIndexerModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
onModalClose: PropTypes.func.isRequired,
onSelectIndexer: PropTypes.func.isRequired
};
export default AddIndexerModal;

View File

@@ -15,7 +15,7 @@ import TableBody from 'Components/Table/TableBody';
import { kinds, scrollDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import SelectIndexerRow from './SelectIndexerRow';
import SelectIndexerRowConnector from './SelectIndexerRowConnector';
import styles from './AddIndexerModalContent.css';
const columns = [
@@ -37,6 +37,12 @@ const columns = [
isSortable: true,
isVisible: true
},
{
name: 'description',
label: translate('Description'),
isSortable: false,
isVisible: true
},
{
name: 'privacy',
label: translate('Privacy'),
@@ -56,6 +62,21 @@ const protocols = [
}
];
const privacyLevels = [
{
key: 'private',
value: translate('Private')
},
{
key: 'semiPrivate',
value: translate('SemiPrivate')
},
{
key: 'public',
value: translate('Public')
}
];
class AddIndexerModalContent extends Component {
//
@@ -99,10 +120,6 @@ class AddIndexerModalContent extends Component {
.sort((a, b) => a.localeCompare(b))
.map((language) => ({ key: language, value: language }));
const privacyLevels = Array.from(new Set(indexers.map(({ privacy }) => privacy)))
.sort((a, b) => a.localeCompare(b))
.map((privacy) => ({ key: privacy, value: privacy }));
const filteredIndexers = indexers.filter((indexer) => {
const { filter, filterProtocols, filterLanguages, filterPrivacyLevels } = this.state;
@@ -125,12 +142,12 @@ class AddIndexerModalContent extends Component {
return true;
});
const errorMessage = getErrorMessage(error, 'Unable to load indexers');
const errorMessage = getErrorMessage(error, translate('UnableToLoadIndexers'));
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Add Indexer
{translate('AddIndexer')}
</ModalHeader>
<ModalBody
@@ -208,7 +225,7 @@ class AddIndexerModalContent extends Component {
<TableBody>
{
filteredIndexers.map((indexer) => (
<SelectIndexerRow
<SelectIndexerRowConnector
key={indexer.name}
implementation={indexer.implementation}
{...indexer}

View File

@@ -51,7 +51,7 @@ class AddIndexerModalContentConnector extends Component {
onIndexerSelect = ({ implementation, name }) => {
this.props.selectIndexerSchema({ implementation, name });
this.props.onModalClose({ indexerSelected: true });
this.props.onSelectIndexer();
};
onSortPress = (sortKey, sortDirection) => {
@@ -76,7 +76,8 @@ AddIndexerModalContentConnector.propTypes = {
fetchIndexerSchema: PropTypes.func.isRequired,
selectIndexerSchema: PropTypes.func.isRequired,
setIndexerSchemaSort: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
onModalClose: PropTypes.func.isRequired,
onSelectIndexer: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(AddIndexerModalContentConnector);

View File

@@ -3,3 +3,9 @@
width: 32px;
}
.alreadyExistsIcon {
margin-left: 10px;
color: #37bc9b;
pointer-events: all;
}

View File

@@ -1,8 +1,12 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRowButton from 'Components/Table/TableRowButton';
import { icons } from 'Helpers/Props';
import ProtocolLabel from 'Indexer/Index/Table/ProtocolLabel';
import firstCharToUpper from 'Utilities/String/firstCharToUpper';
import translate from 'Utilities/String/translate';
import styles from './SelectIndexerRow.css';
class SelectIndexerRow extends Component {
@@ -27,7 +31,9 @@ class SelectIndexerRow extends Component {
protocol,
privacy,
name,
language
language,
description,
isExistingIndexer
} = this.props;
return (
@@ -40,6 +46,16 @@ class SelectIndexerRow extends Component {
<TableRowCell>
{name}
{
isExistingIndexer ?
<Icon
className={styles.alreadyExistsIcon}
name={icons.CHECK_CIRCLE}
size={15}
title={translate('IndexerAlreadySetup')}
/> :
null
}
</TableRowCell>
<TableRowCell>
@@ -47,7 +63,11 @@ class SelectIndexerRow extends Component {
</TableRowCell>
<TableRowCell>
{privacy}
{description}
</TableRowCell>
<TableRowCell>
{translate(firstCharToUpper(privacy))}
</TableRowCell>
</TableRowButton>
);
@@ -59,8 +79,10 @@ SelectIndexerRow.propTypes = {
protocol: PropTypes.string.isRequired,
privacy: PropTypes.string.isRequired,
language: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
implementation: PropTypes.string.isRequired,
onIndexerSelect: PropTypes.func.isRequired
onIndexerSelect: PropTypes.func.isRequired,
isExistingIndexer: PropTypes.bool.isRequired
};
export default SelectIndexerRow;

View File

@@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createExistingIndexerSelector from 'Store/Selectors/createExistingIndexerSelector';
import SelectIndexerRow from './SelectIndexerRow';
function createMapStateToProps() {
return createSelector(
createExistingIndexerSelector(),
(isExistingIndexer, dimensions) => {
return {
isExistingIndexer
};
}
);
}
export default connect(createMapStateToProps)(SelectIndexerRow);

View File

@@ -39,6 +39,7 @@ function EditIndexerModalContent(props) {
const {
id,
implementationName,
definitionName,
name,
enable,
redirect,
@@ -50,10 +51,12 @@ function EditIndexerModalContent(props) {
priority
} = item;
const indexerDisplayName = implementationName === definitionName ? implementationName : `${implementationName} (${definitionName})`;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{`${id ? translate('EditIndexer') : translate('AddIndexer')} - ${implementationName}`}
{`${id ? translate('EditIndexer') : translate('AddIndexer')} - ${indexerDisplayName}`}
</ModalHeader>
<ModalBody>
@@ -159,7 +162,7 @@ function EditIndexerModalContent(props) {
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText="Use tags to specify default clients, specify Indexer Proxies, or just to organize your indexers."
helpText={translate('IndexerTagsHelpText')}
{...tags}
onChange={onInputChange}
/>

View File

@@ -193,11 +193,12 @@ class IndexerIndex extends Component {
this.setState({ isAddIndexerModalOpen: true });
};
onAddIndexerModalClose = ({ indexerSelected = false } = {}) => {
this.setState({
isAddIndexerModalOpen: false,
isEditIndexerModalOpen: indexerSelected
});
onAddIndexerModalClose = () => {
this.setState({ isAddIndexerModalOpen: false });
};
onAddIndexerSelectIndexer = () => {
this.setState({ isEditIndexerModalOpen: true });
};
onEditIndexerModalClose = () => {
@@ -302,14 +303,14 @@ class IndexerIndex extends Component {
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label={'Add Indexer'}
label={translate('AddIndexer')}
iconName={icons.ADD}
spinningName={icons.ADD}
onPress={this.onAddIndexerPress}
/>
<PageToolbarButton
label={'Test All Indexers'}
label={translate('TestAllIndexers')}
iconName={icons.TEST}
isSpinning={isTestingAll}
isDisabled={hasNoIndexer}
@@ -321,13 +322,13 @@ class IndexerIndex extends Component {
{
isMovieEditorActive ?
<PageToolbarButton
label={'Indexers'}
label={translate('Indexers')}
iconName={icons.MOVIE_CONTINUING}
isDisabled={hasNoIndexer}
onPress={this.onMovieEditorTogglePress}
/> :
<PageToolbarButton
label={'Mass Editor'}
label={translate('MassEditor')}
iconName={icons.EDIT}
isDisabled={hasNoIndexer}
onPress={this.onMovieEditorTogglePress}
@@ -463,6 +464,7 @@ class IndexerIndex extends Component {
<AddIndexerModal
isOpen={isAddIndexerModalOpen}
onModalClose={this.onAddIndexerModalClose}
onSelectIndexer={this.onAddIndexerSelectIndexer}
/>
<EditIndexerModalConnector

View File

@@ -240,16 +240,19 @@ class IndexerIndexRow extends Component {
>
<IconButton
name={icons.INFO}
title={'Indexer info'}
title={translate('IndexerInfo')}
onPress={this.onIndexerInfoPress}
/>
<IconButton
className={styles.externalLink}
name={icons.EXTERNAL_LINK}
title={'Website'}
to={indexerUrls[0].replace('api.', '')}
/>
{
indexerUrls ?
<IconButton
className={styles.externalLink}
name={icons.EXTERNAL_LINK}
title={translate('Website')}
to={indexerUrls[0].replace('api.', '')}
/> : null
}
<IconButton
name={icons.EDIT}
@@ -289,7 +292,7 @@ class IndexerIndexRow extends Component {
IndexerIndexRow.propTypes = {
id: PropTypes.number.isRequired,
indexerUrls: PropTypes.arrayOf(PropTypes.string).isRequired,
indexerUrls: PropTypes.arrayOf(PropTypes.string),
protocol: PropTypes.string.isRequired,
privacy: PropTypes.string.isRequired,
priority: PropTypes.number.isRequired,
@@ -298,7 +301,7 @@ IndexerIndexRow.propTypes = {
redirect: PropTypes.bool.isRequired,
appProfile: PropTypes.object.isRequired,
status: PropTypes.object,
capabilities: PropTypes.object.isRequired,
capabilities: PropTypes.object,
added: PropTypes.string.isRequired,
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@@ -4,6 +4,7 @@ import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
import FieldSet from 'Components/FieldSet';
import Link from 'Components/Link/Link';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
@@ -20,6 +21,7 @@ function IndexerInfoModalContent(props) {
language,
indexerUrls,
protocol,
capabilities,
onModalClose
} = props;
@@ -30,41 +32,78 @@ function IndexerInfoModalContent(props) {
</ModalHeader>
<ModalBody>
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Id')}
data={id}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Description')}
data={description ? description : '-'}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Encoding')}
data={encoding ? encoding : '-'}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Language')}
data={language ?? '-'}
/>
<DescriptionListItemTitle>Indexer Site</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to={indexerUrls[0]}>{indexerUrls[0]}</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>{`${protocol === 'usenet' ? 'Newznab' : 'Torznab'} Url`}</DescriptionListItemTitle>
<DescriptionListItemDescription>
{`${window.location.origin}${window.Prowlarr.urlBase}/${id}/api`}
</DescriptionListItemDescription>
</DescriptionList>
<FieldSet legend={translate('IndexerDetails')}>
<div className={styles.groups}>
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Id')}
data={id}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Description')}
data={description ? description : '-'}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Encoding')}
data={encoding ? encoding : '-'}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Language')}
data={language ?? '-'}
/>
<DescriptionListItemTitle>{translate('IndexerSite')}</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to={indexerUrls[0]}>{indexerUrls[0]}</Link>
</DescriptionListItemDescription>
<DescriptionListItemTitle>{`${protocol === 'usenet' ? 'Newznab' : 'Torznab'} Url`}</DescriptionListItemTitle>
<DescriptionListItemDescription>
{`${window.location.origin}${window.Prowlarr.urlBase}/${id}/api`}
</DescriptionListItemDescription>
</DescriptionList>
</div>
</FieldSet>
<FieldSet legend={translate('SearchCapabilities')}>
<div className={styles.groups}>
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('RawSearchSupported')}
data={capabilities.supportsRawSearch ? translate('Yes') : translate('No')}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('SearchTypes')}
data={capabilities.searchParams.length === 0 ? translate('NotSupported') : capabilities.searchParams[0]}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('TVSearchTypes')}
data={capabilities.tvSearchParams.length === 0 ? translate('NotSupported') : capabilities.tvSearchParams.join(', ')}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('MovieSearchTypes')}
data={capabilities.movieSearchParams.length === 0 ? translate('NotSupported') : capabilities.movieSearchParams.join(', ')}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('BookSearchTypes')}
data={capabilities.bookSearchParams.length === 0 ? translate('NotSupported') : capabilities.bookSearchParams.join(', ')}
/>
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('MusicSearchTypes')}
data={capabilities.musicSearchParams.length === 0 ? translate('NotSupported') : capabilities.musicSearchParams.join(', ')}
/>
</DescriptionList>
</div>
</FieldSet>
</ModalBody>
</ModalContent>
</ModalContent >
);
}
@@ -76,6 +115,7 @@ IndexerInfoModalContent.propTypes = {
language: PropTypes.string.isRequired,
indexerUrls: PropTypes.arrayOf(PropTypes.string).isRequired,
protocol: PropTypes.string.isRequired,
capabilities: PropTypes.object.isRequired,
onModalClose: PropTypes.func.isRequired
};

View File

@@ -248,7 +248,7 @@ class QueryParameterModal extends Component {
onSelectionChange={this.onInputSelectionChange}
/>
<Button onPress={onModalClose}>
Close
{translate('Close')}
</Button>
</ModalFooter>
</ModalContent>

View File

@@ -37,7 +37,10 @@ class SearchFooter extends Component {
searchingReleases: false,
searchQuery: defaultSearchQuery || '',
searchIndexerIds: defaultIndexerIds,
searchCategories: defaultCategories
searchCategories: defaultCategories,
searchLimit: 100,
searchOffset: 0,
newSearch: true
};
}
@@ -115,11 +118,28 @@ class SearchFooter extends Component {
};
onSearchPress = () => {
this.props.onSearchPress(this.state.searchQuery, this.state.searchIndexerIds, this.state.searchCategories, this.state.searchType);
const {
searchLimit,
searchOffset,
searchQuery,
searchIndexerIds,
searchCategories,
searchType
} = this.state;
this.props.onSearchPress(searchQuery, searchIndexerIds, searchCategories, searchType, searchLimit, searchOffset);
this.setState({ searchOffset: searchOffset + 100, newSearch: false });
};
onSearchInputChange = ({ value }) => {
this.setState({ searchQuery: value });
this.setState({ searchQuery: value, newSearch: true, searchOffset: 0 });
};
onInputChange = ({ name, value }) => {
this.props.onInputChange({ name, value });
this.setState({ newSearch: true, searchOffset: 0 });
};
//
@@ -141,6 +161,7 @@ class SearchFooter extends Component {
searchQuery,
searchIndexerIds,
searchCategories,
newSearch,
isQueryParameterModalOpen,
queryModalOptions,
searchType
@@ -206,7 +227,7 @@ class SearchFooter extends Component {
name='searchIndexerIds'
value={searchIndexerIds}
isDisabled={isFetching}
onChange={onInputChange}
onChange={this.onInputChange}
/>
</div>
@@ -220,7 +241,7 @@ class SearchFooter extends Component {
name='searchCategories'
value={searchCategories}
isDisabled={isFetching}
onChange={onInputChange}
onChange={this.onInputChange}
/>
</div>
@@ -243,7 +264,7 @@ class SearchFooter extends Component {
isDisabled={isFetching || !hasIndexers || selectedCount === 0}
onPress={onBulkGrabPress}
>
{translate('Grab Releases')}
{translate('GrabReleases')}
</SpinnerButton>
}
@@ -253,7 +274,7 @@ class SearchFooter extends Component {
isDisabled={isFetching || !hasIndexers}
onPress={this.onSearchPress}
>
{translate('Search')}
{newSearch ? translate('Search') : translate('More')}
</SpinnerButton>
</div>
</div>

View File

@@ -196,8 +196,8 @@ class SearchIndex extends Component {
this.setState({ jumpToCharacter });
};
onSearchPress = (query, indexerIds, categories, type) => {
this.props.onSearchPress({ query, indexerIds, categories, type });
onSearchPress = (query, indexerIds, categories, type, limit, offset) => {
this.props.onSearchPress({ query, indexerIds, categories, type, limit, offset });
};
onBulkGrabPress = () => {

View File

@@ -299,7 +299,7 @@ class SearchIndexRow extends Component {
<IconButton
className={styles.downloadLink}
name={icons.SAVE}
title={'Save'}
title={translate('Save')}
to={downloadUrl}
/>
</VirtualTableRowCell>

View File

@@ -61,7 +61,7 @@ class Applications extends Component {
return (
<FieldSet legend={translate('Applications')}>
<PageSectionContent
errorMessage="Unable to load application list"
errorMessage={translate('UnableToLoadApplicationList')}
{...otherProps}
>
<div className={styles.applications}>

View File

@@ -61,7 +61,7 @@ class IndexerProxies extends Component {
} = this.state;
return (
<FieldSet legend={translate('Indexer Proxies')}>
<FieldSet legend={translate('IndexerProxies')}>
<PageSectionContent
errorMessage={translate('UnableToLoadIndexerProxies')}
{...otherProps}

View File

@@ -40,7 +40,7 @@ class Notification extends Component {
});
};
onDeleteNotificationModalClose= () => {
onDeleteNotificationModalClose = () => {
this.setState({ isDeleteNotificationModalOpen: false });
};
@@ -61,12 +61,14 @@ class Notification extends Component {
onRename,
onDelete,
onHealthIssue,
onApplicationUpdate,
supportsOnGrab,
supportsOnDownload,
supportsOnUpgrade,
supportsOnRename,
supportsOnDelete,
supportsOnHealthIssue
supportsOnHealthIssue,
supportsOnApplicationUpdate
} = this.props;
return (
@@ -82,53 +84,62 @@ class Notification extends Component {
{
supportsOnGrab && onGrab &&
<Label kind={kinds.SUCCESS}>
On Grab
{translate('OnGrab')}
</Label>
}
{
supportsOnDelete && onDelete &&
<Label kind={kinds.SUCCESS}>
On Delete
{translate('OnDelete')}
</Label>
}
{
supportsOnDownload && onDownload &&
<Label kind={kinds.SUCCESS}>
On Import
{translate('OnImport')}
</Label>
}
{
supportsOnUpgrade && onDownload && onUpgrade &&
<Label kind={kinds.SUCCESS}>
On Upgrade
{translate('OnUpgrade')}
</Label>
}
{
supportsOnRename && onRename &&
<Label kind={kinds.SUCCESS}>
On Rename
{translate('OnRename')}
</Label>
}
{
supportsOnHealthIssue && onHealthIssue &&
<Label kind={kinds.SUCCESS}>
On Health Issue
{translate('OnHealthIssue')}
</Label>
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete &&
supportsOnApplicationUpdate && onApplicationUpdate ?
<Label kind={kinds.SUCCESS}>
{translate('OnApplicationUpdate')}
</Label> :
null
}
{
!onGrab && !onDownload && !onRename && !onHealthIssue && !onDelete && !onApplicationUpdate ?
<Label
kind={kinds.DISABLED}
outline={true}
>
Disabled
</Label>
{translate('Disabled')}
</Label> :
null
}
<EditNotificationModalConnector
@@ -161,12 +172,14 @@ Notification.propTypes = {
onRename: PropTypes.bool.isRequired,
onDelete: PropTypes.bool.isRequired,
onHealthIssue: PropTypes.bool.isRequired,
onApplicationUpdate: PropTypes.bool.isRequired,
supportsOnGrab: PropTypes.bool.isRequired,
supportsOnDownload: PropTypes.bool.isRequired,
supportsOnDelete: PropTypes.bool.isRequired,
supportsOnUpgrade: PropTypes.bool.isRequired,
supportsOnRename: PropTypes.bool.isRequired,
supportsOnHealthIssue: PropTypes.bool.isRequired,
supportsOnApplicationUpdate: PropTypes.bool.isRequired,
onConfirmDeleteNotification: PropTypes.func.isRequired
};

View File

@@ -16,8 +16,10 @@ function NotificationEventItems(props) {
const {
onHealthIssue,
onApplicationUpdate,
supportsOnHealthIssue,
includeHealthWarnings
includeHealthWarnings,
supportsOnApplicationUpdate
} = item;
return (
@@ -53,6 +55,17 @@ function NotificationEventItems(props) {
/>
</div>
}
<div>
<FormInputGroup
type={inputTypes.CHECK}
name="onApplicationUpdate"
helpText={translate('OnApplicationUpdateHelpText')}
isDisabled={!supportsOnApplicationUpdate.value}
{...onApplicationUpdate}
onChange={onInputChange}
/>
</div>
</div>
</div>
</FormGroup>

View File

@@ -67,7 +67,7 @@ function TagDetailsModalContent(props) {
{
!!indexerProxies.length &&
<FieldSet legend={translate('Indexer Proxies')}>
<FieldSet legend={translate('IndexerProxies')}>
{
indexerProxies.map((item) => {
return (

View File

@@ -106,6 +106,7 @@ export default {
selectedSchema.onDownload = selectedSchema.supportsOnDownload;
selectedSchema.onUpgrade = selectedSchema.supportsOnUpgrade;
selectedSchema.onRename = selectedSchema.supportsOnRename;
selectedSchema.onApplicationUpdate = selectedSchema.supportsOnApplicationUpdate;
return selectedSchema;
});

View File

@@ -118,12 +118,12 @@ export const defaultState = {
filterBuilderProps: [
{
name: 'name',
label: 'Indexer Name',
label: translate('IndexerName'),
type: filterBuilderTypes.STRING
},
{
name: 'enable',
label: 'Enabled',
label: translate('Enabled'),
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.BOOL
},
@@ -135,15 +135,21 @@ export const defaultState = {
},
{
name: 'priority',
label: 'Priority',
label: translate('Priority'),
type: filterBuilderTypes.NUMBER
},
{
name: 'protocol',
label: 'Protocol',
label: translate('Protocol'),
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.PROTOCOL
},
{
name: 'privacy',
label: translate('Privacy'),
type: filterBuilderTypes.EXACT,
valueType: filterBuilderValueTypes.PRIVACY
},
{
name: 'appProfileId',
label: translate('AppProfile'),

View File

@@ -80,8 +80,8 @@ export default function createSentryMiddleware() {
return;
}
const dsn = isProduction ? 'https://b0fb75c38ef4487dbf742f79c4ba62d2@sentry.servarr.com/12' :
'https://da610619280249f891ec3ee306906793@sentry.servarr.com/13';
const dsn = isProduction ? 'https://b233094711fe4430a0b0c5da2e01df93@sentry.servarr.com/28' :
'https://116efebd253a4dff9df9475a31510001@sentry.servarr.com/37';
sentry.init({
dsn,

View File

@@ -1,14 +0,0 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
function createExclusionMovieSelector() {
return createSelector(
(state, { tmdbId }) => tmdbId,
(state) => state.settings.importExclusions,
(tmdbId, importExclusions) => {
return _.some(importExclusions.items, { tmdbId });
}
);
}
export default createExclusionMovieSelector;

View File

@@ -0,0 +1,15 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
import createAllIndexersSelector from './createAllIndexersSelector';
function createExistingIndexerSelector() {
return createSelector(
(state, { definitionName }) => definitionName,
createAllIndexersSelector(),
(definitionName, indexers) => {
return _.some(indexers, { definitionName });
}
);
}
export default createExistingIndexerSelector;

View File

@@ -5,13 +5,14 @@ import createAllIndexersSelector from './createAllIndexersSelector';
function createProfileInUseSelector(profileProp) {
return createSelector(
(state, { id }) => id,
(state) => state.settings.appProfiles.items,
createAllIndexersSelector(),
(id, indexers) => {
(id, profiles, indexers) => {
if (!id) {
return false;
}
if (_.some(indexers, { [profileProp]: id })) {
if (_.some(indexers, { [profileProp]: id }) || profiles.length <= 1) {
return true;
}

View File

@@ -36,6 +36,12 @@ function selectSettings(item, pendingChanges, saveError) {
return result;
}
if (key === 'definitionName') {
result.definitionName = item[key];
return result;
}
const setting = {
value: item[key],
errors: _.map(_.remove(validationFailures, (failure) => {

View File

@@ -199,7 +199,7 @@ class QueuedTaskRow extends Component {
</span>
{
clientUserAgent ?
<span className={styles.userAgent} title="User-Agent provided by the app that called the API">
<span className={styles.userAgent} title={translate('UserAgentProvidedByTheAppThatCalledTheAPI')}>
from: {clientUserAgent}
</span> :
null

View File

@@ -6,8 +6,8 @@
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Chrome, Opera, and Firefox OS -->
<meta name="theme-color" content="#3a3f51" />
<!-- Chrome, Safari, Opera, and Firefox OS -->
<meta name="theme-color" content="#e66001" />
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#3a3f51" />

View File

@@ -6,8 +6,8 @@
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- Chrome, Opera, and Firefox OS -->
<meta name="theme-color" content="#464b51" />
<!-- Chrome, Safari, Opera, and Firefox OS -->
<meta name="theme-color" content="#e66001" />
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#464b51" />

View File

@@ -25,17 +25,17 @@
"not chrome < 60"
],
"dependencies": {
"@fortawesome/fontawesome-free": "5.15.3",
"@fortawesome/fontawesome-svg-core": "1.2.35",
"@fortawesome/free-regular-svg-icons": "5.15.3",
"@fortawesome/free-solid-svg-icons": "5.15.3",
"@fortawesome/react-fontawesome": "0.1.14",
"@microsoft/signalr": "6.0.0",
"@sentry/browser": "6.15.0",
"@sentry/integrations": "6.15.0",
"chart.js": "3.2.0",
"@fortawesome/fontawesome-free": "6.1.1",
"@fortawesome/fontawesome-svg-core": "6.1.1",
"@fortawesome/free-regular-svg-icons": "6.1.1",
"@fortawesome/free-solid-svg-icons": "6.1.1",
"@fortawesome/react-fontawesome": "0.1.18",
"@microsoft/signalr": "6.0.3",
"@sentry/browser": "6.19.2",
"@sentry/integrations": "6.19.2",
"chart.js": "3.7.1",
"classnames": "2.3.1",
"clipboard": "2.0.8",
"clipboard": "2.0.10",
"connected-react-router": "6.9.1",
"element-class": "0.2.2",
"filesize": "6.3.0",
@@ -48,13 +48,13 @@
"moment": "2.29.1",
"mousetrap": "1.6.5",
"normalize.css": "8.0.1",
"prop-types": "15.7.2",
"qs": "6.10.1",
"prop-types": "15.8.1",
"qs": "6.10.3",
"react": "17.0.2",
"react-addons-shallow-compare": "15.6.3",
"react-async-script": "1.2.0",
"react-autosuggest": "10.1.0",
"react-custom-scrollbars": "4.2.1",
"react-custom-scrollbars-2": "4.4.0",
"react-dnd": "14.0.4",
"react-dnd-html5-backend": "14.0.2",
"react-dnd-multi-backend": "6.0.2",
@@ -78,41 +78,41 @@
"reselect": "4.0.0"
},
"devDependencies": {
"@babel/core": "7.16.0",
"@babel/eslint-parser": "7.16.3",
"@babel/plugin-proposal-class-properties": "7.16.0",
"@babel/plugin-proposal-decorators": "7.16.4",
"@babel/plugin-proposal-export-default-from": "7.16.0",
"@babel/plugin-proposal-export-namespace-from": "7.16.0",
"@babel/plugin-proposal-function-sent": "7.16.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.0",
"@babel/plugin-proposal-numeric-separator": "7.16.0",
"@babel/plugin-proposal-optional-chaining": "7.16.0",
"@babel/plugin-proposal-throw-expressions": "7.16.0",
"@babel/core": "7.17.8",
"@babel/eslint-parser": "7.17.0",
"@babel/plugin-proposal-class-properties": "7.16.7",
"@babel/plugin-proposal-decorators": "7.17.8",
"@babel/plugin-proposal-export-default-from": "7.16.7",
"@babel/plugin-proposal-export-namespace-from": "7.16.7",
"@babel/plugin-proposal-function-sent": "7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.16.7",
"@babel/plugin-proposal-numeric-separator": "7.16.7",
"@babel/plugin-proposal-optional-chaining": "7.16.7",
"@babel/plugin-proposal-throw-expressions": "7.16.7",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/preset-env": "7.16.4",
"@babel/preset-react": "7.16.0",
"autoprefixer": "10.2.5",
"babel-loader": "8.2.3",
"@babel/preset-env": "7.16.11",
"@babel/preset-react": "7.16.7",
"autoprefixer": "10.4.4",
"babel-loader": "8.2.4",
"babel-plugin-inline-classnames": "2.0.1",
"babel-plugin-transform-react-remove-prop-types": "0.4.24",
"core-js": "3.11.0",
"css-loader": "6.5.1",
"eslint": "8.3.0",
"core-js": "3.21.1",
"css-loader": "6.7.1",
"eslint": "8.11.0",
"eslint-plugin-filenames": "1.3.2",
"eslint-plugin-import": "2.25.3",
"eslint-plugin-react": "7.27.1",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-react": "7.29.4",
"eslint-plugin-simple-import-sort": "7.0.0",
"esprint": "3.1.0",
"esprint": "3.3.0",
"file-loader": "6.2.0",
"filemanager-webpack-plugin": "6.1.7",
"html-webpack-plugin": "5.5.0",
"loader-utils": "^3.0.0",
"mini-css-extract-plugin": "2.4.5",
"postcss": "8.3.11",
"mini-css-extract-plugin": "2.6.0",
"postcss": "8.4.12",
"postcss-color-function": "4.1.0",
"postcss-loader": "6.2.0",
"postcss-mixins": "8.1.0",
"postcss-loader": "6.2.1",
"postcss-mixins": "9.0.2",
"postcss-nested": "5.0.6",
"postcss-simple-vars": "6.0.3",
"postcss-url": "10.1.3",
@@ -121,11 +121,11 @@
"run-sequence": "2.2.1",
"streamqueue": "1.1.2",
"style-loader": "3.3.1",
"stylelint": "14.1.0",
"stylelint": "14.6.0",
"stylelint-order": "5.0.0",
"url-loader": "4.1.1",
"webpack": "5.64.2",
"webpack-cli": "4.9.1",
"webpack": "5.70.0",
"webpack-cli": "4.9.2",
"webpack-livereload-plugin": "3.0.2"
}
}

View File

@@ -94,10 +94,10 @@
<!-- Standard testing packages -->
<ItemGroup Condition="'$(TestProject)'=='true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="NunitXml.TestLogger" Version="3.0.97" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
<PackageReference Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" />
</ItemGroup>

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Automation.Test
public abstract class AutomationTest
{
private NzbDroneRunner _runner;
protected RemoteWebDriver driver;
protected WebDriver driver;
public AutomationTest()
{

View File

@@ -1,16 +1,15 @@
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace NzbDrone.Automation.Test.PageModel
{
public class PageBase
{
private readonly RemoteWebDriver _driver;
private readonly WebDriver _driver;
public PageBase(RemoteWebDriver driver)
public PageBase(WebDriver driver)
{
_driver = driver;
driver.Manage().Window.Maximize();

View File

@@ -3,8 +3,8 @@
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Selenium.Support" Version="3.141.0" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="90.0.4430.2400" />
<PackageReference Include="Selenium.Support" Version="4.1.0" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="99.0.4844.5100" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Test.Common\Prowlarr.Test.Common.csproj" />

View File

@@ -229,11 +229,6 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_follow_redirects_to_https()
{
if (typeof(TDispatcher) == typeof(ManagedHttpDispatcher) && PlatformInfo.IsMono)
{
Assert.Ignore("Will fail on tls1.2 via managed dispatcher, ignore.");
}
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
.AddQueryParam("url", $"https://radarr.video/")
.Build();

View File

@@ -23,6 +23,7 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[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")]
[TestCase(@"https://torrentseeds.org/api/torrents/filter?api_token=2b51db35e1912ffc138825a12b9933d2&name=&sortField=created_at&sortDirection=desc&perPage=100&page=1")]
//Indexer Responses

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using NzbDrone.Common.EnvironmentInfo;
@@ -24,7 +24,8 @@ namespace NzbDrone.Common.Disk
"/boot",
"/lib",
"/sbin",
"/proc"
"/proc",
"/usr/bin"
};
}
}

View File

@@ -258,6 +258,11 @@ namespace NzbDrone.Common.Extensions
return appFolderInfo.AppDataFolder;
}
public static string GetDataProtectionPath(this IAppFolderInfo appFolderInfo)
{
return Path.Combine(GetAppDataPath(appFolderInfo), "asp");
}
public static string GetLogFolder(this IAppFolderInfo appFolderInfo)
{
return Path.Combine(GetAppDataPath(appFolderInfo), "logs");

View File

@@ -34,19 +34,10 @@ namespace NzbDrone.Common.Http.Dispatchers
{
var webRequest = (HttpWebRequest)WebRequest.Create((Uri)request.Url);
if (PlatformInfo.IsMono)
{
// On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case.
webRequest.AutomaticDecompression = DecompressionMethods.None;
webRequest.Headers.Add("Accept-Encoding", "gzip");
}
else
{
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
}
// Deflate is not a standard and could break depending on implementation.
// we should just stick with the more compatible Gzip
//http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net
webRequest.AutomaticDecompression = DecompressionMethods.GZip;
webRequest.Method = request.Method.ToString();
webRequest.UserAgent = _userAgentBuilder.GetUserAgent(request.UseSimplifiedUserAgent);
@@ -91,9 +82,6 @@ namespace NzbDrone.Common.Http.Dispatchers
if (httpWebResponse == null)
{
// Workaround for mono not closing connections properly in certain situations.
AbortWebRequest(webRequest);
// The default messages for WebException on mono are pretty horrible.
if (e.Status == WebExceptionStatus.NameResolutionFailure)
{
@@ -127,19 +115,6 @@ namespace NzbDrone.Common.Http.Dispatchers
try
{
data = await responseStream.ToBytes();
if (PlatformInfo.IsMono && httpWebResponse.ContentEncoding == "gzip")
{
using (var compressedStream = new MemoryStream(data))
using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var decompressedStream = new MemoryStream())
{
gzip.CopyTo(decompressedStream);
data = decompressedStream.ToArray();
}
httpWebResponse.Headers.Remove("Content-Encoding");
}
}
catch (Exception ex)
{
@@ -264,36 +239,5 @@ namespace NzbDrone.Common.Http.Dispatchers
}
}
}
// Workaround for mono not closing connections properly on timeouts
private void AbortWebRequest(HttpWebRequest webRequest)
{
// First affected version was mono 5.16
if (OsInfo.IsNotWindows && _platformInfo.Version >= new Version(5, 16))
{
try
{
var currentOperationInfo = webRequest.GetType().GetField("currentOperation", BindingFlags.NonPublic | BindingFlags.Instance);
var currentOperation = currentOperationInfo.GetValue(webRequest);
if (currentOperation != null)
{
var responseStreamInfo = currentOperation.GetType().GetField("responseStream", BindingFlags.NonPublic | BindingFlags.Instance);
var responseStream = responseStreamInfo.GetValue(currentOperation) as Stream;
// Note that responseStream will likely be null once mono fixes it.
responseStream?.Dispose();
}
}
catch (Exception ex)
{
// This can fail randomly on future mono versions that have been changed/fixed. Log to sentry and ignore.
_logger.Trace()
.Exception(ex)
.Message("Unable to dispose responseStream on mono {0}", _platformInfo.Version)
.Write();
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Cache;
@@ -74,7 +75,7 @@ namespace NzbDrone.Common.Http
do
{
request.Url += new HttpUri(response.Headers.GetSingleValue("Location"));
request.Url = new HttpUri(response.RedirectUrl);
autoRedirectChain.Add(request.Url.ToString());
_logger.Trace("Redirected to {0}", request.Url);
@@ -87,7 +88,7 @@ namespace NzbDrone.Common.Http
// 302 or 303 should default to GET on redirect even if POST on original
if (response.StatusCode == HttpStatusCode.Redirect || response.StatusCode == HttpStatusCode.RedirectMethod)
{
request.Method = HttpMethod.GET;
request.Method = HttpMethod.Get;
request.ContentData = null;
}
@@ -263,7 +264,7 @@ namespace NzbDrone.Common.Http
public Task<HttpResponse> GetAsync(HttpRequest request)
{
request.Method = HttpMethod.GET;
request.Method = HttpMethod.Get;
return ExecuteAsync(request);
}
@@ -288,7 +289,7 @@ namespace NzbDrone.Common.Http
public Task<HttpResponse> HeadAsync(HttpRequest request)
{
request.Method = HttpMethod.HEAD;
request.Method = HttpMethod.Head;
return ExecuteAsync(request);
}
@@ -299,7 +300,7 @@ namespace NzbDrone.Common.Http
public Task<HttpResponse> PostAsync(HttpRequest request)
{
request.Method = HttpMethod.POST;
request.Method = HttpMethod.Post;
return ExecuteAsync(request);
}

View File

@@ -1,14 +0,0 @@
namespace NzbDrone.Common.Http
{
public enum HttpMethod
{
GET,
POST,
PUT,
DELETE,
HEAD,
OPTIONS,
PATCH,
MERGE
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
@@ -13,6 +14,7 @@ namespace NzbDrone.Common.Http
{
Url = new HttpUri(url);
Headers = new HttpHeader();
Method = HttpMethod.Get;
ConnectionKeepAlive = true;
AllowAutoRedirect = true;
Cookies = new Dictionary<string, string>();

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using NzbDrone.Common.Extensions;
@@ -37,7 +38,7 @@ namespace NzbDrone.Common.Http
{
BaseUrl = new HttpUri(baseUrl);
ResourceUrl = string.Empty;
Method = HttpMethod.GET;
Method = HttpMethod.Get;
Encoding = Encoding.UTF8;
QueryParams = new List<KeyValuePair<string, string>>();
SuffixQueryParams = new List<KeyValuePair<string, string>>();
@@ -275,7 +276,7 @@ namespace NzbDrone.Common.Http
public virtual HttpRequestBuilder Post()
{
Method = HttpMethod.POST;
Method = HttpMethod.Post;
return this;
}
@@ -397,7 +398,7 @@ namespace NzbDrone.Common.Http
public virtual HttpRequestBuilder AddFormParameter(string key, object value)
{
if (Method != HttpMethod.POST)
if (Method != HttpMethod.Post)
{
throw new NotSupportedException("HttpRequest Method must be POST to add FormParameter.");
}
@@ -413,7 +414,7 @@ namespace NzbDrone.Common.Http
public virtual HttpRequestBuilder AddFormUpload(string name, string fileName, byte[] data, string contentType = "application/octet-stream")
{
if (Method != HttpMethod.POST)
if (Method != HttpMethod.Post)
{
throw new NotSupportedException("HttpRequest Method must be POST to add FormUpload.");
}

View File

@@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http
{
public class HttpResponse
{
private static readonly Regex RegexSetCookie = new Regex("^(.*?)=(.*?)(?:;|$)", RegexOptions.Compiled);
private static readonly Regex RegexRefresh = new Regex("^(.*?url)=(.*?)(?:;|$)", RegexOptions.Compiled);
public HttpResponse(HttpRequest request, HttpHeader headers, CookieCollection cookies, byte[] binaryData, long elapsedTime = 0, HttpStatusCode statusCode = HttpStatusCode.OK)
{
@@ -67,7 +67,8 @@ namespace NzbDrone.Common.Http
StatusCode == HttpStatusCode.MovedPermanently ||
StatusCode == HttpStatusCode.RedirectMethod ||
StatusCode == HttpStatusCode.TemporaryRedirect ||
StatusCode == HttpStatusCode.Found;
StatusCode == HttpStatusCode.Found ||
Headers.ContainsKey("Refresh");
public string RedirectUrl
{
@@ -76,6 +77,20 @@ namespace NzbDrone.Common.Http
var newUrl = Headers["Location"];
if (newUrl == null)
{
newUrl = Headers["Refresh"];
if (newUrl == null)
{
return string.Empty;
}
var match = RegexRefresh.Match(newUrl);
if (match.Success)
{
return (Request.Url += new HttpUri(match.Groups[2].Value)).FullUri;
}
return string.Empty;
}

View File

@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Newtonsoft.Json;
using NzbDrone.Common.Serializer;
@@ -17,14 +18,14 @@ namespace NzbDrone.Common.Http
public JsonRpcRequestBuilder(string baseUrl)
: base(baseUrl)
{
Method = HttpMethod.POST;
Method = HttpMethod.Post;
JsonParameters = new List<object>();
}
public JsonRpcRequestBuilder(string baseUrl, string method, IEnumerable<object> parameters)
: base(baseUrl)
{
Method = HttpMethod.POST;
Method = HttpMethod.Post;
JsonMethod = method;
JsonParameters = parameters.ToList();
}

View File

@@ -11,7 +11,7 @@ namespace NzbDrone.Common.Instrumentation
private static readonly Regex[] CleansingRules = new[]
{
// Url
new Regex(@"(?<=[?&: ;])(apikey|(?:access[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=]+?)(?= |&|$|<)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?&: ;])(apikey|(?:(?:access|api)[-_]?)?token|pass(?:key|wd)?|auth|authkey|user|u?id|api|[a-z_]*apikey|account|pwd)=(?<secret>[^&=]+?)(?= |&|$|<)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)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),

View File

@@ -38,16 +38,6 @@ namespace NzbDrone.Common.Instrumentation
return;
}
if (PlatformInfo.IsMono)
{
if ((exception is TypeInitializationException && exception.InnerException is DllNotFoundException) ||
exception is DllNotFoundException)
{
Logger.Debug(exception, "Minor Fail: " + exception.Message);
return;
}
}
Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.Fatal(exception, "EPIC FAIL.");
}

View File

@@ -106,13 +106,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.Debug = false;
o.DiagnosticLevel = SentryLevel.Debug;
o.Release = BuildInfo.Release;
if (PlatformInfo.IsMono)
{
// Mono 6.0 broke GzipStream.WriteAsync
// TODO: Check specific version
o.RequestBodyCompressionLevel = System.IO.Compression.CompressionLevel.NoCompression;
}
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
o.Environment = BuildInfo.Branch;
@@ -155,7 +148,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
{
scope.SetTag("is_docker", $"{osInfo.IsDocker}");
if (osInfo.Name != null && PlatformInfo.IsMono)
if (osInfo.Name != null && !OsInfo.IsWindows)
{
// Sentry auto-detection of non-Windows platforms isn't that accurate on certain devices.
scope.Contexts.OperatingSystem.Name = osInfo.Name.FirstCharToUpper();

View File

@@ -366,11 +366,6 @@ namespace NzbDrone.Common.Processes
private (string Path, string Args) GetPathAndArgs(string path, string args)
{
if (PlatformInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
{
return ("mono", $"--debug {path} {args}");
}
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
{
return ("cmd.exe", $"/c {path} {args}");

View File

@@ -4,14 +4,14 @@
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DryIoc.dll" Version="4.8.1" />
<PackageReference Include="DryIoc.dll" Version="4.8.8" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.2" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.7.9" />
<PackageReference Include="Sentry" Version="3.8.3" />
<PackageReference Include="SharpZipLib" Version="1.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="4.7.14" />
<PackageReference Include="Sentry" Version="3.15.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />

View File

@@ -1,4 +1,4 @@
using System.Data;
using System.Data;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
@@ -38,6 +38,7 @@ namespace NzbDrone.Core.Test.Datastore.SqliteSchemaDumperTests
result.Name.Should().Be(tableName);
result.Columns.Count.Should().Be(1);
result.Columns.First().Name.Should().Be(columnName);
result.Columns.First().IsIdentity.Should().BeTrue();
}
[TestCase(@"CREATE INDEX TestIndex ON TestTable (MyId)", "TestIndex", "TestTable", "MyId")]

View File

@@ -0,0 +1,54 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Applications;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupOrphanedApplicationFixture : DbTest<CleanupOrphanedApplicationStatus, ApplicationStatus>
{
private ApplicationDefinition _application;
[SetUp]
public void Setup()
{
_application = Builder<ApplicationDefinition>.CreateNew()
.BuildNew();
}
private void GivenApplication()
{
Db.Insert(_application);
}
[Test]
public void should_delete_orphaned_applicationstatus()
{
var status = Builder<ApplicationStatus>.CreateNew()
.With(h => h.ProviderId = _application.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_unorphaned_applicationstatus()
{
GivenApplication();
var status = Builder<ApplicationStatus>.CreateNew()
.With(h => h.ProviderId = _application.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
AllStoredModels.Should().Contain(h => h.ProviderId == _application.Id);
}
}
}

View File

@@ -0,0 +1,56 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Flood;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupOrphanedDownloadClientStatusFixture : DbTest<CleanupOrphanedDownloadClientStatus, DownloadClientStatus>
{
private DownloadClientDefinition _downloadClient;
[SetUp]
public void Setup()
{
_downloadClient = Builder<DownloadClientDefinition>.CreateNew()
.With(c => c.Settings = new FloodSettings())
.BuildNew();
}
private void GivenClient()
{
Db.Insert(_downloadClient);
}
[Test]
public void should_delete_orphaned_downloadclientstatus()
{
var status = Builder<DownloadClientStatus>.CreateNew()
.With(h => h.ProviderId = _downloadClient.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_unorphaned_downloadclientstatus()
{
GivenClient();
var status = Builder<DownloadClientStatus>.CreateNew()
.With(h => h.ProviderId = _downloadClient.Id)
.BuildNew();
Db.Insert(status);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
AllStoredModels.Should().Contain(h => h.ProviderId == _downloadClient.Id);
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Applications;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class FixFutureApplicationStatusTimesFixture : CoreTest<FixFutureApplicationStatusTimes>
{
[Test]
public void should_set_disabled_till_when_its_too_far_in_the_future()
{
var disabledTillTime = EscalationBackOff.Periods[1];
var applicationStatuses = Builder<ApplicationStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IApplicationStatusRepository>()
.Setup(s => s.All())
.Returns(applicationStatuses);
Subject.Clean();
Mocker.GetMock<IApplicationStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<ApplicationStatus>>(i => i.All(
s => s.DisabledTill.Value <= DateTime.UtcNow.AddMinutes(disabledTillTime)))));
}
[Test]
public void should_set_initial_failure_when_its_in_the_future()
{
var applicationStatuses = Builder<ApplicationStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IApplicationStatusRepository>()
.Setup(s => s.All())
.Returns(applicationStatuses);
Subject.Clean();
Mocker.GetMock<IApplicationStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<ApplicationStatus>>(i => i.All(
s => s.InitialFailure.Value <= DateTime.UtcNow))));
}
[Test]
public void should_set_most_recent_failure_when_its_in_the_future()
{
var applicationStatuses = Builder<ApplicationStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IApplicationStatusRepository>()
.Setup(s => s.All())
.Returns(applicationStatuses);
Subject.Clean();
Mocker.GetMock<IApplicationStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<ApplicationStatus>>(i => i.All(
s => s.MostRecentFailure.Value <= DateTime.UtcNow))));
}
[Test]
public void should_not_change_statuses_when_times_are_in_the_past()
{
var indexerStatuses = Builder<ApplicationStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 0)
.BuildListOfNew();
Mocker.GetMock<IApplicationStatusRepository>()
.Setup(s => s.All())
.Returns(indexerStatuses);
Subject.Clean();
Mocker.GetMock<IApplicationStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<ApplicationStatus>>(i => i.Count == 0)));
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class FixFutureDownloadClientStatusTimesFixture : CoreTest<FixFutureDownloadClientStatusTimes>
{
[Test]
public void should_set_disabled_till_when_its_too_far_in_the_future()
{
var disabledTillTime = EscalationBackOff.Periods[1];
var clientStatuses = Builder<DownloadClientStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(s => s.All())
.Returns(clientStatuses);
Subject.Clean();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<DownloadClientStatus>>(i => i.All(
s => s.DisabledTill.Value <= DateTime.UtcNow.AddMinutes(disabledTillTime)))));
}
[Test]
public void should_set_initial_failure_when_its_in_the_future()
{
var clientStatuses = Builder<DownloadClientStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(s => s.All())
.Returns(clientStatuses);
Subject.Clean();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<DownloadClientStatus>>(i => i.All(
s => s.InitialFailure.Value <= DateTime.UtcNow))));
}
[Test]
public void should_set_most_recent_failure_when_its_in_the_future()
{
var clientStatuses = Builder<DownloadClientStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(5))
.With(t => t.EscalationLevel = 1)
.BuildListOfNew();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(s => s.All())
.Returns(clientStatuses);
Subject.Clean();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<DownloadClientStatus>>(i => i.All(
s => s.MostRecentFailure.Value <= DateTime.UtcNow))));
}
[Test]
public void should_not_change_statuses_when_times_are_in_the_past()
{
var clientStatuses = Builder<DownloadClientStatus>.CreateListOfSize(5)
.All()
.With(t => t.DisabledTill = DateTime.UtcNow.AddDays(-5))
.With(t => t.InitialFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.MostRecentFailure = DateTime.UtcNow.AddDays(-5))
.With(t => t.EscalationLevel = 0)
.BuildListOfNew();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(s => s.All())
.Returns(clientStatuses);
Subject.Clean();
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.UpdateMany(
It.Is<List<DownloadClientStatus>>(i => i.Count == 0)));
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -34,7 +35,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
var recentFeed = ReadAllText(@"Files/Indexers/Avistaz/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -34,7 +35,7 @@ namespace NzbDrone.Core.Test.IndexerTests.AvistazTests
var recentFeed = ReadAllText(@"Files/Indexers/PrivateHD/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -33,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
var recentFeed = ReadAllText(@"Files/Indexers/FileList/recentfeed.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
@@ -45,7 +46,7 @@ namespace NzbDrone.Core.Test.IndexerTests.HDBitsTests
var responseJson = ReadAllText(fileName);
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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;

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -43,7 +44,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Newznab/newznab_nzb_su.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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

@@ -1,5 +1,6 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -37,11 +38,11 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
var responseJson = ReadAllText(fileName);
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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.ExecuteProxiedAsync(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

@@ -1,10 +1,12 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Rarbg;
@@ -38,7 +40,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
var recentFeed = ReadAllText(@"Files/Indexers/Rarbg/RecentFeed_v2.json");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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;
@@ -51,7 +53,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
torrentInfo.Title.Should().Be("Sense8.S01E01.WEBRip.x264-FGT");
torrentInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
torrentInfo.DownloadUrl.Should().Be("magnet:?xt=urn:btih:d8bde635f573acb390c7d7e7efc1556965fdc802&dn=Sense8.S01E01.WEBRip.x264-FGT&tr=http%3A%2F%2Ftracker.trackerfix.com%3A80%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710&tr=udp%3A%2F%2F9.rarbg.to%3A2710&tr=udp%3A%2F%2Fopen.demonii.com%3A1337%2Fannounce");
torrentInfo.InfoUrl.Should().Be("https://torrentapi.org/redirect_to_info.php?token=i5cx7b9agd&p=8_6_4_4_5_6__d8bde635f5&app_id=Prowlarr");
torrentInfo.InfoUrl.Should().Be($"https://torrentapi.org/redirect_to_info.php?token=i5cx7b9agd&p=8_6_4_4_5_6__d8bde635f5&app_id={BuildInfo.AppName}");
torrentInfo.Indexer.Should().Be(Subject.Definition.Name);
torrentInfo.PublishDate.Should().Be(DateTime.Parse("2015-06-05 16:58:11 +0000").ToUniversalTime());
torrentInfo.Size.Should().Be(564198371);
@@ -65,7 +67,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
public async Task should_parse_error_20_as_empty_results()
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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 +79,7 @@ namespace NzbDrone.Core.Test.IndexerTests.RarbgTests
public async Task should_warn_on_unknown_error()
{
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Moq;
@@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_hdaccess_net.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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 +74,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_tpb.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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 +104,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IIndexerHttpClient>()
.Setup(o => o.ExecuteProxiedAsync(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,90 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using FluentValidation.Results;
using NUnit.Framework;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.NotificationTests
{
[TestFixture]
public class NotificationBaseFixture : TestBase
{
private class TestSetting : IProviderConfig
{
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult();
}
}
private class TestNotificationWithApplicationUpdate : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
TestLogger.Info("OnApplicationUpdate was called");
}
}
private class TestNotificationWithAllEvents : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
public override void OnHealthIssue(NzbDrone.Core.HealthCheck.HealthCheck artist)
{
TestLogger.Info("OnHealthIssue was called");
}
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
TestLogger.Info("OnApplicationUpdate was called");
}
}
private class TestNotificationWithNoEvents : NotificationBase<TestSetting>
{
public override string Name => "TestNotification";
public override string Link => "";
public override ValidationResult Test()
{
throw new NotImplementedException();
}
}
[Test]
public void should_support_all_if_implemented()
{
var notification = new TestNotificationWithAllEvents();
notification.SupportsOnHealthIssue.Should().BeTrue();
notification.SupportsOnApplicationUpdate.Should().BeTrue();
}
[Test]
public void should_support_none_if_none_are_implemented()
{
var notification = new TestNotificationWithNoEvents();
notification.SupportsOnHealthIssue.Should().BeFalse();
notification.SupportsOnApplicationUpdate.Should().BeFalse();
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class DateTimeRoutinesFixture : CoreTest
{
public static IEnumerable DateTimeTestCases
{
get
{
yield return new TestCaseData(@"Member since: 10-Feb-2008").Returns(new DateTime(2008, 2, 10, 0, 0, 0));
yield return new TestCaseData(@"Last Update: 18:16 11 Feb '08 ").Returns(new DateTime(2008, 2, 11, 18, 16, 0));
yield return new TestCaseData(@"date Tue, Feb 10, 2008 at 11:06 AM").Returns(new DateTime(2008, 2, 10, 11, 06, 0));
yield return new TestCaseData(@"see at 12/31/2007 14:16:32").Returns(new DateTime(2007, 12, 31, 14, 16, 32));
yield return new TestCaseData(@"sack finish 14:16:32 November 15 2008, 1-144 app").Returns(new DateTime(2008, 11, 15, 14, 16, 32));
yield return new TestCaseData(@"Genesis Message - Wed 04 Feb 08 - 19:40").Returns(new DateTime(2008, 2, 4, 19, 40, 0));
yield return new TestCaseData(@"The day 07/31/07 14:16:32 is ").Returns(new DateTime(2007, 7, 31, 14, 16, 32));
yield return new TestCaseData(@"Shipping is on us until December 24, 2008 within the U.S. ").Returns(new DateTime(2008, 12, 24, 0, 0, 0));
yield return new TestCaseData(@" 2008 within the U.S. at 14:16:32").Returns(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 14, 16, 32));
yield return new TestCaseData(@"5th November, 1994, 8:15:30 pm").Returns(new DateTime(1994, 11, 5, 20, 15, 30));
yield return new TestCaseData(@"7 boxes January 31 , 14:16:32.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
yield return new TestCaseData(@"the blue sky of Sept 30th 2008 14:16:32").Returns(new DateTime(2008, 9, 30, 14, 16, 32));
yield return new TestCaseData(@" e.g. 1997-07-16T19:20:30+01:00").Returns(new DateTime(1997, 7, 16, 19, 20, 30));
yield return new TestCaseData(@"Apr 1st, 2008 14:16:32 tufa 6767").Returns(new DateTime(2008, 4, 1, 14, 16, 32));
yield return new TestCaseData(@"wait for 07/31/07 14:16:32").Returns(new DateTime(2007, 7, 31, 14, 16, 32));
yield return new TestCaseData(@"later 12.31.08 and before 1.01.09").Returns(new DateTime(2008, 12, 31, 0, 0, 0));
yield return new TestCaseData(@"Expires: Sept 30th 2008 14:16:32").Returns(new DateTime(2008, 9, 30, 14, 16, 32));
yield return new TestCaseData(@"Offer expires Apr 1st, 2007, 14:16:32").Returns(new DateTime(2007, 4, 1, 14, 16, 32));
yield return new TestCaseData(@"Expires 14:16:32 January 31.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
yield return new TestCaseData(@"Expires 14:16:32 January 31-st.").Returns(new DateTime(DateTime.Now.Year, 1, 31, 14, 16, 32));
yield return new TestCaseData(@"Expires 23rd January 2010.").Returns(new DateTime(2010, 1, 23, 0, 0, 0));
yield return new TestCaseData(@"Expires January 22nd, 2010.").Returns(new DateTime(2010, 1, 22, 0, 0, 0));
yield return new TestCaseData(@"Expires DEC 22, 2010.").Returns(new DateTime(2010, 12, 22, 0, 0, 0));
yield return new TestCaseData(@"Version: 1.0.0.692 6/1/2010 2:28:04 AM ").Returns(new DateTime(2010, 6, 1, 2, 28, 4));
yield return new TestCaseData(@"Version: 1.0.0.692 04/21/11 12:30am ").Returns(new DateTime(2011, 4, 21, 00, 30, 00));
yield return new TestCaseData(@"Version: 1.0.0.692 04/21/11 12:30pm ").Returns(new DateTime(2011, 4, 21, 12, 30, 00));
yield return new TestCaseData(@"Version: Thu Aug 06 22:32:15 MDT 2009 ").Returns(new DateTime(2009, 8, 6, 22, 32, 15));
}
}
[TestCaseSource("DateTimeTestCases")]
public DateTime should_parse_date(string date)
{
DateTimeRoutines.TryParseDateOrTime(date, DateTimeRoutines.DateTimeFormat.USDate, out var parsedDateTime);
return parsedDateTime.DateTime;
}
}
}

View File

@@ -30,9 +30,27 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("1", 1)]
[TestCase("11", 11)]
[TestCase("1000 grabs", 1000)]
[TestCase("2.222", 2222)]
[TestCase("2,222", 2222)]
[TestCase("2 222", 2222)]
[TestCase("2,22", 222)]
public void should_parse_int_from_string(string original, int parsedInt)
{
ParseUtil.CoerceInt(original).Should().Be(parsedInt);
}
[TestCase("1.0", 1.0)]
[TestCase("1.1", 1.1)]
[TestCase("1000 grabs", 1000.0)]
[TestCase("2.222", 2.222)]
[TestCase("2,222", 2.222)]
[TestCase("2.222,22", 2222.22)]
[TestCase("2,222.22", 2222.22)]
[TestCase("2 222", 2222.0)]
[TestCase("2,22", 2.22)]
public void should_parse_double_from_string(string original, double parsedInt)
{
ParseUtil.CoerceDouble(original).Should().Be(parsedInt);
}
}
}

View File

@@ -3,7 +3,7 @@
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.90" />
<PackageReference Include="Dapper" Version="2.0.123" />
<PackageReference Include="NBuilder" Version="6.1.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />

View File

@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Annotations
public string Section { get; set; }
public HiddenType Hidden { get; set; }
public PrivacyLevel Privacy { get; set; }
public string Placeholder { get; set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]

View File

@@ -23,8 +23,6 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
public LazyLibrarianSettings()
{
ProwlarrUrl = "http://localhost:9696";
BaseUrl = "http://localhost:5299";
SyncCategories = new[]
{
NewznabStandardCategory.AudioAudiobook.Id,
@@ -38,10 +36,10 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
};
}
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as LazyLibrarian sees it, including http(s)://, port, and urlbase if needed")]
[FieldDefinition(0, Label = "Prowlarr Server", HelpText = "Prowlarr server URL as LazyLibrarian sees it, including http(s)://, port, and urlbase if needed", Placeholder = "http://localhost:9696")]
public string ProwlarrUrl { get; set; }
[FieldDefinition(1, Label = "LazyLibrarian Server", HelpText = "URL used to connect to LazyLibrarian server, including http(s)://, port, and urlbase if required")]
[FieldDefinition(1, Label = "LazyLibrarian Server", HelpText = "URL used to connect to LazyLibrarian server, including http(s)://, port, and urlbase if required", Placeholder = "http://localhost:5299")]
public string BaseUrl { get; set; }
[FieldDefinition(2, Label = "ApiKey", Privacy = PrivacyLevel.ApiKey, HelpText = "The ApiKey generated by LazyLibrarian in Settings/Web Interface")]

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using FluentValidation.Results;
using Newtonsoft.Json;
using NLog;
@@ -31,13 +32,13 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
public LazyLibrarianStatus GetStatus(LazyLibrarianSettings settings)
{
var request = BuildRequest(settings, "/api", "getVersion", HttpMethod.GET);
var request = BuildRequest(settings, "/api", "getVersion", HttpMethod.Get);
return Execute<LazyLibrarianStatus>(request);
}
public List<LazyLibrarianIndexer> GetIndexers(LazyLibrarianSettings settings)
{
var request = BuildRequest(settings, "/api", "listNabProviders", HttpMethod.GET);
var request = BuildRequest(settings, "/api", "listNabProviders", HttpMethod.Get);
var response = Execute<LazyLibrarianIndexerResponse>(request);
@@ -76,7 +77,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
{ "providertype", indexerType.ToString().ToLower() }
};
var request = BuildRequest(settings, "/api", "delProvider", HttpMethod.GET, parameters);
var request = BuildRequest(settings, "/api", "delProvider", HttpMethod.Get, parameters);
CheckForError(Execute<LazyLibrarianStatus>(request));
}
@@ -92,7 +93,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
{ "categories", indexer.Categories }
};
var request = BuildRequest(settings, "/api", "addProvider", HttpMethod.GET, parameters);
var request = BuildRequest(settings, "/api", "addProvider", HttpMethod.Get, parameters);
CheckForError(Execute<LazyLibrarianStatus>(request));
return indexer;
}
@@ -110,7 +111,7 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
{ "altername", indexer.Altername }
};
var request = BuildRequest(settings, "/api", "changeProvider", HttpMethod.GET, parameters);
var request = BuildRequest(settings, "/api", "changeProvider", HttpMethod.Get, parameters);
CheckForError(Execute<LazyLibrarianStatus>(request));
return indexer;
}
@@ -133,12 +134,19 @@ namespace NzbDrone.Core.Applications.LazyLibrarian
{
return new ValidationFailure("ApiKey", status.Error.Message);
}
var indexers = GetIndexers(settings);
}
catch (HttpException ex)
{
_logger.Error(ex, "Unable to send test message");
return new ValidationFailure("BaseUrl", "Unable to complete application test");
}
catch (LazyLibrarianException ex)
{
_logger.Error(ex, "Connection test failed");
return new ValidationFailure("", ex.Message);
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to send test message");

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