1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

Compare commits

...

66 Commits

Author SHA1 Message Date
Qstick bb573594d9 Fix Blocklist Tests 2023-04-13 22:35:22 -05:00
Qstick 12e360ab4f Fixed: Some posters not showing for new movies search results
Fixes #8175

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2023-04-13 22:09:55 -05:00
Qstick bae555f63e Fixed: Custom Format calculation error on Blocklist by Movie
Fixes #8136
2023-04-13 21:55:01 -05:00
Qstick 31abb93d8c Bump dotnet to 6.0.16 2023-04-13 19:15:09 -05:00
LarsHelo 75035035e1 Fixed: Remote path mapping UI being partially hidden on smaller screens 2023-04-13 19:08:19 -05:00
bakerboy448 c1e5990a58 Update library clean helptext 2023-04-13 19:07:39 -05:00
Weblate 4a42ebe44c Translated using Weblate (German) [skip ci]
Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Czech) [skip ci]

Currently translated at 90.7% (1052 of 1159 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Portuguese) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Hebrew) [skip ci]

Currently translated at 92.9% (1077 of 1159 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 95.4% (1106 of 1159 strings)

Translated using Weblate (Arabic) [skip ci]

Currently translated at 90.4% (1048 of 1159 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.6% (1097 of 1159 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Norwegian Bokmål) [skip ci]

Currently translated at 22.5% (261 of 1159 strings)

Translated using Weblate (Greek) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1094 of 1159 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 99.5% (1154 of 1159 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 21.4% (249 of 1159 strings)

Co-authored-by: Bendik Remoy <Bendikremoy@hotmail.com>
Co-authored-by: Cassio Rizzi <clrizzi@gmail.com>
Co-authored-by: Deflector8249 <lh2jwko5@gomail.me>
Co-authored-by: Elias Benbourenane <eliasbenbourenane@gmail.com>
Co-authored-by: Florian <sephrat.flo@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Ivan Mazzoli <dreadtank27@gmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Michele Caputo <michele@caputoweb.xyz>
Co-authored-by: Nir Israel Hen <nirisraelh@gmail.com>
Co-authored-by: Remy <remy@mrbk.fr>
Co-authored-by: TheHrle <Hpranjkovic@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: federicofortini <federico.fortini@yahoo.it>
Co-authored-by: libsu <libsu@qq.com>
Co-authored-by: reloxx <reloxx@interia.pl>
Co-authored-by: tomas15420 <tomas15420@gmail.com>
Co-authored-by: zobe123 <manuel.zobl@gmx.at>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2023-04-13 19:06:57 -05:00
Qstick c82aa5c2c7 Fixed: Validation for CleanOriginalTitle
Fixes #8331
Closes #8332

Co-Authored-By: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
2023-04-13 19:03:13 -05:00
Qstick d1bae32e1c Fixed: Correct TitleThe naming token parsing 2023-04-13 18:59:15 -05:00
Qstick c514c7cac0 Fixed: Default value in new Delay Profile 2023-04-13 18:52:13 -05:00
Qstick 3d244057b5 Fixed: Don't grab propers/repacks when item in queue meets cutoff and propers/repacks are not downloaded automatically
Fixes #8134
2023-04-13 18:47:21 -05:00
Bakerboy448 323510300c Fixed: Cleanup erroneous QBittorrent Seeding calls 2023-04-13 18:14:36 -05:00
Bakerboy448 669d87b7ef Fixed: Include Digital Only Releases for Recent Movie determination
Use most recent of physical or digital release date
2023-04-13 18:14:08 -05:00
Bakerboy448 ec7f7b085a New: More information on on why hardlinks should be used over copying
Closes #8327

(cherry picked from commit 83a9d15ff8721c8effdc2c8055e37bfb757022d4)
2023-04-13 18:13:43 -05:00
Bogdan ecc906a754 Fixed: Config file settings do not need to be case-sensitive
(cherry picked from commit 2107635b7e7e5392624f2957af7d7b88ba6be283)
2023-04-11 20:00:35 -05:00
Bogdan d0fcac389c Fixed: Ensure default config file on starting app
(cherry picked from commit 5326a102e23eacfc1132eb544a92af737a531df5)
2023-04-11 20:00:35 -05:00
Qstick 621acbef9a Bump version to 4.4.4 2023-04-09 12:23:51 -05:00
Mark McDowall 7fb1163b23 Fixed: Number input changing while scrolling
(cherry picked from commit cc46ed56b4b70fe1f1443c0a927383f19c989c47)
2023-04-09 12:12:29 -05:00
Sergey M b48eda95dd Fixed: Mapping by MovieTitles collection instead of PrimaryMovieTitle (#8157)
Co-authored-by: Sergey M <msergein>
2023-04-02 16:53:24 -05:00
Mark McDowall 4627093616 Fixed: Multiple Downloaded Episodes Scan commands should not run in parallel
(cherry picked from commit b3d1e4f520d14c41aa6a7dff049ee9b9ef48fecb)
2023-04-02 15:28:14 -05:00
Lagicrus a006984d5e Fixed: Lock rating to 0 decimal places in Discovery (#8279)
* Locks to 1 decimal place instead of infinite
2023-04-02 15:27:36 -05:00
Qstick 7d9183ef12 Fixed: Queue error for language custom format on unknown items 2023-03-29 19:21:35 -05:00
Qstick d35c6683e9 New: Add indexer option for Discord on grab notifications
Fixes #8242

Co-Authored-By: lodu <48859312+lodu@users.noreply.github.com>
2023-03-29 19:01:11 -05:00
Qstick ac26bcddd9 Fixed: Page Plex Watchlist results
Fixes #8223
Fixes #8042
2023-03-29 18:44:18 -05:00
Devin Buhl 15bafce8cc Add Overview to CustomScript and Webhook Notifications (#8239)
* Add Overview to customscript
2023-03-29 18:34:29 -05:00
Qstick 2167da87ce Enable all analyzers and enforce code style on build
Fixes #8201
Fixes #8202
Fixes #8203
Fixes #8204
Fixes #8205
Fixes #8207
Fixes #8208
Fixes #8209
Fixes #8210
Fixes #8211
Fixes #8212
Fixes #8213
Fixes #8214
Fixes #8215
Closes #8216
Fixes #8217
Closes #8218
Fixes #8219
Closes #8220
2023-03-29 18:27:30 -05:00
Mark McDowall 926d37a572 Fixed: Permissions after installing on Windows and opening Firewall port
(cherry picked from commit ff2e8ffc372a34d08028db3c49f603cdfb87d832)
2023-03-29 17:43:11 -05:00
Bogdan 42c9e4e3e5 Fixed: Parsing of RoDubbed releases as Romanian 2023-03-24 20:40:01 -04:00
bakerboy448 89b609a221 Fixed: Improve some request failure messaging
(cherry picked from commit e968919e63616e30cc401964bd51db8e9e0e26de)

Fixes #8152
2023-03-24 20:38:48 -04:00
Qstick dfc9f74116 Fixed broken path tests
Fixes #8132

(cherry picked from commit 5a22afc42bb03c9cdfb0a46d470d084dbdd495d5)
2023-03-12 16:56:29 -05:00
Mark McDowall 189603c756 Fixed: USB drives mounted to folders are treated as different mounts
(cherry picked from commit 75378f7bde90b9d3d9b72404c25c017da2cd147c)
2023-03-12 16:56:29 -05:00
Mark McDowall a78693a2f7 Fixed: Prevent getting disk space from returning no information when it partially fails
(cherry picked from commit 2c65e4fa41418157d0d27b34c3bab80158cff219)
2023-03-12 16:07:22 -05:00
Servarr cea0c5033a Automated API Docs update 2023-03-12 16:02:48 -05:00
Derek Antrican 1e3a42bf42 Implemented OnMovieAdded for Discord 2023-03-12 16:00:41 -05:00
Qstick 030744ab7b Fixed: Indexer Flags CF Scores not shown in Search
Fixes #8165
2023-03-12 15:51:39 -05:00
Qstick 17fda02d8c Fixed: Drag with touch on Profiles page 2023-03-12 15:32:58 -05:00
cicomalieran aabf6b9ff8 Fixed: Processing very long ETA from Transmission
(cherry picked from commit 9800bd6b439257e73e3545e125cd03900a3036bb)
2023-03-12 12:28:33 -05:00
Mark McDowall 7b2fd5140b Switch to eslint for linting
(cherry picked from commit a18c3774661f466727ab46315211aecb43ef1def)
2023-03-08 14:31:03 +00:00
Weblate b6b10d7c6f Translated using Weblate (Croatian) [skip ci]
Currently translated at 19.7% (229 of 1159 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Russian) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Portuguese) [skip ci]

Currently translated at 98.8% (1146 of 1159 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.5% (1107 of 1159 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Update translation files  [skip ci]

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Spanish) [skip ci]

Currently translated at 98.7% (1145 of 1159 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 16.1% (187 of 1159 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 16.1% (187 of 1159 strings)

Translated using Weblate (Catalan) [skip ci]

Currently translated at 100.0% (1159 of 1159 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 13.2% (153 of 1159 strings)

Translated using Weblate (Croatian) [skip ci]

Currently translated at 13.2% (153 of 1159 strings)

Translated using Weblate (Chinese (Traditional) (zh_TW)) [skip ci]

Currently translated at 1.6% (19 of 1159 strings)

Translated using Weblate (Ukrainian) [skip ci]

Currently translated at 99.8% (1157 of 1159 strings)

Translated using Weblate (Slovak) [skip ci]

Currently translated at 21.4% (249 of 1159 strings)

Translated using Weblate (Norwegian Bokmål) [skip ci]

Currently translated at 22.1% (257 of 1159 strings)

Translated using Weblate (Catalan) [skip ci]

Currently translated at 98.9% (1147 of 1159 strings)

Translated using Weblate (Arabic) [skip ci]

Currently translated at 90.3% (1047 of 1159 strings)

Translated using Weblate (Chinese (Simplified) (zh_CN)) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Translated using Weblate (Thai) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Bulgarian) [skip ci]

Currently translated at 87.8% (1018 of 1159 strings)

Translated using Weblate (Hindi) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Romanian) [skip ci]

Currently translated at 87.5% (1015 of 1159 strings)

Translated using Weblate (Vietnamese) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Turkish) [skip ci]

Currently translated at 89.7% (1040 of 1159 strings)

Translated using Weblate (Swedish) [skip ci]

Currently translated at 90.7% (1052 of 1159 strings)

Translated using Weblate (Russian) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Translated using Weblate (Portuguese) [skip ci]

Currently translated at 98.8% (1146 of 1159 strings)

Translated using Weblate (Polish) [skip ci]

Currently translated at 98.8% (1146 of 1159 strings)

Translated using Weblate (Dutch) [skip ci]

Currently translated at 95.5% (1107 of 1159 strings)

Translated using Weblate (Korean) [skip ci]

Currently translated at 21.8% (253 of 1159 strings)

Translated using Weblate (Japanese) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Icelandic) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Hungarian) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Translated using Weblate (Hebrew) [skip ci]

Currently translated at 92.8% (1076 of 1159 strings)

Translated using Weblate (Finnish) [skip ci]

Currently translated at 99.8% (1157 of 1159 strings)

Translated using Weblate (Greek) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Translated using Weblate (Danish) [skip ci]

Currently translated at 89.9% (1042 of 1159 strings)

Translated using Weblate (Czech) [skip ci]

Currently translated at 90.7% (1052 of 1159 strings)

Translated using Weblate (Italian) [skip ci]

Currently translated at 94.3% (1094 of 1159 strings)

Translated using Weblate (Spanish) [skip ci]

Currently translated at 98.5% (1142 of 1159 strings)

Translated using Weblate (French) [skip ci]

Currently translated at 99.5% (1154 of 1159 strings)

Translated using Weblate (German) [skip ci]

Currently translated at 99.9% (1158 of 1159 strings)

Added translation using Weblate (Tamil) [skip ci]

Added translation using Weblate (Indonesian) [skip ci]

Added translation using Weblate (Estonian) [skip ci]

Added translation using Weblate (Serbian) [skip ci]

Added translation using Weblate (Croatian) [skip ci]

Added translation using Weblate (Bosnian) [skip ci]

Translated using Weblate (Chinese (Traditional) (zh_TW)) [skip ci]

Currently translated at 1.5% (18 of 1157 strings)

Added translation using Weblate (Spanish (Mexico)) [skip ci]

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Qstick <qstick@gmail.com>
Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Co-authored-by: TheHrle <Hpranjkovic@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: dtalens <databio@gmail.com>
Co-authored-by: fiego14 <alvaross_96@hotmail.com>
Co-authored-by: libsu <libsu@qq.com>
Co-authored-by: pedrom20 <pedrom20@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_TW/
Translation: Servarr/Radarr
2023-02-27 19:12:11 -06:00
Qstick 16fcf0b56b New: Additional custom filter predicates for strings
(cherry picked from commit 6082253166b67d59d7907d83c362116d47bcdaeb)
2023-02-27 19:10:58 -06:00
Bakerboy448 c222a1a434 New: Use Best PageSize for Newznab/Torznab
Max of Default or Max and no more than 100
2023-02-12 18:34:22 -06:00
Qstick c6e91e028b New: Add Additional Languages
Fixes #6257
Fixes #7967
Closes #7592
Reference #7788

Co-Authored-By: dtalens <6631832+dtalens@users.noreply.github.com>
2023-02-12 15:03:45 -06:00
Bakerboy448 fcf5984944 Fixed: Translations 2023-02-12 13:29:46 -06:00
Mark McDowall fdfe8ca656 New: Return static response to requests while app is starting
(cherry picked from commit 303fc5d786ccf2ad14c8523fc239696c5d37ea53)

Fixes #8079
Closes #8080
2023-02-12 13:29:46 -06:00
Qstick 150a5c1fc6 Bump version to 4.4.2 2023-02-11 13:23:52 -06:00
Matthew Barrington 9ea0957351 New: Add Ireland as a Certification Country (#8085)
Co-authored-by: Matthew Barrington <git@barrington.it>
2023-02-11 12:36:04 -06:00
Weblate 8befa436cc Translated using Weblate (French) [skip ci]
Currently translated at 99.6% (1153 of 1157 strings)

Translated using Weblate (Portuguese (Brazil)) [skip ci]

Currently translated at 100.0% (1157 of 1157 strings)

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: KevoM <lilmarsu@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translation: Servarr/Radarr
2023-02-11 12:35:19 -06:00
Qstick 53fdb6f07f Delete azuresync.yml 2023-02-11 10:36:40 -06:00
Servarr 0697dbff96 Automated API Docs update 2023-02-07 20:26:31 -06:00
Qstick 34924859aa Fixed: Settings fail to save for some auth setups
(cherry picked from commit a379d0c403449b2623f84aa6851c850971528ff8)
2023-02-07 20:24:48 -06:00
Qstick 9c86598b54 Fixup language specification tests 2023-02-06 19:38:44 -06:00
Qstick 0fe2262162 Fixed: Releases incorrectly rejected due to language 2023-02-06 19:06:03 -06:00
Qstick 47353aea75 Fixed: Avoid failure on null SceneName 2023-02-05 23:31:00 -06:00
Giulia Petenazzi af43cb2aca New: Added release year to queue ( issue #6330) (#8019) 2023-02-05 19:09:20 -06:00
Fuochi bc838b74c7 Fixed: Remove initial dot in filename (#4509) 2023-02-05 17:22:07 -06:00
Qstick cbcf3d1058 New: Custom Format Updates (#8067) 2023-02-05 17:09:37 -06:00
Qstick c72e64f081 Bump version to 4.4.2 2023-02-04 21:15:36 -06:00
Qstick e09607edb0 Remove old, broken test
Fixes #7186
2023-02-04 21:12:22 -06:00
Winter d91578aee3 Fixed: Releases from PTP showing skewed publish date
PTP returns UTC timestamps, without a timezone specifier. Previously, users
would see skewed publish dates, as the UTC timestamps were being parsed
as if they were in the system's timezone. To fix this, we just assume the
publish date is in UTC.
2023-02-04 17:46:23 -06:00
Mark McDowall affedd7f9d Fixed: Ping endpoint no longer requires authentication
(cherry picked from commit ad42d4a14c814d5911dafb5e78e97ec09b4b13a5)
2023-02-04 17:44:37 -06:00
Qstick c3665e9fea New: Spanish (Latino) languages
Closes #7914
Closes #3467
Closes #6415
2023-02-04 17:42:39 -06:00
Mark McDowall 364d8bd7c5 Fixed: Don't try to remove the same item from queue multiple times
Closes #7932

(cherry picked from commit 2491da067815e129df3a3a79c0cc7221a9d87094)
2023-02-04 17:32:18 -06:00
Mark McDowall 7142d1f224 Improve usage of Original Title renaming token
Closes #7168

Fixed: Don't recursively add the current file name to new file name when '{Original Title}' is used in addition to other naming tokens
(cherry picked from commit ebb48a19cc792c71bfbd57d5f106067190d95339)
2023-02-04 17:26:07 -06:00
Stevie Robinson 86777e021b Fixed: Mass Editor Footer on Smaller Screens
Closes #6968

(cherry picked from commit 9afcec8b1ffc11da93ae50b73f77f5ebe6e12391)
2023-02-04 17:26:07 -06:00
bakerboy448 9d2dacea97 New: Improve Manual Import logging when not parsing files
Closes #8059

(cherry picked from commit 83f63590630ae0728fd9f9f03567a294934eebcc)
2023-02-04 17:26:07 -06:00
Mark McDowall d98c86c3d9 Fixed: Parse year in title from square brackets
(cherry picked from commit 99e60196a4e513d6340a090de4a5517f205e7a29)
2023-02-04 17:22:26 -06:00
238 changed files with 2058 additions and 1796 deletions
+15 -11
View File
@@ -19,10 +19,10 @@ indent_size = 4
dotnet_sort_system_directives_first = true dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary # Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:warning dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:warning dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:warning dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:warning dotnet_style_qualification_for_event = false:refactoring
# Indentation preferences # Indentation preferences
csharp_indent_block_contents = true csharp_indent_block_contents = true
@@ -32,6 +32,10 @@ csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true csharp_indent_switch_labels = true
csharp_indent_labels = flush_left csharp_indent_labels = flush_left
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_naming_style.instance_field_style.capitalization = camel_case dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _ dotnet_naming_style.instance_field_style.required_prefix = _
@@ -64,6 +68,7 @@ dotnet_diagnostic.SA1406.severity = suggestion
dotnet_diagnostic.SA1410.severity = suggestion dotnet_diagnostic.SA1410.severity = suggestion
dotnet_diagnostic.SA1411.severity = suggestion dotnet_diagnostic.SA1411.severity = suggestion
dotnet_diagnostic.SA1413.severity = none dotnet_diagnostic.SA1413.severity = none
dotnet_diagnostic.SA1512.severity = none
dotnet_diagnostic.SA1516.severity = none dotnet_diagnostic.SA1516.severity = none
dotnet_diagnostic.SA1600.severity = none dotnet_diagnostic.SA1600.severity = none
dotnet_diagnostic.SA1601.severity = none dotnet_diagnostic.SA1601.severity = none
@@ -162,6 +167,7 @@ dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = suggestion dotnet_diagnostic.CA1310.severity = suggestion
dotnet_diagnostic.CA1401.severity = suggestion dotnet_diagnostic.CA1401.severity = suggestion
dotnet_diagnostic.CA1416.severity = suggestion dotnet_diagnostic.CA1416.severity = suggestion
dotnet_diagnostic.CA1419.severity = suggestion
dotnet_diagnostic.CA1507.severity = suggestion dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1508.severity = suggestion dotnet_diagnostic.CA1508.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion dotnet_diagnostic.CA1707.severity = suggestion
@@ -177,9 +183,6 @@ dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1721.severity = suggestion dotnet_diagnostic.CA1721.severity = suggestion
dotnet_diagnostic.CA1724.severity = suggestion dotnet_diagnostic.CA1724.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1801.severity = suggestion
dotnet_diagnostic.CA1802.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1806.severity = suggestion dotnet_diagnostic.CA1806.severity = suggestion
dotnet_diagnostic.CA1810.severity = suggestion dotnet_diagnostic.CA1810.severity = suggestion
dotnet_diagnostic.CA1812.severity = suggestion dotnet_diagnostic.CA1812.severity = suggestion
@@ -191,13 +194,11 @@ dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA2008.severity = suggestion dotnet_diagnostic.CA2008.severity = suggestion
dotnet_diagnostic.CA2009.severity = suggestion
dotnet_diagnostic.CA2010.severity = suggestion
dotnet_diagnostic.CA2011.severity = suggestion
dotnet_diagnostic.CA2012.severity = suggestion dotnet_diagnostic.CA2012.severity = suggestion
dotnet_diagnostic.CA2013.severity = suggestion dotnet_diagnostic.CA2013.severity = suggestion
dotnet_diagnostic.CA2100.severity = suggestion dotnet_diagnostic.CA2100.severity = suggestion
@@ -228,6 +229,9 @@ dotnet_diagnostic.CA2243.severity = suggestion
dotnet_diagnostic.CA2244.severity = suggestion dotnet_diagnostic.CA2244.severity = suggestion
dotnet_diagnostic.CA2245.severity = suggestion dotnet_diagnostic.CA2245.severity = suggestion
dotnet_diagnostic.CA2246.severity = suggestion dotnet_diagnostic.CA2246.severity = suggestion
dotnet_diagnostic.CA2249.severity = suggestion
dotnet_diagnostic.CA2251.severity = suggestion
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CA3061.severity = suggestion dotnet_diagnostic.CA3061.severity = suggestion
dotnet_diagnostic.CA3075.severity = suggestion dotnet_diagnostic.CA3075.severity = suggestion
dotnet_diagnostic.CA3076.severity = suggestion dotnet_diagnostic.CA3076.severity = suggestion
@@ -255,7 +259,7 @@ dotnet_diagnostic.CA5392.severity = suggestion
dotnet_diagnostic.CA5394.severity = suggestion dotnet_diagnostic.CA5394.severity = suggestion
dotnet_diagnostic.CA5397.severity = suggestion dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.SYSLIB0006.severity = none
[*.{js,html,js,hbs,less,css}] [*.{js,html,js,hbs,less,css}]
charset = utf-8 charset = utf-8
-9
View File
@@ -1,9 +0,0 @@
{
"paths": [
"frontend/src/**/*.js"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}
-45
View File
@@ -1,45 +0,0 @@
name: Sync issue to Azure DevOps work item
on:
issues:
types:
[opened, edited, deleted, closed, reopened, labeled, unlabeled, assigned]
concurrency: azuresync-${{ github.event.issue.number }}
permissions: {}
jobs:
alert:
permissions:
issues: write # to update issue body
runs-on: ubuntu-latest
steps:
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == true }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "Bug"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
- uses: danhellem/github-actions-issue-to-work-item@master
if: "${{ contains(github.event.issue.labels.*.name, 'Type: Bug') == false }}"
env:
ado_token: "${{ secrets.ADO_PERSONAL_ACCESS_TOKEN }}"
github_token: "${{ github.token }}"
ado_organization: "Servarr"
ado_project: "Servarr"
ado_area_path: "Servarr\\Radarr"
ado_wit: "User Story"
ado_new_state: "New"
ado_active_state: "Active"
ado_close_state: "Closed"
ado_bypassrules: true
log_level: 100
+2 -2
View File
@@ -9,13 +9,13 @@ variables:
testsFolder: './_tests' testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '4.4.1' majorVersion: '4.4.4'
minorVersion: $[counter('minorVersion', 2000)] minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)' radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)' buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr' sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com' sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.400' dotnetVersion: '6.0.408'
nodeVersion: '16.X' nodeVersion: '16.X'
innoVersion: '6.2.0' innoVersion: '6.2.0'
windowsImage: 'windows-2022' windowsImage: 'windows-2022'
@@ -1,3 +1,4 @@
// eslint-disable-next-line filenames/match-exported
const loaderUtils = require('loader-utils'); const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) { module.exports = function cssVariablesLoader(source) {
+4 -2
View File
@@ -56,6 +56,7 @@ class HistoryRow extends Component {
movie, movie,
quality, quality,
customFormats, customFormats,
customFormatScore,
languages, languages,
qualityCutoffNotMet, qualityCutoffNotMet,
eventType, eventType,
@@ -175,7 +176,7 @@ class HistoryRow extends Component {
key={name} key={name}
className={styles.customFormatScore} className={styles.customFormatScore}
> >
{formatCustomFormatScore(data.customFormatScore)} {formatCustomFormatScore(customFormatScore)}
</TableRowCell> </TableRowCell>
); );
} }
@@ -241,8 +242,9 @@ HistoryRow.propTypes = {
movie: PropTypes.object.isRequired, movie: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
eventType: PropTypes.string.isRequired, eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
date: PropTypes.string.isRequired, date: PropTypes.string.isRequired,
+12
View File
@@ -128,6 +128,7 @@ class QueueRow extends Component {
{ {
columns.map((column) => { columns.map((column) => {
const { const {
name, name,
isVisible isVisible
@@ -234,6 +235,16 @@ class QueueRow extends Component {
); );
} }
if (name === 'year') {
return (
<TableRowCell key={name}>
{
movie ? movie.year : ''
}
</TableRowCell>
);
}
if (name === 'title') { if (name === 'title') {
return ( return (
<TableRowCell key={name}> <TableRowCell key={name}>
@@ -362,6 +373,7 @@ QueueRow.propTypes = {
estimatedCompletionTime: PropTypes.string, estimatedCompletionTime: PropTypes.string,
timeleft: PropTypes.string, timeleft: PropTypes.string,
size: PropTypes.number, size: PropTypes.number,
year: PropTypes.number,
sizeleft: PropTypes.number, sizeleft: PropTypes.number,
showRelativeDates: PropTypes.bool.isRequired, showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired, shortDateFormat: PropTypes.string.isRequired,
@@ -113,6 +113,7 @@ class AddNewMovieSearchResult extends Component {
images={images} images={images}
size={250} size={250}
overflow={true} overflow={true}
lazy={false}
/> />
</div> </div>
@@ -162,7 +162,7 @@ class CalendarOptionsModalContent extends Component {
values={weekColumnOptions} values={weekColumnOptions}
value={calendarWeekColumnHeader} value={calendarWeekColumnHeader}
onChange={this.onGlobalInputChange} onChange={this.onGlobalInputChange}
helpText={translate('HelpText')} helpText={translate('SettingsWeekColumnHeaderHelpText')}
/> />
</FormGroup> </FormGroup>
@@ -112,6 +112,12 @@ class TextInput extends Component {
this._isMouseTarget = false; this._isMouseTarget = false;
}; };
onWheel = () => {
if (this.props.type === 'number') {
this._input.blur();
}
};
// //
// Render // Render
@@ -161,6 +167,7 @@ class TextInput extends Component {
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}
onMouseDown={this.onMouseDown} onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp} onMouseUp={this.onMouseUp}
onWheel={this.onWheel}
/> />
); );
} }
+4 -3
View File
@@ -1,6 +1,7 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import translate from 'Utilities/String/translate';
import EnhancedSelectInput from './EnhancedSelectInput'; import EnhancedSelectInput from './EnhancedSelectInput';
import styles from './UMaskInput.css'; import styles from './UMaskInput.css';
@@ -101,16 +102,16 @@ class UMaskInput extends Component {
</div> </div>
<div className={styles.details}> <div className={styles.details}>
<div> <div>
<label>UMask</label> <label>{translate('UMask')}</label>
<div className={styles.value}>{umask}</div> <div className={styles.value}>{umask}</div>
</div> </div>
<div> <div>
<label>Folder</label> <label>{translate('Folder')}</label>
<div className={styles.value}>{folder}</div> <div className={styles.value}>{folder}</div>
<div className={styles.unit}>d{formatPermissions(folderNum)}</div> <div className={styles.unit}>d{formatPermissions(folderNum)}</div>
</div> </div>
<div> <div>
<label>File</label> <label>{translate('File')}</label>
<div className={styles.value}>{file}</div> <div className={styles.value}>{file}</div>
<div className={styles.unit}>{formatPermissions(fileNum)}</div> <div className={styles.unit}>{formatPermissions(fileNum)}</div>
</div> </div>
@@ -19,7 +19,7 @@
} }
} }
@media only screen and (max-width: $breakpointLarge) { @media only screen and (max-width: $breakpointExtraLarge) {
.contentFooter { .contentFooter {
flex-wrap: wrap; flex-wrap: wrap;
} }
@@ -73,7 +73,7 @@ function getInfoRowProps(row, props) {
return { return {
title: translate('Ratings'), title: translate('Ratings'),
iconName: icons.HEART, iconName: icons.HEART,
label: `${props.ratings.tmdb.value * 10}%` label: `${(props.ratings.tmdb.value * 10).toFixed()}%`
}; };
} }
@@ -47,6 +47,10 @@ export const possibleFilterTypes = {
{ key: filterTypes.CONTAINS, value: 'contains' }, { key: filterTypes.CONTAINS, value: 'contains' },
{ key: filterTypes.NOT_CONTAINS, value: 'does not contain' }, { key: filterTypes.NOT_CONTAINS, value: 'does not contain' },
{ key: filterTypes.EQUAL, value: 'equal' }, { key: filterTypes.EQUAL, value: 'equal' },
{ key: filterTypes.NOT_EQUAL, value: 'not equal' } { key: filterTypes.NOT_EQUAL, value: 'not equal' },
{ key: filterTypes.STARTS_WITH, value: 'starts with' },
{ key: filterTypes.NOT_STARTS_WITH, value: 'does not start with' },
{ key: filterTypes.ENDS_WITH, value: 'ends with' },
{ key: filterTypes.NOT_ENDS_WITH, value: 'does not end with' }
] ]
}; };
@@ -39,6 +39,22 @@ const filterTypePredicates = {
[filterTypes.NOT_EQUAL]: function(itemValue, filterValue) { [filterTypes.NOT_EQUAL]: function(itemValue, filterValue) {
return itemValue !== filterValue; return itemValue !== filterValue;
},
[filterTypes.STARTS_WITH]: function(itemValue, filterValue) {
return itemValue.toLowerCase().startsWith(filterValue.toLowerCase());
},
[filterTypes.NOT_STARTS_WITH]: function(itemValue, filterValue) {
return !itemValue.toLowerCase().startsWith(filterValue.toLowerCase());
},
[filterTypes.ENDS_WITH]: function(itemValue, filterValue) {
return itemValue.toLowerCase().endsWith(filterValue.toLowerCase());
},
[filterTypes.NOT_ENDS_WITH]: function(itemValue, filterValue) {
return !itemValue.toLowerCase().endsWith(filterValue.toLowerCase());
} }
}; };
+9 -1
View File
@@ -10,6 +10,10 @@ export const LESS_THAN = 'lessThan';
export const LESS_THAN_OR_EQUAL = 'lessThanOrEqual'; export const LESS_THAN_OR_EQUAL = 'lessThanOrEqual';
export const NOT_CONTAINS = 'notContains'; export const NOT_CONTAINS = 'notContains';
export const NOT_EQUAL = 'notEqual'; export const NOT_EQUAL = 'notEqual';
export const STARTS_WITH = 'startsWith';
export const NOT_STARTS_WITH = 'notStartsWith';
export const ENDS_WITH = 'endsWith';
export const NOT_ENDS_WITH = 'notEndsWith';
export const all = [ export const all = [
CONTAINS, CONTAINS,
@@ -23,5 +27,9 @@ export const all = [
IN_LAST, IN_LAST,
NOT_IN_LAST, NOT_IN_LAST,
IN_NEXT, IN_NEXT,
NOT_IN_NEXT NOT_IN_NEXT,
STARTS_WITH,
NOT_STARTS_WITH,
ENDS_WITH,
NOT_ENDS_WITH
]; ];
@@ -64,6 +64,15 @@ const columns = [
isSortable: true, isSortable: true,
isVisible: true isVisible: true
}, },
{
name: 'customFormats',
label: React.createElement(Icon, {
name: icons.INTERACTIVE,
title: translate('CustomFormat')
}),
isSortable: true,
isVisible: true
},
{ {
name: 'rejections', name: 'rejections',
label: React.createElement(Icon, { label: React.createElement(Icon, {
@@ -5,8 +5,10 @@
} }
.quality, .quality,
.language { .languages {
composes: cell from '~Components/Table/Cells/TableRowCell.css'; composes: cell from '~Components/Table/Cells/TableRowCell.css';
text-align: center;
} }
.label { .label {
@@ -21,3 +23,7 @@
margin-top: 0; margin-top: 0;
text-align: start; text-align: start;
} }
.customFormatTooltip {
max-width: 250px;
}
@@ -12,6 +12,7 @@ import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal'
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal'; import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal'; import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal'; import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage'; import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality'; import MovieQuality from 'Movie/MovieQuality';
import formatBytes from 'Utilities/Number/formatBytes'; import formatBytes from 'Utilities/Number/formatBytes';
@@ -150,6 +151,7 @@ class InteractiveImportRow extends Component {
languages, languages,
releaseGroup, releaseGroup,
size, size,
customFormats,
rejections, rejections,
isReprocessing, isReprocessing,
isSelected, isSelected,
@@ -226,7 +228,7 @@ class InteractiveImportRow extends Component {
</TableRowCellButton> </TableRowCellButton>
<TableRowCellButton <TableRowCellButton
className={styles.language} className={styles.languages}
title={translate('ClickToChangeLanguage')} title={translate('ClickToChangeLanguage')}
onPress={this.onSelectLanguagePress} onPress={this.onSelectLanguagePress}
> >
@@ -259,7 +261,26 @@ class InteractiveImportRow extends Component {
<TableRowCell> <TableRowCell>
{ {
!!rejections.length && customFormats?.length ?
<Popover
anchor={
<Icon name={icons.INTERACTIVE} />
}
title={translate('Formats')}
body={
<div className={styles.customFormatTooltip}>
<MovieFormats formats={customFormats} />
</div>
}
position={tooltipPositions.LEFT}
/> :
null
}
</TableRowCell>
<TableRowCell>
{
rejections.length ?
<Popover <Popover
anchor={ anchor={
<Icon <Icon
@@ -282,7 +303,9 @@ class InteractiveImportRow extends Component {
</ul> </ul>
} }
position={tooltipPositions.LEFT} position={tooltipPositions.LEFT}
/> canFlip={false}
/> :
null
} }
</TableRowCell> </TableRowCell>
@@ -330,6 +353,7 @@ InteractiveImportRow.propTypes = {
languages: PropTypes.arrayOf(PropTypes.object), languages: PropTypes.arrayOf(PropTypes.object),
releaseGroup: PropTypes.string, releaseGroup: PropTypes.string,
size: PropTypes.number.isRequired, size: PropTypes.number.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
rejections: PropTypes.arrayOf(PropTypes.object).isRequired, rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
isReprocessing: PropTypes.bool, isReprocessing: PropTypes.bool,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
@@ -62,6 +62,7 @@ class MovieHistoryRow extends Component {
sourceTitle, sourceTitle,
quality, quality,
customFormats, customFormats,
customFormatScore,
languages, languages,
qualityCutoffNotMet, qualityCutoffNotMet,
date, date,
@@ -106,7 +107,7 @@ class MovieHistoryRow extends Component {
</TableRowCell> </TableRowCell>
<TableRowCell key={name}> <TableRowCell key={name}>
{formatCustomFormatScore(data.customFormatScore)} {formatCustomFormatScore(customFormatScore)}
</TableRowCell> </TableRowCell>
<RelativeDateCellConnector <RelativeDateCellConnector
@@ -161,7 +162,8 @@ MovieHistoryRow.propTypes = {
sourceTitle: PropTypes.string.isRequired, sourceTitle: PropTypes.string.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired, customFormats: PropTypes.arrayOf(PropTypes.object),
customFormatScore: PropTypes.number.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired, qualityCutoffNotMet: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired, date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
@@ -8,11 +8,15 @@
} }
.host { .host {
flex: 0 0 300px; @add-mixin truncate;
flex: 0 1 300px;
} }
.path { .path {
flex: 0 0 400px; @add-mixin truncate;
flex: 0 1 400px;
} }
.actions { .actions {
@@ -1,15 +1,20 @@
.remotePathMappingsHeader { .remotePathMappingsHeader {
display: flex; display: flex;
margin-bottom: 10px; margin-bottom: 10px;
padding-right: 24px;
font-weight: bold; font-weight: bold;
} }
.host { .host {
flex: 0 0 300px; @add-mixin truncate;
flex: 0 1 300px;
} }
.path { .path {
flex: 0 0 400px; @add-mixin truncate;
flex: 0 1 400px;
} }
.addRemotePathMapping { .addRemotePathMapping {
@@ -18,6 +18,7 @@ export const certificationCountryOptions = [
{ key: 'fr', value: 'France' }, { key: 'fr', value: 'France' },
{ key: 'de', value: 'Germany' }, { key: 'de', value: 'Germany' },
{ key: 'gb', value: 'Great Britain' }, { key: 'gb', value: 'Great Britain' },
{ key: 'ie', value: 'Ireland' },
{ key: 'it', value: 'Italy' }, { key: 'it', value: 'Italy' },
{ key: 'es', value: 'Spain' }, { key: 'es', value: 'Spain' },
{ key: 'us', value: 'United States' }, { key: 'us', value: 'United States' },
@@ -191,6 +191,7 @@ const delayProfileShape = {
enableTorrent: PropTypes.shape(boolSettingShape).isRequired, enableTorrent: PropTypes.shape(boolSettingShape).isRequired,
usenetDelay: PropTypes.shape(numberSettingShape).isRequired, usenetDelay: PropTypes.shape(numberSettingShape).isRequired,
torrentDelay: PropTypes.shape(numberSettingShape).isRequired, torrentDelay: PropTypes.shape(numberSettingShape).isRequired,
bypassIfHighestQuality: PropTypes.shape(boolSettingShape).isRequired,
order: PropTypes.shape(numberSettingShape), order: PropTypes.shape(numberSettingShape),
tags: PropTypes.shape(tagSettingShape).isRequired tags: PropTypes.shape(tagSettingShape).isRequired
}; };
@@ -13,6 +13,7 @@ const newDelayProfile = {
preferredProtocol: 'usenet', preferredProtocol: 'usenet',
usenetDelay: 0, usenetDelay: 0,
torrentDelay: 0, torrentDelay: 0,
bypassIfHighestQuality: false,
tags: [] tags: []
}; };
+3 -3
View File
@@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { DndProvider } from 'react-dnd'; import { DndProvider } from 'react-dnd-multi-backend';
import { HTML5Backend } from 'react-dnd-html5-backend'; import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch';
import Link from 'Components/Link/Link'; import Link from 'Components/Link/Link';
import PageContent from 'Components/Page/PageContent'; import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody'; import PageContentBody from 'Components/Page/PageContentBody';
@@ -25,7 +25,7 @@ class Profiles extends Component {
/> />
<PageContentBody> <PageContentBody>
<DndProvider backend={HTML5Backend}> <DndProvider options={HTML5toTouch}>
<QualityProfilesConnector /> <QualityProfilesConnector />
<DelayProfilesConnector /> <DelayProfilesConnector />
<div className={styles.addCustomFormatMessage}> <div className={styles.addCustomFormatMessage}>
@@ -118,6 +118,12 @@ export const defaultState = {
isSortable: true, isSortable: true,
isVisible: false isVisible: false
}, },
{
name: 'year',
label: translate('Year'),
isSortable: true,
isVisible: true
},
{ {
name: 'outputPath', name: 'outputPath',
label: translate('OutputPath'), label: translate('OutputPath'),
@@ -1,5 +1,4 @@
function formatCustomFormatScore(input, customFormatsLength = 0) {
function formatCustomFormatScore(input) {
const score = Number(input); const score = Number(input);
if (score > 0) { if (score > 0) {
@@ -10,7 +9,7 @@ function formatCustomFormatScore(input) {
return score; return score;
} }
return ''; return customFormatsLength > 0 ? '+0' : '';
} }
export default formatCustomFormatScore; export default formatCustomFormatScore;
+4 -5
View File
@@ -8,8 +8,8 @@
"clean": "rimraf ./_output/UI && rimraf \"**/*.js.map\"", "clean": "rimraf ./_output/UI && rimraf \"**/*.js.map\"",
"start": "webpack --watch --config ./frontend/build/webpack.config.js", "start": "webpack --watch --config ./frontend/build/webpack.config.js",
"watch": "webpack --watch --config ./frontend/build/webpack.config.js", "watch": "webpack --watch --config ./frontend/build/webpack.config.js",
"lint": "esprint check", "lint": "eslint --config frontend/.eslintrc.js --ignore-path frontend/.eslintignore frontend/",
"lint-fix": "esprint check --fix", "lint-fix": "yarn lint --fix",
"stylelint-linux": "stylelint $(find frontend -name '*.css') --config frontend/.stylelintrc", "stylelint-linux": "stylelint $(find frontend -name '*.css') --config frontend/.stylelintrc",
"stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc" "stylelint-windows": "stylelint frontend/**/*.css --config frontend/.stylelintrc"
}, },
@@ -30,7 +30,7 @@
"@fortawesome/free-regular-svg-icons": "6.1.0", "@fortawesome/free-regular-svg-icons": "6.1.0",
"@fortawesome/free-solid-svg-icons": "6.1.0", "@fortawesome/free-solid-svg-icons": "6.1.0",
"@fortawesome/react-fontawesome": "0.1.18", "@fortawesome/react-fontawesome": "0.1.18",
"@microsoft/signalr": "6.0.8", "@microsoft/signalr": "6.0.16",
"@sentry/browser": "6.18.2", "@sentry/browser": "6.18.2",
"@sentry/integrations": "6.18.2", "@sentry/integrations": "6.18.2",
"classnames": "2.3.1", "classnames": "2.3.1",
@@ -108,8 +108,7 @@
"eslint-plugin-json": "3.1.0", "eslint-plugin-json": "3.1.0",
"eslint-plugin-react": "7.29.4", "eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "7.0.0", "eslint-plugin-simple-import-sort": "8.0.0",
"esprint": "3.3.0",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"filemanager-webpack-plugin": "5.0.0", "filemanager-webpack-plugin": "5.0.0",
"html-webpack-plugin": "5.3.1", "html-webpack-plugin": "5.3.1",
+3
View File
@@ -0,0 +1,3 @@
is_global = true
dotnet_diagnostic.CA1014.severity = none
+3 -1
View File
@@ -1,7 +1,9 @@
<Project> <Project>
<!-- Common to all Radarr Projects --> <!-- Common to all Radarr Projects -->
<PropertyGroup> <PropertyGroup>
<AnalysisLevel>6.0-all</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles> <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -90,7 +92,7 @@
<!-- Standard testing packages --> <!-- Standard testing packages -->
<ItemGroup Condition="'$(TestProject)'=='true'"> <ItemGroup Condition="'$(TestProject)'=='true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="NUnit" Version="3.13.2" /> <PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageReference Include="NunitXml.TestLogger" Version="3.0.117" /> <PackageReference Include="NunitXml.TestLogger" Version="3.0.117" />
+1 -1
View File
@@ -356,7 +356,7 @@ namespace NzbDrone.Common.Disk
} }
} }
public string GetPathRoot(string path) public virtual string GetPathRoot(string path)
{ {
Ensure.That(path, () => path).IsValidPath(); Ensure.That(path, () => path).IsValidPath();
@@ -11,6 +11,7 @@ namespace NzbDrone.Common.EnvironmentInfo
public interface IAppFolderFactory public interface IAppFolderFactory
{ {
void Register(); void Register();
void SetPermissions();
} }
public class AppFolderFactory : IAppFolderFactory public class AppFolderFactory : IAppFolderFactory
@@ -58,7 +59,7 @@ namespace NzbDrone.Common.EnvironmentInfo
InitializeMonoApplicationData(); InitializeMonoApplicationData();
} }
private void SetPermissions() public void SetPermissions()
{ {
try try
{ {
@@ -9,6 +9,7 @@ namespace NzbDrone.Common.EnvironmentInfo
bool IsAdmin { get; } bool IsAdmin { get; }
bool IsWindowsService { get; } bool IsWindowsService { get; }
bool IsWindowsTray { get; } bool IsWindowsTray { get; }
bool IsStarting { get; set; }
bool IsExiting { get; set; } bool IsExiting { get; set; }
bool IsTray { get; } bool IsTray { get; }
RuntimeMode Mode { get; } RuntimeMode Mode { get; }
@@ -19,6 +19,7 @@ namespace NzbDrone.Common.EnvironmentInfo
_logger = logger; _logger = logger;
IsWindowsService = hostLifetime is WindowsServiceLifetime; IsWindowsService = hostLifetime is WindowsServiceLifetime;
IsStarting = true;
// net6.0 will return Radarr.dll for entry assembly, we need the actual // net6.0 will return Radarr.dll for entry assembly, we need the actual
// executable name (Radarr on linux). On mono this will return the location of // executable name (Radarr on linux). On mono this will return the location of
@@ -82,6 +83,7 @@ namespace NzbDrone.Common.EnvironmentInfo
public bool IsWindowsService { get; private set; } public bool IsWindowsService { get; private set; }
public bool IsStarting { get; set; }
public bool IsExiting { get; set; } public bool IsExiting { get; set; }
public bool IsTray public bool IsTray
{ {
@@ -131,7 +131,7 @@ namespace NzbDrone.Common.Extensions
public static string WrapInQuotes(this string text) public static string WrapInQuotes(this string text)
{ {
if (!text.Contains(" ")) if (!text.Contains(' '))
{ {
return text; return text;
} }
@@ -217,7 +217,7 @@ namespace NzbDrone.Common.Extensions
public static string ToUrlHost(this string input) public static string ToUrlHost(this string input)
{ {
return input.Contains(":") ? $"[{input}]" : input; return input.Contains(':') ? $"[{input}]" : input;
} }
} }
} }
@@ -216,7 +216,7 @@ namespace NzbDrone.Common.Http.Dispatchers
} }
} }
private void AddContentHeader(HttpRequestMessage request, string header, string value) private static void AddContentHeader(HttpRequestMessage request, string header, string value)
{ {
var headers = request.Content?.Headers; var headers = request.Content?.Headers;
if (headers == null) if (headers == null)
+1 -1
View File
@@ -170,7 +170,7 @@ namespace NzbDrone.Common.Http
if (baseSlashIndex >= 0) if (baseSlashIndex >= 0)
{ {
return basePath.Substring(0, baseSlashIndex) + "/" + relativePath; return $"{basePath.AsSpan(0, baseSlashIndex)}/{relativePath}";
} }
return relativePath; return relativePath;
+4 -4
View File
@@ -5,8 +5,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DryIoc.dll" Version="5.3.0" /> <PackageReference Include="DryIoc.dll" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="NLog" Version="5.0.1" /> <PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.0.0" />
@@ -14,10 +14,10 @@
<PackageReference Include="Sentry" Version="3.23.1" /> <PackageReference Include="Sentry" Version="3.23.1" />
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" /> <PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <PackageReference Include="SharpZipLib" Version="1.3.3" />
<PackageReference Include="System.Text.Json" Version="6.0.5" /> <PackageReference Include="System.Text.Json" Version="6.0.7" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" /> <PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.115.5-18" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> <PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="6.0.0" />
+1 -1
View File
@@ -64,7 +64,7 @@ namespace NzbDrone.Common
var args = $"create {serviceName} " + var args = $"create {serviceName} " +
$"DisplayName= \"{serviceName}\" " + $"DisplayName= \"{serviceName}\" " +
$"binpath= \"{Process.GetCurrentProcess().MainModule.FileName}\" " + $"binpath= \"{Environment.ProcessPath}\" " +
"start= auto " + "start= auto " +
"depend= EventLog/Tcpip/http " + "depend= EventLog/Tcpip/http " +
"obj= \"NT AUTHORITY\\LocalService\""; "obj= \"NT AUTHORITY\\LocalService\"";
@@ -19,7 +19,7 @@ namespace NzbDrone.Common.TPL
private readonly int _maxDegreeOfParallelism; private readonly int _maxDegreeOfParallelism;
/// <summary>Whether the scheduler is currently processing work items.</summary> /// <summary>Whether the scheduler is currently processing work items.</summary>
private int _delegatesQueuedOrRunning = 0; private int _delegatesQueuedOrRunning;
/// <summary> /// <summary>
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
@@ -81,8 +81,9 @@ namespace NzbDrone.Core.Test.Blocklisting
Subject.DeleteForMovies(new List<int> { _movie1.Id }); Subject.DeleteForMovies(new List<int> { _movie1.Id });
var removedMovieBlocklists = Subject.BlocklistedByMovie(_movie1.Id); var blocklist = Subject.All();
var nonRemovedMovieBlocklists = Subject.BlocklistedByMovie(_movie2.Id); var removedMovieBlocklists = blocklist.Where(b => b.MovieId == _movie1.Id);
var nonRemovedMovieBlocklists = blocklist.Where(b => b.MovieId == _movie2.Id);
removedMovieBlocklists.Should().HaveCount(0); removedMovieBlocklists.Should().HaveCount(0);
nonRemovedMovieBlocklists.Should().HaveCount(1); nonRemovedMovieBlocklists.Should().HaveCount(1);
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@@ -9,7 +9,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.CustomFormats namespace NzbDrone.Core.Test.CustomFormats
{ {
[TestFixture] [TestFixture]
public class CustomFormatsFixture : CoreTest public class CustomFormatsTestHelpers : CoreTest
{ {
private static List<CustomFormat> _customFormats { get; set; } private static List<CustomFormat> _customFormats { get; set; }
@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.Datastore.Migration
[TestFixture] [TestFixture]
public class custom_formatsFixture : MigrationTest<add_custom_formats> public class custom_formatsFixture : MigrationTest<add_custom_formats>
{ {
public static Dictionary<int, int> QualityToDefinition = null; public static Dictionary<int, int> QualityToDefinition;
public void AddDefaultProfile(add_custom_formats m, string name, Quality cutoff, params Quality[] allowed) public void AddDefaultProfile(add_custom_formats m, string name, Quality cutoff, params Quality[] allowed)
{ {
@@ -46,14 +46,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
}; };
CustomFormatsFixture.GivenCustomFormats(_format1, _format2); CustomFormatsTestHelpers.GivenCustomFormats(_format1, _format2);
} }
[Test] [Test]
public void should_allow_if_format_score_greater_than_min() public void should_allow_if_format_score_greater_than_min()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_format_score_not_greater_than_min() public void should_deny_if_format_score_not_greater_than_min()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Console.WriteLine(_remoteMovie.CustomFormatScore); Console.WriteLine(_remoteMovie.CustomFormatScore);
@@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_format_score_not_greater_than_min_2() public void should_deny_if_format_score_not_greater_than_min_2()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
@@ -86,7 +86,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_all_format_is_defined_in_profile() public void should_allow_if_all_format_is_defined_in_profile()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 }; _remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_deny_if_no_format_was_parsed_and_min_score_positive() public void should_deny_if_no_format_was_parsed_and_min_score_positive()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { }; _remoteMovie.CustomFormats = new List<CustomFormat> { };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
@@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_no_format_was_parsed_min_score_is_zero() public void should_allow_if_no_format_was_parsed_min_score_is_zero()
{ {
_remoteMovie.CustomFormats = new List<CustomFormat> { }; _remoteMovie.CustomFormats = new List<CustomFormat> { };
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name); _remoteMovie.Movie.Profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_format1.Name, _format2.Name);
_remoteMovie.Movie.Profile.MinFormatScore = 0; _remoteMovie.Movie.Profile.MinFormatScore = 0;
_remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats); _remoteMovie.CustomFormatScore = _remoteMovie.Movie.Profile.CalculateCustomFormatScore(_remoteMovie.CustomFormats);
@@ -40,8 +40,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void GivenProfile(Profile profile) private void GivenProfile(Profile profile)
{ {
CustomFormatsFixture.GivenCustomFormats(); CustomFormatsTestHelpers.GivenCustomFormats();
profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(); profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems();
profile.MinFormatScore = 0; profile.MinFormatScore = 0;
_remoteMovie.Movie.Profile = profile; _remoteMovie.Movie.Profile = profile;
@@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
_customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 }; _customFormat = new CustomFormat("My Format", new ResolutionSpecification { Value = (int)Resolution.R1080p }) { Id = 1 };
CustomFormatsFixture.GivenCustomFormats(_customFormat); CustomFormatsTestHelpers.GivenCustomFormats(_customFormat);
} }
[Test] [Test]
@@ -157,7 +157,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Cutoff = Quality.HDTV720p.Id, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
MinFormatScore = 0, MinFormatScore = 0,
FormatItems = CustomFormatsFixture.GetSampleFormatItems("My Format"), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("My Format"),
UpgradeAllowed = true UpgradeAllowed = true
}); });
@@ -38,14 +38,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeHistory = Mocker.Resolve<HistorySpecification>(); _upgradeHistory = Mocker.Resolve<HistorySpecification>();
CustomFormatsFixture.GivenCustomFormats(); CustomFormatsTestHelpers.GivenCustomFormats();
_fakeMovie = Builder<Movie>.CreateNew() _fakeMovie = Builder<Movie>.CreateNew()
.With(c => c.Profile = new Profile .With(c => c.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems("None"),
MinFormatScore = 0, MinFormatScore = 0,
UpgradeAllowed = true UpgradeAllowed = true
}) })
@@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Returns(true); .Returns(true);
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>())) .Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>(), It.IsAny<Movie>()))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
} }
@@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -171,7 +171,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>())) .Setup(x => x.ParseCustomFormat(It.IsAny<MovieHistory>(), It.IsAny<Movie>()))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, MovieHistoryEventType.Grabbed); GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, MovieHistoryEventType.Grabbed);
@@ -186,7 +186,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.WEBDL1080p.Id, Cutoff = Quality.WEBDL1080p.Id,
FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -221,7 +221,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = Quality.WEBDL1080p.Id, Cutoff = Quality.WEBDL1080p.Id,
FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -41,17 +41,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void WithEnglishRelease() private void WithEnglishRelease()
{ {
_remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.English }; _remoteMovie.Languages = new List<Language> { Language.English };
} }
private void WithGermanRelease() private void WithGermanRelease()
{ {
_remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.German }; _remoteMovie.Languages = new List<Language> { Language.German };
} }
private void WithFrenchRelease() private void WithFrenchRelease()
{ {
_remoteMovie.ParsedMovieInfo.Languages = new List<Language> { Language.French }; _remoteMovie.Languages = new List<Language> { Language.French };
} }
[Test] [Test]
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 }; _customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 };
_customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 }; _customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 };
CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2); CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2);
Mocker.GetMock<IQualityDefinitionService>() Mocker.GetMock<IQualityDefinitionService>()
.Setup(s => s.Get(It.IsAny<Quality>())) .Setup(s => s.Get(It.IsAny<Quality>()))
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
remoteMovie.Movie = Builder<Movie>.CreateNew().With(m => m.Profile = new Profile remoteMovie.Movie = Builder<Movie>.CreateNew().With(m => m.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name),
MinFormatScore = 0 MinFormatScore = 0
}) })
.With(m => m.Title = "A Movie") .With(m => m.Title = "A Movie")
@@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2); CustomFormatsTestHelpers.GivenCustomFormats(_customFormat1, _customFormat2);
} }
private void GivenAutoDownloadPropers(ProperDownloadTypes type) private void GivenAutoDownloadPropers(ProperDownloadTypes type)
@@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var profile = new Profile var profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name),
MinFormatScore = 0 MinFormatScore = 0
}; };
@@ -4,9 +4,11 @@ using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFormats; using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles; using NzbDrone.Core.Profiles;
@@ -32,13 +34,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
CustomFormatsFixture.GivenCustomFormats(); CustomFormatsTestHelpers.GivenCustomFormats();
_movie = Builder<Movie>.CreateNew() _movie = Builder<Movie>.CreateNew()
.With(e => e.Profile = new Profile .With(e => e.Profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
MinFormatScore = 0, MinFormatScore = 0,
UpgradeAllowed = true UpgradeAllowed = true
}) })
@@ -58,7 +60,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build(); .Build();
Mocker.GetMock<ICustomFormatCalculationService>() Mocker.GetMock<ICustomFormatCalculationService>()
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedMovieInfo>(), _movie)) .Setup(x => x.ParseCustomFormat(It.IsAny<RemoteMovie>(), It.IsAny<long>()))
.Returns(new List<CustomFormat>()); .Returns(new List<CustomFormat>());
} }
@@ -204,5 +206,31 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
} }
[Test]
public void should_return_false_if_same_quality_non_proper_in_queue_and_download_propers_is_do_not_upgrade()
{
_remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(2));
_movie.Profile.Cutoff = _remoteMovie.ParsedMovieInfo.Quality.Quality.Id;
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotUpgrade);
var remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(r => r.Movie = _movie)
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo
{
Quality = new QualityModel(Quality.HDTV720p),
Languages = new List<Language> { Language.English }
})
.With(r => r.Release = _releaseInfo)
.With(r => r.CustomFormats = new List<CustomFormat>())
.Build();
GivenQueue(new List<RemoteMovie> { remoteMovie });
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
}
} }
} }
@@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Mocker.Resolve<UpgradableSpecification>(); Mocker.Resolve<UpgradableSpecification>();
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>(); _upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
CustomFormatsFixture.GivenCustomFormats(); CustomFormatsTestHelpers.GivenCustomFormats();
_firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; _firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now };
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.With(c => c.Profile = new Profile .With(c => c.Profile = new Profile
{ {
Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(), Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(),
FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(),
MinFormatScore = 0 MinFormatScore = 0
}) })
.With(e => e.MovieFile = _firstFile) .With(e => e.MovieFile = _firstFile)
@@ -0,0 +1,112 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.Aggregation.Aggregators;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download.Aggregation.Aggregators
{
[TestFixture]
public class AggregateLanguagesFixture : CoreTest<AggregateLanguages>
{
private RemoteMovie _remoteMovie;
private Movie _movie;
private string _simpleReleaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
[SetUp]
public void Setup()
{
_movie = Builder<Movie>.CreateNew()
.With(m => m.MovieMetadata = new MovieMetadata
{
Title = "Some Movie",
OriginalLanguage = Language.English
})
.Build();
_remoteMovie = Builder<RemoteMovie>.CreateNew()
.With(l => l.ParsedMovieInfo = null)
.With(l => l.Movie = _movie)
.Build();
}
private ParsedMovieInfo GetParsedMovieInfo(List<Language> languages, string releaseTitle, string releaseTokens = "")
{
return new ParsedMovieInfo
{
Languages = languages,
ReleaseTitle = releaseTitle,
SimpleReleaseTitle = releaseTokens
};
}
[Test]
public void should_return_existing_language_if_episode_title_does_not_have_language()
{
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Original }, _simpleReleaseTitle);
Subject.Aggregate(_remoteMovie).Languages.Should().Contain(_movie.MovieMetadata.Value.OriginalLanguage);
}
[Test]
public void should_return_parsed_language()
{
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.French }, _simpleReleaseTitle);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_remoteMovie.ParsedMovieInfo.Languages);
}
[Test]
public void should_exclude_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(_movie.MovieMetadata.Value.OriginalLanguage);
}
[Test]
public void should_remove_parsed_language_that_is_part_of_episode_title_when_release_tokens_contains_episode_title()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.French.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.French.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek, Language.French }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.French);
}
[Test]
public void should_not_exclude_language_that_is_part_of_episode_title_when_release_tokens_does_not_contain_episode_title()
{
var releaseTitle = "Series.Title.S01E01.xyz-RlsGroup";
var releaseTokens = ".xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek);
}
[Test]
public void should_use_reparse_language_after_determining_languages_that_are_in_episode_titles()
{
var releaseTitle = "Series.Title.S01E01.Jimmy.The.Greek.Greek.xyz-RlsGroup";
var releaseTokens = ".Jimmy.The.Greek.Greek.xyz-RlsGroup";
_remoteMovie.Movie.Title = "Jimmy The Greek";
_remoteMovie.ParsedMovieInfo = GetParsedMovieInfo(new List<Language> { Language.Greek }, releaseTitle, releaseTokens);
Subject.Aggregate(_remoteMovie).Languages.Should().Equal(Language.Greek);
}
}
}
@@ -80,7 +80,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.FreeboxDownloadTests
Mocker.GetMock<IHttpClient>() Mocker.GetMock<IHttpClient>()
.Setup(s => s.Get(It.IsAny<HttpRequest>())) .Setup(s => s.Get(It.IsAny<HttpRequest>()))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), new byte[0])); .Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), Array.Empty<byte>()));
} }
protected void GivenCategory() protected void GivenCategory()
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
@@ -275,7 +276,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
[TestCase(-1)] // Infinite/Unknown [TestCase(-1)] // Infinite/Unknown
[TestCase(-2)] // Magnet Downloading [TestCase(-2)] // Magnet Downloading
public void should_ignore_negative_eta(int eta) public void should_ignore_negative_eta(long eta)
{ {
_completed.Eta = eta; _completed.Eta = eta;
@@ -284,6 +285,26 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
item.RemainingTime.Should().NotHaveValue(); item.RemainingTime.Should().NotHaveValue();
} }
[TestCase(2147483648)] // 2038-01-19T03:14:08Z > int.MaxValue as unix timestamp can be either an int or a long
public void should_support_long_values_for_eta_in_seconds(long eta)
{
_downloading.Eta = eta;
PrepareClientToReturnDownloadingItem();
var item = Subject.GetItems().Single();
item.RemainingTime.Should().Be(TimeSpan.FromSeconds(eta));
}
[TestCase(2147483648000)] // works with milliseconds format too
public void should_support_long_values_for_eta_in_milliseconds(long eta)
{
_downloading.Eta = eta;
PrepareClientToReturnDownloadingItem();
var item = Subject.GetItems().Single();
item.RemainingTime.Should().Be(TimeSpan.FromMilliseconds(eta));
}
[Test] [Test]
public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_stopped() public void should_not_be_removable_and_should_not_allow_move_files_if_max_ratio_reached_and_not_stopped()
{ {
@@ -271,7 +271,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
[TestCase(-1)] // Infinite/Unknown [TestCase(-1)] // Infinite/Unknown
[TestCase(-2)] // Magnet Downloading [TestCase(-2)] // Magnet Downloading
public void should_ignore_negative_eta(int eta) public void should_ignore_negative_eta(long eta)
{ {
_completed.Eta = eta; _completed.Eta = eta;
@@ -54,6 +54,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
Id = id, Id = id,
Title = "Movie.Title.2020.720p-Radarr", Title = "Movie.Title.2020.720p-Radarr",
ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year }, ParsedMovieInfo = new ParsedMovieInfo { MovieTitles = new List<string> { title }, Year = year },
Release = Builder<ReleaseInfo>.CreateNew().Build(),
MovieId = _movie.Id MovieId = _movie.Id
}); });
} }
@@ -62,7 +62,7 @@ namespace NzbDrone.Core.Test.Extras.Others
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1); results.Count.Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
} }
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.Extras.Others
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1); results.Count.Should().Be(1);
} }
} }
} }
@@ -67,7 +67,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(0); results.Count.Should().Be(0);
} }
[Test] [Test]
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(1); results.Count.Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
} }
@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(expectedOutputs.Length); results.Count.Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++) for (int i = 0; i < expectedOutputs.Length; i++)
{ {
@@ -139,7 +139,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, files, true).ToList();
results.Count().Should().Be(expectedOutputs.Length); results.Count.Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++) for (int i = 0; i < expectedOutputs.Length; i++)
{ {
@@ -169,7 +169,7 @@ namespace NzbDrone.Core.Test.Extras.Subtitles
var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList(); var results = Subject.ImportFiles(_localMovie, _movieFile, new List<string> { subtitleFile }, true).ToList();
results.Count().Should().Be(1); results.Count.Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true); results[0].RelativePath.AsOsAgnostic().PathEquals(expectedOutputPath.AsOsAgnostic()).Should().Be(true);
@@ -62,13 +62,21 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
releaseInfo.Size.Should().Be(1183105773); releaseInfo.Size.Should().Be(1183105773);
} }
[Test] public void should_use_best_pagesize_reported_by_caps()
public void should_use_pagesize_reported_by_caps()
{ {
_caps.MaxPageSize = 30; _caps.MaxPageSize = 30;
_caps.DefaultPageSize = 25; _caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(25); Subject.PageSize.Should().Be(30);
}
[Test]
public void should_not_use_pagesize_over_100_even_if_reported_in_caps()
{
_caps.MaxPageSize = 250;
_caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(100);
} }
} }
} }
@@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using FluentAssertions; using FluentAssertions;
@@ -55,7 +56,7 @@ namespace NzbDrone.Core.Test.IndexerTests.PTPTests
first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000"); first.DownloadUrl.Should().Be("https://passthepopcorn.me/torrents.php?action=download&id=452135&authkey=00000000000000000000000000000000&torrent_pass=00000000000000000000000000000000");
first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=452135"); first.InfoUrl.Should().Be("https://passthepopcorn.me/torrents.php?id=148131&torrentid=452135");
// first.PublishDate.Should().Be(DateTime.Parse("2017-04-17T12:13:42+0000").ToUniversalTime()); stupid timezones first.PublishDate.Should().Be(DateTime.Parse("2016-10-18T23:40:59+0000").ToUniversalTime());
first.Size.Should().Be(2466170624L); first.Size.Should().Be(2466170624L);
first.InfoHash.Should().BeNullOrEmpty(); first.InfoHash.Should().BeNullOrEmpty();
first.MagnetUrl.Should().BeNullOrEmpty(); first.MagnetUrl.Should().BeNullOrEmpty();
@@ -135,12 +135,21 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
} }
[Test] [Test]
public void should_use_pagesize_reported_by_caps() public void should_use_best_pagesize_reported_by_caps()
{ {
_caps.MaxPageSize = 30; _caps.MaxPageSize = 30;
_caps.DefaultPageSize = 25; _caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(25); Subject.PageSize.Should().Be(30);
}
[Test]
public void should_not_use_pagesize_over_100_even_if_reported_in_caps()
{
_caps.MaxPageSize = 250;
_caps.DefaultPageSize = 25;
Subject.PageSize.Should().Be(100);
} }
[TestCase("http://localhost:9117/", "/api")] [TestCase("http://localhost:9117/", "/api")]
@@ -49,6 +49,14 @@ namespace NzbDrone.Core.Test.Languages
new object[] { 34, Language.Bengali }, new object[] { 34, Language.Bengali },
new object[] { 35, Language.Slovak }, new object[] { 35, Language.Slovak },
new object[] { 36, Language.Latvian }, new object[] { 36, Language.Latvian },
new object[] { 37, Language.SpanishLatino },
new object[] { 38, Language.Catalan },
new object[] { 39, Language.Croatian },
new object[] { 40, Language.Serbian },
new object[] { 41, Language.Bosnian },
new object[] { 42, Language.Estonian },
new object[] { 43, Language.Tamil },
new object[] { 44, Language.Indonesian }
}; };
public static object[] ToIntCases = public static object[] ToIntCases =
@@ -92,6 +100,14 @@ namespace NzbDrone.Core.Test.Languages
new object[] { Language.Bengali, 34 }, new object[] { Language.Bengali, 34 },
new object[] { Language.Slovak, 35 }, new object[] { Language.Slovak, 35 },
new object[] { Language.Latvian, 36 }, new object[] { Language.Latvian, 36 },
new object[] { Language.SpanishLatino, 37 },
new object[] { Language.Catalan, 38 },
new object[] { Language.Croatian, 39 },
new object[] { Language.Serbian, 40 },
new object[] { Language.Bosnian, 41 },
new object[] { Language.Estonian, 42 },
new object[] { Language.Tamil, 43 },
new object[] { Language.Indonesian, 44 }
}; };
[Test] [Test]
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaFileDeletionService
[TestFixture] [TestFixture]
public class DeleteMovieFileFixture : CoreTest<Core.MediaFiles.MediaFileDeletionService> public class DeleteMovieFileFixture : CoreTest<Core.MediaFiles.MediaFileDeletionService>
{ {
private static readonly string RootFolder = @"C:\Test\Movies"; private const string RootFolder = @"C:\Test\Movies";
private Movie _movie; private Movie _movie;
private MovieFile _movieFile; private MovieFile _movieFile;
@@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests
var profile = new Profile var profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
FormatItems = CustomFormatsFixture.GetDefaultFormatItems(), FormatItems = CustomFormatsTestHelpers.GetDefaultFormatItems(),
MinFormatScore = 0, MinFormatScore = 0,
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
Name = "TestProfile" Name = "TestProfile"
@@ -10,6 +10,8 @@ namespace NzbDrone.Core.Test.OrganizerTests
{ {
[TestCase("Mission: Impossible - no [HDTV-720p]", [TestCase("Mission: Impossible - no [HDTV-720p]",
"Mission Impossible - no [HDTV-720p]")] "Mission Impossible - no [HDTV-720p]")]
[TestCase(".45 (2006)", "45 (2006)")]
[TestCase(" The Movie Title ", "The Movie Title")]
public void CleanFileName(string name, string expectedName) public void CleanFileName(string name, string expectedName)
{ {
FileNameBuilder.CleanFileName(name).Should().Be(expectedName); FileNameBuilder.CleanFileName(name).Should().Be(expectedName);
@@ -562,19 +562,6 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
.Should().Be(string.Format("HDTV-720p{0}South{0}Park", separator)); .Should().Be(string.Format("HDTV-720p{0}South{0}Park", separator));
} }
[Test]
public void should_be_able_to_use_original_filename()
{
_movie.Title = "30 Rock";
_namingConfig.StandardMovieFormat = "{Movie Title} - {Original Filename}";
_movieFile.SceneName = "30.Rock.S01E01.xvid-LOL";
_movieFile.RelativePath = "30 Rock - S01E01 - Test";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("30 Rock - 30 Rock - S01E01 - Test");
}
[TestCase("en-US")] [TestCase("en-US")]
[TestCase("fr-FR")] [TestCase("fr-FR")]
[TestCase("az")] [TestCase("az")]
@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.CustomFormats;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class OriginalTitleFixture : CoreTest<FileNameBuilder>
{
private Movie _movie;
private MovieFile _movieFile;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_movie = Builder<Movie>
.CreateNew()
.With(s => s.Title = "My Movie")
.Build();
_movieFile = new MovieFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "RadarrTest" };
_namingConfig = NamingConfig.Default;
_namingConfig.RenameMovies = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
Mocker.GetMock<ICustomFormatService>()
.Setup(v => v.All())
.Returns(new List<CustomFormat>());
}
[Test]
public void should_not_recursively_include_current_filename()
{
_movieFile.RelativePath = "My Movie";
_namingConfig.StandardMovieFormat = "{Movie Title} {[Original Title]}";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("My Movie");
}
[Test]
public void should_include_original_title_if_not_current_file_name()
{
_movieFile.SceneName = "my.movie.2008";
_movieFile.RelativePath = "My Movie";
_namingConfig.StandardMovieFormat = "{Movie Title} {[Original Title]}";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("My Movie [my.movie.2008]");
}
[Test]
public void should_include_current_filename_if_not_renaming_files()
{
_movieFile.SceneName = "my.movie.2008";
_namingConfig.RenameMovies = false;
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("my.movie.2008");
}
[Test]
public void should_include_current_filename_if_not_including_multiple_naming_tokens()
{
_movieFile.RelativePath = "My Movie";
_namingConfig.StandardMovieFormat = "{Original Title}";
Subject.BuildFileName(_movie, _movieFile)
.Should().Be("My Movie");
}
}
}
@@ -22,7 +22,6 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("enus")] [TestCase("enus")]
[TestCase("enusa")] [TestCase("enusa")]
[TestCase("wo")] [TestCase("wo")]
[TestCase("ca-IT")]
[TestCase("fr-CA")] [TestCase("fr-CA")]
public void unknown_or_invalid_code_should_return_null(string isoCode) public void unknown_or_invalid_code_should_return_null(string isoCode)
{ {
@@ -1,3 +1,4 @@
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
@@ -146,6 +147,7 @@ namespace NzbDrone.Core.Test.ParserTests
} }
[TestCase("Movie.Title.1994.Romanian.1080p.XviD-LOL")] [TestCase("Movie.Title.1994.Romanian.1080p.XviD-LOL")]
[TestCase("Movie.Title.1994.1080p.XviD.RoDubbed-LOL")]
public void should_parse_language_romanian(string postTitle) public void should_parse_language_romanian(string postTitle)
{ {
var result = Parser.Parser.ParseMovieTitle(postTitle, true); var result = Parser.Parser.ParseMovieTitle(postTitle, true);
@@ -380,6 +382,26 @@ namespace NzbDrone.Core.Test.ParserTests
result.Languages.Should().BeEquivalentTo(Language.Latvian); result.Languages.Should().BeEquivalentTo(Language.Latvian);
} }
[TestCase("Movie.Title.2019.720p_Eng-Spa(Latino)_MovieClubMx")]
[TestCase("Movie.Title.1.WEB-DL.720p.Complete.Latino.YG")]
[TestCase("Movie.Title.1080p.WEB.H264.Latino.YG")]
[TestCase("Movie Title latino")]
[TestCase("Movie Title (Temporada 11 Completa) Audio Dual Ingles/Latino 1920x1080")]
[TestCase("Movie title 7x4 audio latino")]
public void should_parse_language_spanish_latino(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);
result.First().Id.Should().Be(Language.SpanishLatino.Id);
}
[TestCase("Movie.Title.1994.Catalan.1080p.XviD-LOL")]
public void should_parse_language_catalan(string postTitle)
{
var result = Parser.Parser.ParseMovieTitle(postTitle, true);
result.Languages.Should().BeEquivalentTo(Language.Catalan);
}
[TestCase("Movie.Title.en.sub")] [TestCase("Movie.Title.en.sub")]
[TestCase("Movie Title.eng.sub")] [TestCase("Movie Title.eng.sub")]
[TestCase("Movie.Title.eng.forced.sub")] [TestCase("Movie.Title.eng.forced.sub")]
@@ -215,6 +215,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Movie Name FRENCH BluRay 720p 2016 kjhlj", 2016)] [TestCase("Movie Name FRENCH BluRay 720p 2016 kjhlj", 2016)]
[TestCase("Der.Movie.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)] [TestCase("Der.Movie.German.Bluray.FuckYou.Pso.Why.cant.you.follow.scene.rules.1998", 1998)]
[TestCase("Movie Name (1897) [DVD].mp4", 1897)] [TestCase("Movie Name (1897) [DVD].mp4", 1897)]
[TestCase("World Movie Z Movie [2023]", 2023)]
public void should_parse_movie_year(string postTitle, int year) public void should_parse_movie_year(string postTitle, int year)
{ {
Parser.Parser.ParseMovieTitle(postTitle).Year.Should().Be(year); Parser.Parser.ParseMovieTitle(postTitle).Year.Should().Be(year);
@@ -254,6 +255,18 @@ namespace NzbDrone.Core.Test.ParserTests
parsed.Languages.Should().Contain(Language.German); parsed.Languages.Should().Contain(Language.German);
} }
[TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "KORSUB")]
[TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")]
[TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2000.1080p.BlueRay.x264.DTS.RoSubbed-playHD", null)]
[TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)]
[TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)]
public void should_parse_hardcoded_subs(string postTitle, string sub)
{
Parser.Parser.ParseMovieTitle(postTitle).HardcodedSubs.Should().Be(sub);
}
[TestCase("That Italian Movie 2008 [tt1234567] 720p BluRay X264", "tt1234567")] [TestCase("That Italian Movie 2008 [tt1234567] 720p BluRay X264", "tt1234567")]
[TestCase("That Italian Movie 2008 [tt12345678] 720p BluRay X264", "tt12345678")] [TestCase("That Italian Movie 2008 [tt12345678] 720p BluRay X264", "tt12345678")]
public void should_parse_imdb_in_title(string postTitle, string imdb) public void should_parse_imdb_in_title(string postTitle, string imdb)
@@ -1,28 +0,0 @@
using System.Collections.Generic;
using NUnit.Framework;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public abstract class AugmentMovieInfoFixture<TAugmenter> : CoreTest<TAugmenter>
where TAugmenter : class, IAugmentParsedMovieInfo
{
protected ParsedMovieInfo MovieInfo;
[SetUp]
public virtual void Setup()
{
MovieInfo = new ParsedMovieInfo
{
MovieTitles = new List<string> { "A Movie" },
Year = 1998,
SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p",
Quality = new QualityModel(Quality.Bluray1080p)
};
}
}
}
@@ -1,23 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public class AugmentWithFileSizeFixture : AugmentMovieInfoFixture<AugmentWithFileSize>
{
[Test]
public void should_add_file_size()
{
var localMovie = new LocalMovie
{
Size = 1500
};
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, localMovie);
movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1500);
}
}
}
@@ -1,109 +0,0 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.History;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Rarbg;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public class AugmentWithHistoryFixture : AugmentMovieInfoFixture<AugmentWithHistory>
{
private AugmentWithHistory _customSubject { get; set; }
[SetUp]
public override void Setup()
{
base.Setup();
// Add multi indexer
GivenIndexerSettings(new RarbgSettings
{
MultiLanguages = new List<int>
{
(int)Language.English,
(int)Language.French,
}
});
}
protected new AugmentWithHistory Subject
{
get
{
if (_customSubject == null)
{
_customSubject = new AugmentWithHistory(new List<Lazy<IAugmentParsedMovieInfo>> { new (Mocker.Resolve<AugmentWithReleaseInfo>()) });
}
return _customSubject;
}
}
private void GivenIndexerSettings(IIndexerSettings indexerSettings)
{
Mocker.GetMock<IIndexerFactory>().Setup(f => f.Get(It.IsAny<int>())).Returns(new IndexerDefinition
{
Settings = indexerSettings
});
}
private MovieHistory HistoryWithData(params string[] data)
{
var dict = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
for (var i = 0; i < data.Length; i += 2)
{
dict.Add(data[i], data[i + 1]);
}
return new MovieHistory
{
Data = dict,
EventType = MovieHistoryEventType.Grabbed
};
}
[Test]
public void should_add_indexer_flags()
{
var history = HistoryWithData("IndexerFlags", (IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden).ToString());
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history);
movieInfo.ExtraInfo["IndexerFlags"].Should().BeEquivalentTo(IndexerFlags.PTP_Golden | IndexerFlags.PTP_Approved);
}
[Test]
public void should_add_size()
{
var history = HistoryWithData("Size", 9663676416.ToString());
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history);
movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(9663676416);
}
[Test]
public void should_use_settings_languages_when_necessary()
{
var history = HistoryWithData("IndexerId", 1.ToString());
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, history);
movieInfo.Languages.Should().BeEquivalentTo();
MovieInfo.SimpleReleaseTitle = "A Movie 1998 Bluray 1080p MULTI";
var multiInfo = Subject.AugmentMovieInfo(MovieInfo, history);
multiInfo.Languages.Should().BeEquivalentTo(Language.English, Language.French);
}
[Test]
public void should_not_use_settings_languages()
{
var unknownIndexer = HistoryWithData();
var unknownIndexerInfo = Subject.AugmentMovieInfo(MovieInfo, unknownIndexer);
unknownIndexerInfo.Languages.Should().BeEquivalentTo();
}
}
}
@@ -1,90 +0,0 @@
// using FluentAssertions;
// using NUnit.Framework;
// using NzbDrone.Core.CustomFormats;
// using NzbDrone.Core.MediaFiles.MediaInfo;
// using NzbDrone.Core.Parser;
// using NzbDrone.Core.Parser.Augmenters;
// using NzbDrone.Core.Qualities;
// namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
// {
// [TestFixture]
// public class AugmentWithMediaInfoFixture : AugmentMovieInfoFixture<AugmentWithMediaInfo>
// {
// [TestCase(Resolution.R720p, Source.BLURAY, Resolution.R1080p)]
// [TestCase(Resolution.R1080p, Source.TV, Resolution.R720p)]
// public void should_correct_resolution(Resolution resolution, Source source, Resolution realResolution)
// {
// var quality = new QualityModel
// {
// Source = source,
// Resolution = resolution,
// };
// MovieInfo.Quality = quality;
// var realWidth = 480;
// switch (realResolution)
// {
// case Resolution.R720p:
// realWidth = 1280;
// break;
// case Resolution.R1080p:
// realWidth = 1920;
// break;
// case Resolution.R2160p:
// realWidth = 2160;
// break;
// }
// var mediaInfo = new MediaInfoModel
// {
// Width = realWidth
// };
// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
// movieInfo.Quality.Resolution.Should().BeEquivalentTo(realResolution);
// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.MediaInfo);
// }
// [TestCase(Resolution.R720P, Source.BLURAY, Resolution.R1080P, Modifier.BRDISK)]
// [TestCase(Resolution.R1080P, Source.BLURAY, Resolution.R720P, Modifier.REMUX)]
// [TestCase(Resolution.R480P, Source.BLURAY, Resolution.R720P)]
// [TestCase(Resolution.R720P, Source.DVD, Resolution.R480P)]
// public void should_not_correct_resolution(Resolution resolution, Source source, Resolution realResolution, Modifier modifier = Modifier.NONE)
// {
// var quality = new QualityModel
// {
// Source = source,
// Resolution = resolution,
// Modifier = modifier,
// };
// MovieInfo.Quality = quality;
// var realWidth = 480;
// switch (realResolution)
// {
// case Resolution.R720P:
// realWidth = 1280;
// break;
// case Resolution.R1080P:
// realWidth = 1920;
// break;
// case Resolution.R2160P:
// realWidth = 2160;
// break;
// }
// var mediaInfo = new MediaInfoModel
// {
// Width = realWidth
// };
// var movieInfo = Subject.AugmentMovieInfo(MovieInfo, mediaInfo);
// movieInfo.Quality.Resolution.Should().BeEquivalentTo(resolution);
// movieInfo.Quality.QualityDetectionSource.Should().BeEquivalentTo(QualityDetectionSource.Name);
// }
// }
// }
@@ -1,29 +0,0 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public class AugmentWithOriginalLanguageFixture : AugmentMovieInfoFixture<AugmentWithOriginalLanguage>
{
[Test]
public void should_add_movie_original_language()
{
var releaseInfo = new ParsedMovieInfo();
var movie = new Movies.Movie
{
MovieMetadata = new Movies.MovieMetadata
{
OriginalLanguage = Language.English
}
};
var result = Subject.AugmentMovieInfo(releaseInfo, movie);
result.ExtraInfo.Should().ContainKey("OriginalLanguage");
result.ExtraInfo["OriginalLanguage"].Should().Be(Language.English);
}
}
}
@@ -1,77 +0,0 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public class AugmentWithParsedMovieInfoFixture : AugmentMovieInfoFixture<AugmentWithParsedMovieInfo>
{
[Test]
public void should_add_edition_if_null()
{
var folderInfo = new ParsedMovieInfo
{
Edition = "Directors Cut"
};
var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
result.Edition.Should().Be(folderInfo.Edition);
}
[Test]
public void should_preferr_longer_edition()
{
var folderInfo = new ParsedMovieInfo
{
Edition = "Super duper cut"
};
MovieInfo.Edition = "Rogue";
var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
result.Edition.Should().Be(folderInfo.Edition);
MovieInfo.Edition = "Super duper awesome cut";
result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
result.Edition.Should().Be(MovieInfo.Edition);
}
[Test]
public void should_combine_languages()
{
var folderInfo = new ParsedMovieInfo
{
Languages = new List<Language> { Language.French }
};
MovieInfo.Languages = new List<Language> { Language.English };
var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
result.Languages.Should().BeEquivalentTo(Language.English, Language.French);
}
[Test]
public void should_use_folder_release_group()
{
var folderInfo = new ParsedMovieInfo
{
ReleaseGroup = "AwesomeGroup"
};
MovieInfo.ReleaseGroup = "";
var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
result.ReleaseGroup.Should().BeEquivalentTo(folderInfo.ReleaseGroup);
}
}
}
@@ -1,82 +0,0 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Rarbg;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Parser.Augmenters;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
{
[TestFixture]
public class AugmentWithReleaseInfoFixture : AugmentMovieInfoFixture<AugmentWithReleaseInfo>
{
private IndexerDefinition _indexerDefinition;
private ReleaseInfo ReleaseInfoWithLanguages(params Language[] languages)
{
_indexerDefinition = new IndexerDefinition
{
Settings = new RarbgSettings { MultiLanguages = languages.ToList().Select(l => (int)l) }
};
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(1))
.Returns(_indexerDefinition);
return new ReleaseInfo
{
IndexerId = 1
};
}
[Test]
public void should_add_language_from_indexer()
{
var releaseInfo = ReleaseInfoWithLanguages(Language.English, Language.French);
MovieInfo.SimpleReleaseTitle = "A Movie Title 1998 Bluray 1080p MULTI";
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo);
movieInfo.Languages.Count.Should().Be(2);
movieInfo.Languages.Should().BeEquivalentTo(Language.English, Language.French);
}
[Test]
public void should_add_size_info()
{
var releaseInfo = new ReleaseInfo
{
Size = 1500
};
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo);
movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1500);
}
[Test]
public void should_not_add_size_when_already_present()
{
var releaseInfo = new ReleaseInfo
{
Size = 1500
};
MovieInfo.ExtraInfo["Size"] = 1600;
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo);
movieInfo.ExtraInfo["Size"].Should().BeEquivalentTo(1600);
}
[Test]
public void should_add_indexer_flags()
{
var releaseInfo = new ReleaseInfo
{
IndexerFlags = IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden
};
var movieInfo = Subject.AugmentMovieInfo(MovieInfo, releaseInfo);
movieInfo.ExtraInfo["IndexerFlags"].Should().BeEquivalentTo(IndexerFlags.PTP_Approved | IndexerFlags.PTP_Golden);
}
}
}
@@ -194,20 +194,5 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests
Subject.Map(_umlautInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie); Subject.Map(_umlautInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie);
Subject.Map(_umlautAltInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie); Subject.Map(_umlautAltInfo, "", _movieSearchCriteria).Movie.Should().Be(_movieSearchCriteria.Movie);
} }
[Test]
public void should_convert_original()
{
Subject.Map(_multiLanguageInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.English);
Subject.Map(_multiLanguageInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.French);
}
[Test]
public void should_remove_original_as_already_exists()
{
Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.English);
Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().Contain(Language.French);
Subject.Map(_multiLanguageWithOriginalInfo, "", _movieSearchCriteria).RemoteMovie.ParsedMovieInfo.Languages.Should().NotContain(Language.Original);
}
} }
} }
@@ -452,18 +452,6 @@ namespace NzbDrone.Core.Test.ParserTests
result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name); result.ResolutionDetectionSource.Should().Be(QualityDetectionSource.Name);
} }
[TestCase("Movie.Title.2016.1080p.KORSUB.WEBRip.x264.AAC2.0-RADARR", "KORSUB")]
[TestCase("Movie.Title.2016.1080p.KORSUBS.WEBRip.x264.AAC2.0-RADARR", "KORSUBS")]
[TestCase("Movie Title 2017 HC 720p HDRiP DD5 1 x264-LEGi0N", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2017.720p.SUBBED.HDRip.V2.XViD-26k.avi", "Generic Hardcoded Subs")]
[TestCase("Movie.Title.2000.1080p.BlueRay.x264.DTS.RoSubbed-playHD", null)]
[TestCase("Movie Title! 2018 [Web][MKV][h264][480p][AAC 2.0][Softsubs]", null)]
[TestCase("Movie Title! 2019 [HorribleSubs][Web][MKV][h264][848x480][AAC 2.0][Softsubs(HorribleSubs)]", null)]
public void should_parse_hardcoded_subs(string postTitle, string sub)
{
QualityParser.ParseQuality(postTitle).HardcodedSubs.Should().Be(sub);
}
[TestCase("Movie Title 2018 REPACK 720p x264 aAF", true)] [TestCase("Movie Title 2018 REPACK 720p x264 aAF", true)]
[TestCase("Movie.Title.2018.REPACK.720p.x264-aAF", true)] [TestCase("Movie.Title.2018.REPACK.720p.x264-aAF", true)]
[TestCase("Movie.Title.2018.PROPER.720p.x264-aAF", false)] [TestCase("Movie.Title.2018.PROPER.720p.x264-aAF", false)]
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.Profiles
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
MinFormatScore = 0, MinFormatScore = 0,
FormatItems = CustomFormatsFixture.GetDefaultFormatItems(), FormatItems = CustomFormatsTestHelpers.GetDefaultFormatItems(),
Cutoff = Quality.Bluray1080p.Id, Cutoff = Quality.Bluray1080p.Id,
Name = "TestProfile" Name = "TestProfile"
}; };
@@ -170,9 +170,9 @@ namespace NzbDrone.Core.Test.Profiles
var customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 }; var customFormat1 = new CustomFormat("My Format 1", new LanguageSpecification { Value = (int)Language.English }) { Id = 1 };
var customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 }; var customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 };
CustomFormatsFixture.GivenCustomFormats(customFormat1, customFormat2); CustomFormatsTestHelpers.GivenCustomFormats(customFormat1, customFormat2);
profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(customFormat2.Name); profile.FormatItems = CustomFormatsTestHelpers.GetSampleFormatItems(customFormat2.Name);
Mocker.GetMock<IProfileRepository>() Mocker.GetMock<IProfileRepository>()
.Setup(s => s.Get(It.IsAny<int>())) .Setup(s => s.Get(It.IsAny<int>()))
@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Movies; using NzbDrone.Core.Movies;
@@ -32,7 +33,14 @@ namespace NzbDrone.Core.Blocklisting
public List<Blocklist> BlocklistedByMovie(int movieId) public List<Blocklist> BlocklistedByMovie(int movieId)
{ {
return Query(x => x.MovieId == movieId); var builder = Builder().Join<Blocklist, Movie>((h, a) => h.MovieId == a.Id)
.Where<Blocklist>(h => h.MovieId == movieId);
return _database.QueryJoined<Blocklist, Movie>(builder, (blocklist, movie) =>
{
blocklist.Movie = movie;
return blocklist;
}).OrderByDescending(h => h.Date).ToList();
} }
public void DeleteForMovies(List<int> movieIds) public void DeleteForMovies(List<int> movieIds)
@@ -26,6 +26,7 @@ namespace NzbDrone.Core.Configuration
XDocument LoadConfigFile(); XDocument LoadConfigFile();
Dictionary<string, object> GetConfigDictionary(); Dictionary<string, object> GetConfigDictionary();
void SaveConfigDictionary(Dictionary<string, object> configValues); void SaveConfigDictionary(Dictionary<string, object> configValues);
void EnsureDefaultConfigFile();
string BindAddress { get; } string BindAddress { get; }
int Port { get; } int Port { get; }
@@ -253,7 +254,7 @@ namespace NzbDrone.Core.Configuration
public T GetValueEnum<T>(string key, T defaultValue, bool persist = true) public T GetValueEnum<T>(string key, T defaultValue, bool persist = true)
{ {
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist); return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue, persist), true);
} }
public string GetValue(string key, object defaultValue, bool persist = true) public string GetValue(string key, object defaultValue, bool persist = true)
@@ -312,7 +313,7 @@ namespace NzbDrone.Core.Configuration
SetValue(key, value.ToString().ToLower()); SetValue(key, value.ToString().ToLower());
} }
private void EnsureDefaultConfigFile() public void EnsureDefaultConfigFile()
{ {
if (!File.Exists(_configFile)) if (!File.Exists(_configFile))
{ {
@@ -14,28 +14,134 @@ namespace NzbDrone.Core.CustomFormats
{ {
public interface ICustomFormatCalculationService public interface ICustomFormatCalculationService
{ {
List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo, Movie movie); List<CustomFormat> ParseCustomFormat(RemoteMovie remoteMovie, long size);
List<CustomFormat> ParseCustomFormat(MovieFile movieFile, Movie movie);
List<CustomFormat> ParseCustomFormat(MovieFile movieFile); List<CustomFormat> ParseCustomFormat(MovieFile movieFile);
List<CustomFormat> ParseCustomFormat(Blocklist blocklist); List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Movie movie);
List<CustomFormat> ParseCustomFormat(MovieHistory history); List<CustomFormat> ParseCustomFormat(MovieHistory history, Movie movie);
List<CustomFormat> ParseCustomFormat(LocalMovie localMovie);
} }
public class CustomFormatCalculationService : ICustomFormatCalculationService public class CustomFormatCalculationService : ICustomFormatCalculationService
{ {
private readonly ICustomFormatService _formatService; private readonly ICustomFormatService _formatService;
private readonly IParsingService _parsingService;
private readonly IMovieService _movieService;
public CustomFormatCalculationService(ICustomFormatService formatService, public CustomFormatCalculationService(ICustomFormatService formatService)
IParsingService parsingService,
IMovieService movieService)
{ {
_formatService = formatService; _formatService = formatService;
_parsingService = parsingService;
_movieService = movieService;
} }
public static List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo, List<CustomFormat> allCustomFormats) public List<CustomFormat> ParseCustomFormat(RemoteMovie remoteMovie, long size)
{
var input = new CustomFormatInput
{
MovieInfo = remoteMovie.ParsedMovieInfo,
Movie = remoteMovie.Movie,
Size = size,
Languages = remoteMovie.Languages,
IndexerFlags = remoteMovie.Release?.IndexerFlags ?? 0
};
return ParseCustomFormat(input);
}
public List<CustomFormat> ParseCustomFormat(MovieFile movieFile, Movie movie)
{
return ParseCustomFormat(movieFile, movie, _formatService.All());
}
public List<CustomFormat> ParseCustomFormat(MovieFile movieFile)
{
return ParseCustomFormat(movieFile, movieFile.Movie, _formatService.All());
}
public List<CustomFormat> ParseCustomFormat(Blocklist blocklist, Movie movie)
{
var parsed = Parser.Parser.ParseMovieTitle(blocklist.SourceTitle);
var movieInfo = new ParsedMovieInfo
{
MovieTitles = new List<string>() { movie.Title },
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blocklist.SourceTitle.SimplifyReleaseTitle(),
ReleaseTitle = parsed?.ReleaseTitle ?? blocklist.SourceTitle,
Edition = parsed?.Edition,
Quality = blocklist.Quality,
Languages = blocklist.Languages,
ReleaseGroup = parsed?.ReleaseGroup
};
var input = new CustomFormatInput
{
MovieInfo = movieInfo,
Movie = movie,
Size = blocklist.Size ?? 0,
IndexerFlags = blocklist.IndexerFlags,
Languages = blocklist.Languages
};
return ParseCustomFormat(input);
}
public List<CustomFormat> ParseCustomFormat(MovieHistory history, Movie movie)
{
var parsed = Parser.Parser.ParseMovieTitle(history.SourceTitle);
long.TryParse(history.Data.GetValueOrDefault("size"), out var size);
Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags);
var movieInfo = new ParsedMovieInfo
{
MovieTitles = new List<string>() { movie.Title },
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(),
ReleaseTitle = parsed?.ReleaseTitle ?? history.SourceTitle,
Edition = parsed?.Edition,
Quality = history.Quality,
Languages = history.Languages,
ReleaseGroup = parsed?.ReleaseGroup,
};
var input = new CustomFormatInput
{
MovieInfo = movieInfo,
Movie = movie,
Size = size,
IndexerFlags = flags,
Languages = history.Languages
};
return ParseCustomFormat(input);
}
public List<CustomFormat> ParseCustomFormat(LocalMovie localMovie)
{
var episodeInfo = new ParsedMovieInfo
{
MovieTitles = new List<string>() { localMovie.Movie.Title },
SimpleReleaseTitle = localMovie.SceneName?.SimplifyReleaseTitle(),
ReleaseTitle = localMovie.SceneName,
Quality = localMovie.Quality,
Edition = localMovie.Edition,
Languages = localMovie.Languages,
ReleaseGroup = localMovie.ReleaseGroup
};
var input = new CustomFormatInput
{
MovieInfo = episodeInfo,
Movie = localMovie.Movie,
Size = localMovie.Size,
Languages = localMovie.Languages
};
return ParseCustomFormat(input);
}
private List<CustomFormat> ParseCustomFormat(CustomFormatInput input)
{
return ParseCustomFormat(input, _formatService.All());
}
private static List<CustomFormat> ParseCustomFormat(CustomFormatInput input, List<CustomFormat> allCustomFormats)
{ {
var matches = new List<CustomFormat>(); var matches = new List<CustomFormat>();
@@ -45,7 +151,7 @@ namespace NzbDrone.Core.CustomFormats
.GroupBy(t => t.GetType()) .GroupBy(t => t.GetType())
.Select(g => new SpecificationMatchesGroup .Select(g => new SpecificationMatchesGroup
{ {
Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(movieInfo)) Matches = g.ToDictionary(t => t, t => t.IsSatisfiedBy(input))
}) })
.ToList(); .ToList();
@@ -58,7 +164,7 @@ namespace NzbDrone.Core.CustomFormats
return matches; return matches;
} }
public static List<CustomFormat> ParseCustomFormat(MovieFile movieFile, List<CustomFormat> allCustomFormats) private static List<CustomFormat> ParseCustomFormat(MovieFile movieFile, Movie movie, List<CustomFormat> allCustomFormats)
{ {
var sceneName = string.Empty; var sceneName = string.Empty;
if (movieFile.SceneName.IsNotNullOrWhiteSpace()) if (movieFile.SceneName.IsNotNullOrWhiteSpace())
@@ -74,90 +180,29 @@ namespace NzbDrone.Core.CustomFormats
sceneName = Path.GetFileName(movieFile.RelativePath); sceneName = Path.GetFileName(movieFile.RelativePath);
} }
var info = new ParsedMovieInfo var movieInfo = new ParsedMovieInfo
{ {
MovieTitles = new List<string>() { movieFile.Movie.MovieMetadata.Value.Title }, MovieTitles = new List<string>() { movie.Title },
SimpleReleaseTitle = sceneName.SimplifyReleaseTitle(), SimpleReleaseTitle = sceneName.SimplifyReleaseTitle(),
Quality = movieFile.Quality, Quality = movieFile.Quality,
Languages = movieFile.Languages, Languages = movieFile.Languages,
ReleaseGroup = movieFile.ReleaseGroup, ReleaseGroup = movieFile.ReleaseGroup,
Edition = movieFile.Edition, Edition = movieFile.Edition,
Year = movieFile.Movie.MovieMetadata.Value.Year, Year = movieFile.Movie.MovieMetadata.Value.Year,
ImdbId = movieFile.Movie.MovieMetadata.Value.ImdbId, ImdbId = movieFile.Movie.MovieMetadata.Value.ImdbId
ExtraInfo = new Dictionary<string, object>
{
{ "IndexerFlags", movieFile.IndexerFlags },
{ "Size", movieFile.Size },
{ "Filename", Path.GetFileName(movieFile.RelativePath) },
{ "OriginalLanguage", movieFile.Movie.MovieMetadata.Value.OriginalLanguage }
}
}; };
return ParseCustomFormat(info, allCustomFormats); var input = new CustomFormatInput
}
public List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo, Movie movie)
{
movieInfo = _parsingService.EnhanceMovieInfo(movieInfo, new List<object> { movie }) ?? movieInfo;
return ParseCustomFormat(movieInfo, _formatService.All());
}
public List<CustomFormat> ParseCustomFormat(MovieFile movieFile)
{
return ParseCustomFormat(movieFile, _formatService.All());
}
public List<CustomFormat> ParseCustomFormat(Blocklist blocklist)
{
var movie = _movieService.GetMovie(blocklist.MovieId);
var parsed = _parsingService.ParseMovieInfo(blocklist.SourceTitle, null);
var info = new ParsedMovieInfo
{ {
MovieTitles = new List<string>() { movie.MovieMetadata.Value.Title }, MovieInfo = movieInfo,
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blocklist.SourceTitle.SimplifyReleaseTitle(), Movie = movie,
Quality = blocklist.Quality, Size = movieFile.Size,
Languages = blocklist.Languages, IndexerFlags = movieFile.IndexerFlags,
ReleaseGroup = parsed?.ReleaseGroup, Languages = movieFile.Languages,
Edition = parsed?.Edition, Filename = Path.GetFileName(movieFile.RelativePath)
Year = movie.MovieMetadata.Value.Year,
ImdbId = movie.MovieMetadata.Value.ImdbId,
ExtraInfo = new Dictionary<string, object>
{
{ "IndexerFlags", blocklist.IndexerFlags },
{ "Size", blocklist.Size }
}
}; };
return ParseCustomFormat(info, movie); return ParseCustomFormat(input, allCustomFormats);
}
public List<CustomFormat> ParseCustomFormat(MovieHistory history)
{
var movie = _movieService.GetMovie(history.MovieId);
var parsed = _parsingService.ParseMovieInfo(history.SourceTitle, null);
Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags);
long.TryParse(history.Data.GetValueOrDefault("size"), out var size);
var info = new ParsedMovieInfo
{
MovieTitles = new List<string>() { movie.MovieMetadata.Value.Title },
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(),
Quality = history.Quality,
Languages = history.Languages,
ReleaseGroup = parsed?.ReleaseGroup,
Edition = parsed?.Edition,
Year = movie.MovieMetadata.Value.Year,
ImdbId = movie.MovieMetadata.Value.ImdbId,
ExtraInfo = new Dictionary<string, object>
{
{ "IndexerFlags", flags },
{ "Size", size }
}
};
return ParseCustomFormat(info, movie);
} }
} }
} }
@@ -0,0 +1,22 @@
using System.Collections.Generic;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Movies;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.CustomFormats
{
public class CustomFormatInput
{
public ParsedMovieInfo MovieInfo { get; set; }
public Movie Movie { get; set; }
public long Size { get; set; }
public IndexerFlags IndexerFlags { get; set; }
public List<Language> Languages { get; set; }
public string Filename { get; set; }
public CustomFormatInput()
{
Languages = new List<Language>();
}
}
}
@@ -21,9 +21,9 @@ namespace NzbDrone.Core.CustomFormats
public abstract NzbDroneValidationResult Validate(); public abstract NzbDroneValidationResult Validate();
public bool IsSatisfiedBy(ParsedMovieInfo movieInfo) public bool IsSatisfiedBy(CustomFormatInput input)
{ {
var match = IsSatisfiedByWithoutNegate(movieInfo); var match = IsSatisfiedByWithoutNegate(input);
if (Negate) if (Negate)
{ {
match = !match; match = !match;
@@ -32,6 +32,6 @@ namespace NzbDrone.Core.CustomFormats
return match; return match;
} }
protected abstract bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo); protected abstract bool IsSatisfiedByWithoutNegate(CustomFormatInput input);
} }
} }
@@ -1,5 +1,3 @@
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.CustomFormats namespace NzbDrone.Core.CustomFormats
{ {
public class EditionSpecification : RegexSpecificationBase public class EditionSpecification : RegexSpecificationBase
@@ -8,9 +6,9 @@ namespace NzbDrone.Core.CustomFormats
public override string ImplementationName => "Edition"; public override string ImplementationName => "Edition";
public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2";
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
return MatchString(movieInfo.Edition); return MatchString(input.MovieInfo.Edition);
} }
} }
} }
@@ -15,6 +15,6 @@ namespace NzbDrone.Core.CustomFormats
NzbDroneValidationResult Validate(); NzbDroneValidationResult Validate();
ICustomFormatSpecification Clone(); ICustomFormatSpecification Clone();
bool IsSatisfiedBy(ParsedMovieInfo movieInfo); bool IsSatisfiedBy(CustomFormatInput input);
} }
} }
@@ -32,10 +32,9 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Flag", Type = FieldType.Select, SelectOptions = typeof(IndexerFlags))] [FieldDefinition(1, Label = "Flag", Type = FieldType.Select, SelectOptions = typeof(IndexerFlags))]
public int Value { get; set; } public int Value { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
var flags = movieInfo?.ExtraInfo?.GetValueOrDefault("IndexerFlags") as IndexerFlags?; return input.IndexerFlags.HasFlag((IndexerFlags)Value) == true;
return flags?.HasFlag((IndexerFlags)Value) == true;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
@@ -32,12 +32,13 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))] [FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
public int Value { get; set; } public int Value { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
var comparedLanguage = movieInfo != null && Value == Language.Original.Id && movieInfo.ExtraInfo.ContainsKey("OriginalLanguage") var comparedLanguage = input.MovieInfo != null && input.Movie != null && Value == Language.Original.Id && input.Movie.MovieMetadata.Value.OriginalLanguage != Language.Unknown
? (Language)movieInfo.ExtraInfo["OriginalLanguage"] ? input.Movie.MovieMetadata.Value.OriginalLanguage
: (Language)Value; : (Language)Value;
return movieInfo?.Languages?.Contains(comparedLanguage) ?? false;
return input?.Languages?.Contains(comparedLanguage) ?? false;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
@@ -32,9 +32,9 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Quality Modifier", Type = FieldType.Select, SelectOptions = typeof(Modifier))] [FieldDefinition(1, Label = "Quality Modifier", Type = FieldType.Select, SelectOptions = typeof(Modifier))]
public int Value { get; set; } public int Value { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
return (movieInfo?.Quality?.Quality?.Modifier ?? (int)Modifier.NONE) == (Modifier)Value; return (input.MovieInfo?.Quality?.Quality?.Modifier ?? (int)Modifier.NONE) == (Modifier)Value;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
@@ -8,9 +8,9 @@ namespace NzbDrone.Core.CustomFormats
public override string ImplementationName => "Release Group"; public override string ImplementationName => "Release Group";
public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2";
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
return MatchString(movieInfo?.ReleaseGroup); return MatchString(input.MovieInfo?.ReleaseGroup);
} }
} }
} }
@@ -9,11 +9,9 @@ namespace NzbDrone.Core.CustomFormats
public override string ImplementationName => "Release Title"; public override string ImplementationName => "Release Title";
public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2"; public override string InfoLink => "https://wiki.servarr.com/radarr/settings#custom-formats-2";
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
var filename = (string)movieInfo?.ExtraInfo?.GetValueOrDefault("Filename"); return MatchString(input.MovieInfo?.SimpleReleaseTitle) || MatchString(input.Filename);
return MatchString(movieInfo?.SimpleReleaseTitle) || MatchString(filename);
} }
} }
} }
@@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))] [FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
public int Value { get; set; } public int Value { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
return (movieInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value; return (input.MovieInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == Value;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
@@ -28,9 +28,9 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)] [FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)]
public double Max { get; set; } public double Max { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
var size = (movieInfo?.ExtraInfo?.GetValueOrDefault("Size", 0.0) as long?) ?? 0; var size = input.Size;
return size > Min.Gigabytes() && size <= Max.Gigabytes(); return size > Min.Gigabytes() && size <= Max.Gigabytes();
} }
@@ -24,9 +24,9 @@ namespace NzbDrone.Core.CustomFormats
[FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(Source))] [FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(Source))]
public int Value { get; set; } public int Value { get; set; }
protected override bool IsSatisfiedByWithoutNegate(ParsedMovieInfo movieInfo) protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
{ {
return (movieInfo?.Quality?.Quality?.Source ?? (int)Source.UNKNOWN) == (Source)Value; return (input.MovieInfo?.Quality?.Quality?.Source ?? (int)Source.UNKNOWN) == (Source)Value;
} }
public override NzbDroneValidationResult Validate() public override NzbDroneValidationResult Validate()
@@ -627,13 +627,13 @@ namespace NzbDrone.Core.Datastore.Migration
try try
{ {
if (audioChannelPositions.Contains("+")) if (audioChannelPositions.Contains('+'))
{ {
return audioChannelPositions.Split('+') return audioChannelPositions.Split('+')
.Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture)); .Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture));
} }
if (audioChannelPositions.Contains("/")) if (audioChannelPositions.Contains('/'))
{ {
var channelStringList = Regex.Replace(audioChannelPositions, var channelStringList = Regex.Replace(audioChannelPositions,
@"^\d+\sobjects", @"^\d+\sobjects",
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Text; using System.Text;
@@ -250,7 +250,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
} }
Index = end + 1; Index = end + 1;
identifier.Append(Buffer.Substring(start, end - start)); identifier.Append(Buffer.AsSpan(start, end - start));
if (Buffer[Index] != escape) if (Buffer[Index] != escape)
{ {
@@ -15,9 +15,9 @@ namespace NzbDrone.Core.Datastore
private const DbType EnumerableMultiParameter = (DbType)(-1); private const DbType EnumerableMultiParameter = (DbType)(-1);
private readonly string _paramNamePrefix; private readonly string _paramNamePrefix;
private readonly bool _requireConcreteValue = false; private readonly bool _requireConcreteValue;
private int _paramCount = 0; private int _paramCount;
private bool _gotConcreteValue = false; private bool _gotConcreteValue;
public WhereBuilderPostgres(Expression filter, bool requireConcreteValue, int seq) public WhereBuilderPostgres(Expression filter, bool requireConcreteValue, int seq)
{ {

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