1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-03-05 13:21:25 -05:00

Compare commits

..

209 Commits

Author SHA1 Message Date
Bogdan
c96b3c4b0b Fixed: Autotagging Genres are case insensitive
(cherry picked from commit 87ecbf39c1c0cc8a3a3f4ee1d1878b34ea49f6b8)

Closes #9436
2023-11-20 00:59:06 +02:00
Mark McDowall
78bc9f9b4b Fixed: Saving indexer, download client, etc settings
(cherry picked from commit 804a5921b3b620e2407d5d6a7fd69fb1fd9b0cbf)
2023-11-20 00:53:45 +02:00
Servarr
b737f05a83 Automated API Docs update 2023-11-19 22:40:17 +02:00
Bogdan
8d96fd2387 More cleanup appName tokens 2023-11-19 22:35:57 +02:00
servarr[bot]
6c487ead00 Fixed: Disable SSL when using the removed SslCertHash configuration (#9426)
(cherry picked from commit d95660d3c78d1ee11a7966d58e78a82a8df01393)

Co-authored-by: Mark McDowall <mark@mcdowall.ca>
2023-11-19 21:46:06 +02:00
Bogdan
22e3cf844c New: Confirmation before clearing blocklist
(cherry picked from commit 366d8a4e78e024bc045e4791d3a51af391f4e95a)

Closes #9429
Fixes #9401
2023-11-19 21:17:00 +02:00
Bogdan
14b4b5e122 Cleanup appName tokens
Co-authored-by: Stevie Robinson <stevie.robinson@gmail.com>

Closes #9430
2023-11-19 21:14:48 +02:00
Mark McDowall
10f5f3c5c8 New: Require password confirmation when setting or changing password
(cherry picked from commit b248163df598dc611ee919d525eb7357256d73d5)
2023-11-19 21:10:52 +02:00
Bogdan
c687b552f0 Bump version to 5.2.2 2023-11-19 07:06:29 +02:00
Stevie Robinson
92c8c8a7f5 Translate System pages
(cherry picked from commit 93e8ff0ac7610fa8739f2e577ece98c2c06c8881)

Closes #8852
2023-11-18 23:44:43 +02:00
Stevie Robinson
86a16c3c0c Fix translation token for Update Check
(cherry picked from commit cd1d8a3ff04b0d06f6fa7dfeb7a7ab6dd88b36b3)

Closes #9341
2023-11-18 23:44:41 +02:00
Weblate
e8c280db34 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Baptiste Mongin <baptiste.mongin@gmail.com>
Co-authored-by: Francisco Cachado <franciscomcachado@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Javier Parada <jparada@gmail.com>
Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Co-authored-by: Ruben Lourenco <ruben.lourenco01@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: bowsefather <husseinali39@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
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/tr/
Translation: Servarr/Radarr
2023-11-18 03:21:28 +02:00
Nesego
ea65e2174c Translated using Weblate (French) [skip ci]
Currently translated at 100.0% (1336 of 1336 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
2023-11-18 03:01:28 +02:00
Weblate
4aa2466693 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Baptiste Mongin <baptiste.mongin@gmail.com>
Co-authored-by: Francisco Cachado <franciscomcachado@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Javier Parada <jparada@gmail.com>
Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Co-authored-by: Ruben Lourenco <ruben.lourenco01@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: bowsefather <husseinali39@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
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/tr/
Translation: Servarr/Radarr
2023-11-18 02:59:39 +02:00
Bogdan
4df0f0f721 Revert translation keys 2023-11-18 02:49:58 +02:00
Mark McDowall
d7bee375e8 Use named tokens for backend translations
(cherry picked from commit 11f96c31048c2d1aafca0c91736d439f7f9a95a8)
2023-11-18 02:37:33 +02:00
Bogdan
906295466d Use variable for App name in translations
Co-authored-by: Qstick <qstick@gmail.com>
2023-11-18 02:37:33 +02:00
Mark McDowall
f86060eca2 Don't retest unchanged providers
New: Don't retest connections, indexers, download clients, etc if re-saved with the exact same settings

Closes #9411
Fixes ##9397

(cherry picked from commit 71fd09f162b2880c461e03cba4317c34ee3203dc)
2023-11-17 16:27:42 +02:00
Mark McDowall
bf9a0b62f2 Rename 'ReturnUrl' to 'returnUrl' for forms auth redirection
(cherry picked from commit 812712e2843a738054c065a6d5c1b7c81c5f8e7b)
2023-11-17 03:30:03 +02:00
Bogdan
ccc62f0450 Fixed: Enforce validation warnings when testing providers
(cherry picked from commit c3b4126d0c4f449a41e2cf7ea438b20e25370995)
2023-11-17 02:40:39 +02:00
bakerboy448
524657ad78 Improve sample detection logging (#9405)
Sonarr 6181
2023-11-15 19:38:58 +02:00
Qstick
7a394ff864 Update menu background on dark theme 2023-11-14 20:57:26 -06:00
Bogdan
d8862eedd3 Fixed: Refresh discovery movies state on refresh lists 2023-11-14 20:36:12 +02:00
Bogdan
71f700e240 Fixed: (HDBits) Add labels for categories, codecs and mediums 2023-11-14 17:13:34 +02:00
Bogdan
ae5dd84e0a Fixed: (HDBits) Increase search limit to 100 releases 2023-11-14 15:13:45 +02:00
Bogdan
17b398cf62 Bump version to 5.2.1 2023-11-12 16:52:53 +02:00
Qstick
d00678c1ba Bump ffprobe to 5.1.4
(cherry picked from commit e68b13940d4dd4c5fd008884d909e614521c8cee)
2023-11-11 20:04:59 +02:00
Bogdan
03df9b7f07 Add troubleshooting link when movie searches fail 2023-11-11 18:45:34 +02:00
Bogdan
3442a0ecca Bump version to 5.2.0 2023-11-10 06:24:33 +02:00
Bogdan
3376a467ca Fixed: Record status for notifications on tests 2023-11-10 05:07:29 +02:00
Servarr
1650ce17fb Automated API Docs update 2023-11-10 03:33:52 +02:00
Bogdan
2f2004faa2 New: Resolve download client by name using 'downloadClient' for pushed releases
(cherry picked from commit 0496e728dc1e21b16b9ee2fee010de9707bdff85)
2023-11-10 03:14:38 +02:00
Mark McDowall
437e2f4597 Don't store successful results for invalid providers
(cherry picked from commit de23182d593e2284972103d505e66dd8d812dfdb)
2023-11-10 03:13:27 +02:00
Bogdan
17b8605751 Simply queue item for movie details page 2023-11-09 20:53:49 +02:00
Bogdan
b2a52e52b6 Fixed: (UI) Progress bar status for searching existing movies 2023-11-09 20:53:49 +02:00
Servarr
0f5fabdfcd Automated API Docs update 2023-11-09 15:11:53 +02:00
Bogdan
6362ee9b7d Fixed: (UI) Import Lists enabled status doesn't rely on automatic add
Fixes #9375
2023-11-09 14:27:10 +02:00
Bogdan
50465fd482 New: Add enabled to manage import lists bulk 2023-11-09 14:16:03 +02:00
Bogdan
54d447d55f New: Parse VIE as Vietnamese language 2023-11-08 20:37:07 +02:00
Bogdan
50f48277e5 Don't show already imported movies as downloading in Movies index 2023-11-08 17:49:29 +02:00
Weblate
c2e206b7ac Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Baptiste Mongin <baptiste.mongin@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Javier Parada <jparada@gmail.com>
Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Co-authored-by: Ruben Lourenco <ruben.lourenco01@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
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-11-07 20:29:36 +02:00
Bogdan
7a46de602f Increase the wait timeout for integration tests init 2023-11-06 00:29:02 +02:00
bakerboy448
89820c1ff7 Lower log level for missing translation/culture resource (#8956)
Fixes #8150
2023-11-04 16:21:47 +02:00
Stevie Robinson
67e6e129ff Fix event ids for history custom filters 2023-11-02 00:37:54 +02:00
Bogdan
b6001238e5 Sort root folders by path in UI
Fixes #9280
2023-11-01 13:13:21 +02:00
Stevie Robinson
7bbdcc81bb Use Diacritical.Net library for TitleFirstCharacter token
(cherry picked from commit 59ea524e0ce85333779f430b867e93aae366289f)

Closes #9324
2023-11-01 13:13:21 +02:00
Stevie Robinson
3e8cbc497e Fixed: Cleanup First Character in Title when using 'TitleFirstCharacter'
(cherry picked from commit b3c691859a0841cde9bb2655c01c240cc5279d74)

Closes #9249
2023-11-01 13:13:21 +02:00
Bogdan
60d2df043b Rename instances of Profile to QualityProfile
Closes #9337
2023-11-01 13:13:21 +02:00
Mark McDowall
da41cb8840 Fixed: Blocking unknown indexers from pushed releases
(cherry picked from commit 44d8dbaac81706691124ae5f8317289f0a3e5d73)
2023-10-30 14:21:29 +02:00
Bogdan
a4b7c99d91 Fix full disk releases regex for DVDs
(cherry picked from commit aa8659eecd0a0fedd56eb1066730d1d1ec32df0d)

Closes #9346
2023-10-29 18:50:38 +02:00
Bogdan
8fb21e073b Bump version to 5.1.3 2023-10-29 10:35:11 +02:00
Stevie Robinson
dbf424d454 New: Relative path as default Sort order on Manual Import
(cherry picked from commit 6e2162ebf4b3366df540bd35ad91bd5527976728)

Closes #9344
2023-10-29 01:12:45 +03:00
Bogdan
a6dda70c0a New: Add Download Client validation for indexers
(cherry picked from commit e53b7f8c945e3597ca1719961e82540f1f01f0e9)

Closes #9338
2023-10-29 01:05:50 +03:00
Bogdan
e6fa14b1e6 Allow 0 as valid value in QualityProfileExistsValidator
(cherry picked from commit 36ca24e55a5eda859047d82855f65c401cc0b30f)
2023-10-29 00:59:16 +03:00
Bogdan
b5c0d515ee Improve UI notice for delayed queue items
(cherry picked from commit f2cae4e2b2f87924973a00431be7216296648dac)
2023-10-29 00:59:02 +03:00
Bogdan
b7aee25d0d Set proper default props for Queue details and status
(cherry picked from commit dc099a77ca9e6993d386a2b70f1aff1fed34a32b)
2023-10-29 00:58:30 +03:00
Bogdan
233b85aaf3 Sort Custom Formats by name
(cherry picked from commit e9bb1d52a72b20a58d1a672ecfa3797eda6f081a)
2023-10-29 00:58:11 +03:00
Weblate
80db9a7dd4 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Jordy <prive@jordyhoebergen.nl>
Co-authored-by: LeDaFeEs <leonardo.fonseca85300@gmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: bai0012 <baicongrui@gmail.com>
Co-authored-by: 無情天 <kofzhanganguo@126.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
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/zh_CN/
Translation: Servarr/Radarr
2023-10-28 22:05:58 +03:00
Bogdan
660d3d7643 Fixed: Don't reject DVDRip/DVDRemux as full DVD disk releases 2023-10-28 22:05:12 +03:00
Bogdan
d999aea36f Add missing translation in release profiles 2023-10-26 00:18:52 +03:00
Jordy
5d45f1de89 Update link to Docker instructions in readme (#9318) 2023-10-25 11:21:51 +03:00
Weblate
3e5089719c Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Ruben Lourenco <ruben.lourenco01@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: jianl <jianjianfengyun@126.com>
Co-authored-by: 宿命 <331874545@qq.com>
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/zh_CN/
Translation: Servarr/Radarr
2023-10-22 10:53:19 +03:00
Bogdan
ec69dfaabb Bump version to 5.1.2 2023-10-22 09:37:23 +03:00
Qstick
aa13a40bad Fixed: Update movie collection status in Radarr if removed on TMDB 2023-10-21 20:11:49 -05:00
Bogdan
9b458812f1 Fixed: Set Busy Timeout for SQLite 2023-10-21 22:47:19 +03:00
Bogdan
1bdc48a889 Revert "Bump SQLite to 3.42.0 (1.0.118)"
This reverts commit e3160466e0.
2023-10-20 22:28:49 +03:00
Servarr
e5d479a162 Automated API Docs update 2023-10-17 19:13:07 +03:00
Bogdan
9a50fcb82a Sort movies by name in filter builder 2023-10-17 18:59:34 +03:00
Stevie Robinson
f2357e0b60 Fixed: Reduce font size for series title on series details
(cherry picked from commit 03f5174a4b2a005aab8d1a1540f4bcb272682f2e)

Closes #9301
Closes #9302
2023-10-17 18:23:33 +03:00
Mark McDowall
0591d05c3b New: History custom filters
(cherry picked from commit 2fe8f3084c90688e6dd01d600796396e74f43ff9)

Closes #9298
2023-10-17 18:23:07 +03:00
Mark McDowall
299d50d56c Cleanup Calendar custom filters
(cherry picked from commit fd789343b587da252461d84bafba2d72651a11df)

Closes #9296
2023-10-17 14:41:55 +03:00
Mark McDowall
7d3c01114b New: Queue custom filters
(cherry picked from commit e357d17b187378b92377f8acb077b12c1e7ea527)

Closes #9297
2023-10-17 14:38:49 +03:00
bakerboy448
70376af70b Fixed: Re-run Removed Movie health check after movie is deleted (#9277) 2023-10-17 12:39:20 +03:00
Bogdan
9ef031bd9e Fixed: Don't die when adding existing exclusions 2023-10-16 05:45:24 +03:00
Weblate
3a9b276c43 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: DavidHenryThoreau <sorau@protonmail.com>
Co-authored-by: Dlgeri123 <bornemiszageri@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: bai0012 <baicongrui@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-10-15 18:33:49 +03:00
Bogdan
aabf209a07 Bump version to 5.1.1 2023-10-15 07:52:43 +03:00
Mark McDowall
79c03f2fe6 Fixed: Reject full DVD disk releases
(cherry picked from commit df2e867528249cf707788d8341c4a26293e179ba)
2023-10-14 21:06:36 -04:00
Bogdan
9b36404071 Fixed: Don't die in Collections when a collection doesn't have movies 2023-10-14 20:11:36 +03:00
Bogdan
ecfaea3885 Fixed: Don't die in FileNameBuilder when OriginalTitle is null 2023-10-13 19:49:31 +03:00
Bogdan
bfbeb4c62e Fixed: Ignore case when cleansing announce URLs 2023-10-12 05:03:22 +03:00
Bogdan
4b98d27f31 New: Tooltips for dates in MovieReleaseDates 2023-10-11 15:02:43 +03:00
Weblate
604d74270d Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: He Zhu <zhuhe202@qq.com>
Co-authored-by: Timo <Tclemens@live.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 宿命 <331874545@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-10-11 06:33:42 +03:00
Bogdan
15bb9139d1 New: Sort movies by release dates 2023-10-11 06:32:30 +03:00
Servarr
32722eb704 Automated API Docs update 2023-10-11 05:07:00 +03:00
Mark McDowall
e0c8a8f0d6 New: Download client option for redownloading failed releases from Interactive Search
(cherry picked from commit 87e0a7983a437a4d166aa8b9c9eaf78ea5431969)

Closes #9260
2023-10-11 04:50:39 +03:00
Bogdan
a3bb0541f0 Prevent NullRef on header assert 2023-10-11 04:29:20 +03:00
Bogdan
e78bc34514 Fixed: Fetch import lists without depending on Automatic Add 2023-10-11 03:38:28 +03:00
Servarr
35c4538288 Automated API Docs update 2023-10-11 02:27:16 +03:00
Bogdan
3981e816cd Remove PagingResourceFilter 2023-10-11 02:19:28 +03:00
Mark McDowall
9354031571 Log executing health check
Towards #6076

(cherry picked from commit 78b39bd2fecda60e04a1fef17ae17f62bd2b6914)
2023-10-10 07:04:52 +03:00
Mark McDowall
a01328dc8c Paging params in API docs
(cherry picked from commit bfaa7291e14a8d3847ef2154a52c363944560803)

Closes #9248
2023-10-10 07:03:41 +03:00
Stevie Robinson
8cb6295ddc New: Additional tooltips for icon buttons
(cherry picked from commit 8c07f0d3d19a48ed96d1ded54399c66bf2977b2a)

Closes #9253
2023-10-10 06:51:57 +03:00
Bogdan
99f7d8bcf5 New: Auto tag based on movie's quality profile
(cherry picked from commit 6de3e7c950bd939bab96ef2ae74337108ad5a212)

Closes #9254
2023-10-10 06:50:36 +03:00
Bogdan
f13d479b88 Add status test all button for IndexerLongTermStatusCheck
(cherry picked from commit 4ffa1816bd2305550abee20cea27e1296a99ddf6)
2023-10-10 06:48:04 +03:00
Bogdan
23eb637bc3 Fixed: Avoid logging evaluations when not using any Remote Path Mappings
(cherry picked from commit 44eb729ccc13237f4439006159bd616e8bdb5750)
2023-10-10 06:47:49 +03:00
Bogdan
3a786d0b9d Fixed: Displaying multiple values when adding custom filters
Closes #5810
2023-10-10 05:55:55 +03:00
Bogdan
6fb127235c Fixed: Calendar's agenda on mobile 2023-10-10 01:42:46 +03:00
Qstick
5517e578b6 Bump version to 5.1.0 2023-10-07 23:07:42 -05:00
nuxen
bced2e7b2e Fixed: Updated BR-DISK quality parsing 2023-10-08 06:49:37 +03:00
Bogdan
f7313369b5 Fixed: Show year in collection movies as labels 2023-10-08 06:12:18 +03:00
Servarr
b14e93e11f Automated API Docs update 2023-10-08 01:50:59 +03:00
Bogdan
f5692d6cf1 Fixed: Show year and fix sorting for collection movies 2023-10-08 01:41:24 +03:00
Weblate
a2d505c795 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Florian <sephrat.flo@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: blankhang <blankhang@gmail.com>
Co-authored-by: 宿命 <331874545@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-10-07 16:53:23 -05:00
Qstick
3d46bd2d8f Revert cover mapping for collections, optimize translation mapping 2023-10-07 15:43:59 -05:00
Bogdan
017f272201 Log Notifiarr errors as warnings 2023-10-07 22:56:22 +03:00
Bogdan
c221e2097a Prevent NullRef for cases when media covers have nullable urls 2023-10-05 02:42:18 +03:00
Bogdan
a61804e949 Fixed localization test 2023-10-05 01:45:55 +03:00
Bogdan
cb2bed93cb Fixed: Sorting by movie title in Blocklist and History
Fixes #9234
2023-10-05 01:02:35 +03:00
Weblate
2bea61bae5 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Dimitri <dimitridroeck@gmail.com>
Co-authored-by: Garkus98 <ivan12061998@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: RicardoVelaC <ricardovelac@gmail.com>
Co-authored-by: 宿命 <331874545@qq.com>
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/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-10-04 08:43:53 +03:00
Mark McDowall
7922109f01 Fixed a potential issue when extra files for multiple movies have the same relative path
(cherry picked from commit a6a68b4cae7688506c45ff6cf10989fe6596c274)

Closes #7222
2023-10-04 00:09:58 +03:00
Mark McDowall
46dd72e0cd New: Validate that naming formats don't contain illegal characters
(cherry picked from commit 145c644c9d8f1636027da8a782a7e74f3182c678)

Closes #5382
2023-10-03 23:49:30 +03:00
Mark McDowall
4e3535f1fe Fixed Misaligned table border
(cherry picked from commit aa938d911b61b08185dc57a0887f3f33e3c6e1f2)

Closes #8176
2023-10-03 23:36:45 +03:00
Mark McDowall
3468f1144d New: Calendar month view will scroll to today on load and press
(cherry picked from commit 7c0d3444376caa8a116b5c2084821c326beb01a1)

Closes #8501
2023-10-03 21:42:50 +03:00
Bogdan
572c410f54 Add runtime param to ServerSideNotificationService 2023-10-02 18:16:43 +03:00
Bogdan
1762a189d2 Fixed: (PassThePopcorn) Disable grouping 2023-10-02 03:38:24 +03:00
Mark McDowall
e2f5f2f73a Fixed: Completed downloads in Qbit missing import path
(cherry picked from commit 35365665cfd436ac276dd9591e23333bd26cf789)

Closes #9221
2023-10-01 17:13:24 +03:00
Weblate
ade387ba74 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Stevie Robinson <stevie.robinson@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mr cmuc <github@nextcos.de>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translation: Servarr/Radarr
2023-10-01 17:11:28 +03:00
Stevie Robinson
6b9a622328 Fixed: qBittorent history retention to allow at least 14 days seeding
(cherry picked from commit 33b87acabf2b4c71ee24cda1a466dec6f4f76996)
2023-10-01 17:11:05 +03:00
bakerboy448
ba5028bebb Fixed: Only apply remote path mappings for completed items in Qbit
(cherry picked from commit 583eb52ddc01b608ab6cb17e863a8830c17b7b75)
2023-10-01 17:10:47 +03:00
Stevie Robinson
33d1d1f875 Fixed: SABnzbd history retention to allow at least 14 days
(cherry picked from commit a3938d8e0264b48b35f4715cbc15329fb489218a)

Closes #9217
2023-09-27 23:14:34 +03:00
Weblate
fb60dcb5bf Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Jaspils <jasperkemper@gmail.com>
Co-authored-by: SKAL <sir_kalot@yahoo.it>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 宿命 <331874545@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-09-27 19:45:22 +03:00
Bogdan
ddf23530fc Bump version to 5.0.3 2023-09-24 16:22:06 +03:00
Bogdan
30b1edbff0 Add content summary for bulk movie info 2023-09-20 20:58:39 +03:00
Bogdan
f20c260a4f Avoid returning null in static resource mapper Task 2023-09-19 20:15:59 +03:00
Bogdan
2fcbac49c7 Fixed: Add support for TMDb in Plex Watchlist RSS
Fixes #9208
2023-09-19 19:10:20 +03:00
Mark McDowall
3248e7f476 Fixed: Pushed releases not being properly rejected
(cherry picked from commit 07f816ffb18ac34090c2f8ba25147737299b361d)

Closes #9204
2023-09-19 14:04:01 +03:00
Mark McDowall
ce145a3050 Optional 'downloadClientId' for pushed releases
(cherry picked from commit fa5bfc3742c24c5730b77bf8178a423d98fdf50e)

Closes #9190
2023-09-19 14:01:45 +03:00
Bogdan
3bc4197b4a Preserve the protocol for fanart images
(cherry picked from commit d8633b968830fd20d73612e9f0d0559b0bcb304c)

Closes #9205
2023-09-19 14:01:39 +03:00
Bogdan
552b8f91d2 Fixed: Skip parsing releases without title
(cherry picked from commit c7824bb593291634bf14a5f7aa689666969b03bf)
2023-09-19 13:58:11 +03:00
Bogdan
e9e36ae56a Revert "New: Optional 'downloadClientId' for pushed releases"
This reverts commit 4e01fa57fd.

Closes #9202
2023-09-19 04:07:27 +03:00
Bogdan
450d6c0c80 Replace support-requests with label-actions 2023-09-19 02:05:48 +03:00
Servarr
9eece2965a Automated API Docs update 2023-09-18 16:04:17 +03:00
Stevie Robinson
cd5d4f993a Add health check for dl clients removing completed downloads + enable for sab and qbit
(cherry picked from commit 7f2cd8a0e99b537a1c616998514bacdd8468a016)

Closes #9195
2023-09-18 15:28:13 +03:00
Bogdan
fe7203815d Preserve the protocol in Movie Image
(cherry picked from commit a2f16bddfd7dfba207c5feaaf472913c38dc3e25)

Closes #9198
2023-09-18 15:24:06 +03:00
Mark McDowall
4e01fa57fd New: Optional 'downloadClientId' for pushed releases
(cherry picked from commit fa5bfc3742c24c5730b77bf8178a423d98fdf50e)

Closes #9190
2023-09-18 15:18:55 +03:00
Bogdan
bbeb4d7b5f Log request failures in Notifiarr 2023-09-18 14:57:05 +03:00
Mark McDowall
49dac0ebaa New: Don't treat 400 responses from Notifiarr as errors
(cherry picked from commit 5eb420bbe12f59d0a5392abf3d351be28ca210e6)

Closes #9194
2023-09-18 14:53:09 +03:00
Mark McDowall
ea8f5c7b9f Fixed: Skip free space check only applies during import
(cherry picked from commit 5ff254b6468fc66a337dc0a13f4be53a997c52fd)

Closes #9191
2023-09-18 14:48:55 +03:00
Mark McDowall
24a17a9240 Fixed: Don't try to create metadata images if source files doesn't exist
(cherry picked from commit 9a1022386a031c928fc0495d6ab990ebce605ec1)

Closes #9189
2023-09-18 14:47:03 +03:00
Weblate
97c2d4f9db Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Daghriry <mdaghriri@gmail.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Gyuyeop Kim <rlarbduq777@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mati300m <mateusz.smolec@gmail.com>
Co-authored-by: 宿命 <331874545@qq.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/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/id/
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/pl/
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/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/zh_CN/
Translation: Servarr/Radarr
2023-09-18 14:43:29 +03:00
Qstick
b7cafb2917 Fixed: Show correct error on unauthorized caps call
(cherry picked from commit f2b0fc946e1fb1b4649f1b46a003bd2add09a461)
2023-09-18 14:42:50 +03:00
Mark McDowall
2a2667a2ec Fixed: Don't allow quality profile to be created without all qualities
(cherry picked from commit 32e1ae2f64827272d351991838200884876e52b4)
2023-09-18 14:42:06 +03:00
Bogdan
27da524391 Use async requests for media cover proxy
(cherry picked from commit ad1f185330a30a2a9d27c9d3f18d384e66727c2a)

Closes #9183
2023-09-18 03:46:52 +03:00
Bogdan
4bd1c14db9 Log exceptions for failed fetches in Radarr import lists
Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
(cherry picked from commit c1d9187bb66c0524048020613d816918b84b5532)

Closes #9186
2023-09-18 03:46:46 +03:00
Bogdan
608e2e7307 Use await on reading the response content
(cherry picked from commit 82d586e7015d7ea06356ca436024a8af5a4fb677)
2023-09-18 03:22:14 +03:00
Qstick
cff54d76b9 Fix regression in collections endpoint speed 2023-09-17 00:14:28 -05:00
Mark McDowall
3244282a83 Less logging when no import lists are enabled
(cherry picked from commit 7be4840f028f24e3920bd395a4e15eb5e643e46f)

Closes #9040
2023-09-15 15:22:59 +03:00
Weblate
1d488df242 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Akashi2020 <dieux02400@gmail.com>
Co-authored-by: AlexR-sf <omg.portal.supp@gmail.com>
Co-authored-by: Anthony Veaudry <anthonyveaudry@gmail.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Herve Lauwerier <hervelauwerier@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: 宿命 <331874545@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
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/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/zh_CN/
Translation: Servarr/Radarr
2023-09-14 23:34:25 +03:00
Bogdan
22927224c6 Fix lint issues in AppUpdatedModalContent 2023-09-14 23:32:33 +03:00
Stevie Robinson
51149bccdd Translate Updated and Connection Lost Modals in frontend
(cherry picked from commit 074aa6f4457bf83173e6ba7209c452a6e0659a35)

Closes #8994
2023-09-14 23:04:49 +03:00
Stevie Robinson
4bbc166040 Translate Frontend Organize + Rename modal
(cherry picked from commit 866fbc7f093c34e4414734f90e756e2f6870d84a)

Closes #9054
2023-09-14 23:04:44 +03:00
Bogdan
11c7446cbe Save Whitelisted Subtitle Tags as string
Fixes #9134
2023-09-14 22:37:53 +03:00
Mark McDowall
dce637905a Mock debouncer for testing
(cherry picked from commit bb7b2808e2f70389157408809ec47cc8860b4938)
2023-09-14 22:19:12 +03:00
Mark McDowall
7d85922f8d Fixed: Duplicate notifications for failed health checks
(cherry picked from commit c0e54773e213f526a5046fa46aa7b57532471128)
2023-09-14 22:19:12 +03:00
Bogdan
80f6033595 Fixed: Ignore invalid mount points
Fixes #9176
2023-09-13 22:21:43 +03:00
Weblate
78b8747b50 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Richard de Souza Leite <rs9010482@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: volrod64 <sebsogamer@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
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-09-13 19:16:37 +03:00
Stevie Robinson
c2df194d49 Translate Frontend Utilities
(cherry picked from commit 3f0e8ce8634b085d49638d11df96109797ac9cef)

Closes #9129
2023-09-12 20:11:18 +03:00
Stevie Robinson
4a41c67dfe Fix missing translations and correct some keys
(cherry picked from commit ce4ac7594192fa8f63ba239607be61d229c24ce1)
2023-09-12 19:33:32 +03:00
Stevie Robinson
85d51e485a Translate Activity pages
(cherry picked from commit 322836e2b3726b653bf4a7f01ea7018ef44a77bd)
2023-09-12 19:33:32 +03:00
Bogdan
50e2e9edef Add custom format score to history details 2023-09-12 19:33:32 +03:00
Bogdan
703c251b5c Fix some translations 2023-09-12 19:33:32 +03:00
Mark McDowall
a798556d32 New: Show detailed queue status on Calendar
(cherry picked from commit 8fff59ff107d9a9fcfc0de1acb6aa635565e5d9b)
2023-09-12 19:33:32 +03:00
Bogdan
69253a4ac4 Fix status style for unmonitored events 2023-09-12 12:05:59 +03:00
Mark McDowall
4e827e726f New: Calendar option for full color events
(cherry picked from commit 0210b5c5c1b5c56dce6f4c9f3f56366adba950d3)
2023-09-11 22:54:45 +03:00
Bogdan
e3abda9afc Ensure the images are mapped correctly after updating a movie 2023-09-11 18:53:34 +03:00
Steven Lu
0386ea9b71 Updating Steven Lu movies URL 2023-09-11 17:49:35 +03:00
Bogdan
f0fcd23248 Fix custom formats with indexer flags in queue 2023-09-11 11:10:38 +03:00
Mark McDowall
18f22d7ada Fixed: Parsing of multiple languages from Newznab/Torznab indexer releases
(cherry picked from commit 2a241294b5eeb9e95c46e030828191da09d05e88)

Closes #9159
2023-09-11 10:45:23 +03:00
Servarr
1c4b5f2abf Automated API Docs update 2023-09-10 22:17:06 -04:00
Mark McDowall
b48b970f25 Map Clearlogo images to cover type
(cherry picked from commit 809788eb2ed7e448f7f23101ae535533de57cd02)
2023-09-10 22:05:03 -04:00
Qstick
e715557a0d Fixed: Tweak all movies endpoint for better performance from Radarr lists 2023-09-10 22:04:23 -04:00
Qstick
248ac9619c Fixed: Images for some movies not downloading
Closes #8006

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2023-09-10 22:04:23 -04:00
Denis Gheorghescu
feff609685 New: Add support for Pushcut notifications 2023-09-10 23:29:54 +03:00
Bogdan
07cfbb59da Add media proxy for collections, credits and discovery images 2023-09-10 22:41:26 +03:00
Weblate
9db0058114 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Shiessis <shiessis@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/uk/
Translation: Servarr/Radarr
2023-09-10 20:29:24 +03:00
Weblate
8d7f6b9de8 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: AlexR-sf <omg.portal.supp@gmail.com>
Co-authored-by: ChewyGlitter <lulu3dddsss@gmail.com>
Co-authored-by: DavidJares <david.jares@me.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: He Zhu <zhuhe202@qq.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/fr/
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/zh_CN/
Translation: Servarr/Radarr
2023-09-10 00:26:49 -05:00
Bogdan
28c566a071 Hide obsolete indexer flags from UI 2023-09-10 08:18:23 +03:00
Bogdan
e5963c9ee1 Add rename deprecated indexer flags migration 2023-09-09 13:13:56 +03:00
Bogdan
336cb4a2bc Improve messaging for failed movie details data 2023-09-09 13:13:56 +03:00
Bogdan
ff3d38a515 New: Add support for additional Torznab indexer flags 2023-09-09 07:54:37 +03:00
Bogdan
a2bde5e016 Fixed: Calculating seed time for qBittorrent
(cherry picked from commit 1b3ff64cc521396f9f1623617052c497649325a8)
2023-09-08 04:25:55 +03:00
Bogdan
cb04ef960e Fixed: macOS version detection
(cherry picked from commit 060be6177a5477c94823e6a423c42064dedc1afb)
2023-09-08 04:14:47 +03:00
Servarr
ba732847ef Automated API Docs update 2023-09-06 12:37:12 +03:00
Bogdan
1865257544 Include indexer flags to show the correct custom formats in movie file history 2023-09-06 12:29:37 +03:00
Bogdan
58e0b19d06 Include indexer flags to show the correct custom formats in movie imported history 2023-09-06 09:56:10 +03:00
Bogdan
05c5bcbe15 Bump ImageSharp 2023-09-05 03:18:27 +03:00
Bogdan
d6749a0c8e Migrate to merged proposals now included in babel/present-env
Bump core-js too.
2023-09-04 00:50:20 +03:00
Bogdan
72fe25d7b2 Bump dotnet to 6.0.21 2023-09-04 00:50:20 +03:00
Bogdan
0598d46ee8 Bump version to 5.0.2 2023-09-03 07:28:12 +03:00
Bogdan
e73d05c0fe Check if import list items are valid before importing
Co-authored-by: Qstick <qstick@gmail.com>
2023-09-03 04:21:10 +03:00
Bakerboy448
9a0ca650a3 Fixed: Dont Parse 24-bit as group 2023-09-02 20:16:40 -05:00
Qstick
ac6da13a82 Avoid double ToList conversion in ImportList fetch 2023-09-02 15:17:06 -05:00
Bogdan
61ffc50b7f Use paths relative to the frontend source root 2023-09-02 05:41:39 +03:00
Stevie Robinson
fcd8a4a873 Translate Frontend Store
Fixed indexer copy translation

(cherry picked from commit d31fcbb2dfe98a540a359b98b204d101d554cf03)
(cherry picked from commit 276352dda478aaa7be7d0fc744548adddec6e6b6)

Closes #9118
Closes #9121
2023-09-02 05:37:13 +03:00
Stevie Robinson
093bb94e42 Fixed: Fallback to English translations if invalid UI language in config
(cherry picked from commit 4c7201741276eccaea2fb1f33daecc31e8b2d54e)

Close #9112
2023-09-02 05:29:36 +03:00
Bogdan
14b9dd77af Display a not allowed cursor for disabled select options 2023-09-02 05:18:35 +03:00
Bogdan
d6b62e738a Fixed: Increase timeout when downloading updates
(cherry picked from commit 467ce70291c793042ffb3ed8942c42e7bc1424d5)
2023-09-01 04:04:07 +03:00
Bogdan
53254f6aeb Fix showing Grab ID in history details modal 2023-08-29 22:36:33 +03:00
Bogdan
756384d94a Simplify user check in Authentication Required for newer installs 2023-08-29 19:57:48 +03:00
Bogdan
7f172dcfd1 Fix user check in Authentication Required for newer installs 2023-08-29 18:54:12 +03:00
Mark McDowall
02998cd59a Fixed: Auto Tag required not showing in the UI correctly
(cherry picked from commit 08939f2fb4b6f7aca4916d8bd3b60535f3a992da)
2023-08-29 08:31:10 +03:00
Stevie Robinson
37aa739611 Add info box under health messages
(cherry picked from commit 936ef9f461c3e80a36099508c420dc44d8cd293c)

Closes #9095
2023-08-29 05:43:12 +03:00
Bogdan
0e2c98827f Remove column labels from Interactive Search 2023-08-29 05:31:50 +03:00
Bogdan
27f45b8fd6 Add missing app name token for translations 2023-08-29 03:29:46 +03:00
Bogdan
2210ce9394 Improve messaging in Authentication Required modal 2023-08-29 01:42:30 +03:00
Bogdan
bbef1590a3 Remove duplicate Language interface 2023-08-29 01:35:55 +03:00
Bogdan
feb3131ad4 Fix download tooltip in interactive search 2023-08-28 03:36:32 +03:00
Weblate
89f5595e64 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: ChewyGlitter <lulu3dddsss@gmail.com>
Co-authored-by: DavidJares <david.jares@me.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/
Translate-URL: https://translate.servarr.com/projects/servarr/radarr/cs/
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-08-27 19:20:29 +03:00
Bogdan
cf9cff61b5 Bump version to 5.0.1 2023-08-27 08:02:02 +03:00
398 changed files with 10953 additions and 6910 deletions

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

@@ -0,0 +1,16 @@
# Configuration for Label Actions - https://github.com/dessant/label-actions
'Type: Support':
comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord).
close: true
close-reason: 'not planned'
'Status: Logs Needed':
comment: >
:wave: @{issue-author}, In order to help you further we'll need to see logs.
You'll need to enable trace logging and replicate the problem that you encountered.
Guidance on how to enable trace logging can be found in
our [troubleshooting guide](https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files).

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

@@ -0,0 +1,17 @@
name: 'Label Actions'
on:
issues:
types: [labeled, unlabeled]
permissions:
contents: read
issues: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/label-actions@v3
with:
process-only: 'issues'

View File

@@ -1,36 +0,0 @@
name: 'Support requests'
on:
issues:
types: [labeled, unlabeled, reopened]
permissions: {}
jobs:
support:
permissions:
issues: write # to modify issues
runs-on: ubuntu-latest
steps:
- uses: dessant/support-requests@v3
with:
github-token: ${{ github.token }}
support-label: 'Type: Support'
issue-comment: >
:wave: @{issue-author}, we use the issue tracker exclusively
for bug reports and feature requests. However, this issue appears
to be a support request. Please hop over onto our [Discord](https://radarr.video/discord).
close-issue: true
close-reason: 'not planned'
lock-issue: false
- uses: dessant/support-requests@v3
with:
github-token: ${{ github.token }}
support-label: 'Status: Logs Needed'
issue-comment: >
:wave: @{issue-author}, In order to help you further we'll need to see logs.
You'll need to enable trace logging and replicate the problem that you encountered.
Guidance on how to enable trace logging can be found in
our [troubleshooting guide](https://wiki.servarr.com/radarr/troubleshooting#logging-and-log-files).
close-issue: false
lock-issue: false

View File

@@ -2,7 +2,7 @@
[![Build Status](https://dev.azure.com/Radarr/Radarr/_apis/build/status/Radarr.Radarr?branchName=develop)](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
[![Translated](https://translate.servarr.com/widgets/servarr/-/radarr/svg-badge.svg)](https://translate.servarr.com/engage/radarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/radarr.svg)](https://wiki.servarr.com/radarr/installation#docker)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/radarr.svg)](https://wiki.servarr.com/radarr/installation/docker)
![Github Downloads](https://img.shields.io/github/downloads/Radarr/Radarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Radarr/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/Radarr/sponsors/badge.svg)](#sponsors)

View File

@@ -9,13 +9,13 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '5.0.0'
majorVersion: '5.2.2'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.408'
dotnetVersion: '6.0.413'
nodeVersion: '16.X'
innoVersion: '6.2.0'
windowsImage: 'windows-2022'

View File

@@ -4,14 +4,14 @@ module.exports = {
plugins: [
// Stage 1
'@babel/plugin-proposal-export-default-from',
['@babel/plugin-proposal-optional-chaining', { loose }],
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
['@babel/plugin-transform-optional-chaining', { loose }],
['@babel/plugin-transform-nullish-coalescing-operator', { loose }],
// Stage 2
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-transform-export-namespace-from',
// Stage 3
['@babel/plugin-proposal-class-properties', { loose }],
['@babel/plugin-transform-class-properties', { loose }],
'@babel/plugin-syntax-dynamic-import'
],
env: {

View File

@@ -36,6 +36,7 @@ class Blocklist extends Component {
lastToggled: null,
selectedState: {},
isConfirmRemoveModalOpen: false,
isConfirmClearModalOpen: false,
items: props.items
};
}
@@ -90,6 +91,19 @@ class Blocklist extends Component {
this.setState({ isConfirmRemoveModalOpen: false });
};
onClearBlocklistPress = () => {
this.setState({ isConfirmClearModalOpen: true });
};
onClearBlocklistConfirmed = () => {
this.props.onClearBlocklistPress();
this.setState({ isConfirmClearModalOpen: false });
};
onConfirmClearModalClose = () => {
this.setState({ isConfirmClearModalOpen: false });
};
//
// Render
@@ -103,7 +117,6 @@ class Blocklist extends Component {
totalRecords,
isRemoving,
isClearingBlocklistExecuting,
onClearBlocklistPress,
...otherProps
} = this.props;
@@ -111,7 +124,8 @@ class Blocklist extends Component {
allSelected,
allUnselected,
selectedState,
isConfirmRemoveModalOpen
isConfirmRemoveModalOpen,
isConfirmClearModalOpen
} = this.state;
const selectedIds = this.getSelectedIds();
@@ -131,8 +145,9 @@ class Blocklist extends Component {
<PageToolbarButton
label={translate('Clear')}
iconName={icons.CLEAR}
isDisabled={!items.length}
isSpinning={isClearingBlocklistExecuting}
onPress={onClearBlocklistPress}
onPress={this.onClearBlocklistPress}
/>
</PageToolbarSection>
@@ -158,7 +173,7 @@ class Blocklist extends Component {
{
!isFetching && !!error &&
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadBlocklist')}
{translate('BlocklistLoadError')}
</Alert>
}
@@ -210,11 +225,21 @@ class Blocklist extends Component {
isOpen={isConfirmRemoveModalOpen}
kind={kinds.DANGER}
title={translate('RemoveSelected')}
message={translate('RemoveSelectedItemBlocklistMessageText')}
message={translate('RemoveSelectedBlocklistMessageText')}
confirmLabel={translate('RemoveSelected')}
onConfirm={this.onRemoveSelectedConfirmed}
onCancel={this.onConfirmRemoveModalClose}
/>
<ConfirmModal
isOpen={isConfirmClearModalOpen}
kind={kinds.DANGER}
title={translate('ClearBlocklist')}
message={translate('ClearBlocklistMessageText')}
confirmLabel={translate('Clear')}
onConfirm={this.onClearBlocklistConfirmed}
onCancel={this.onConfirmClearModalClose}
/>
</PageContent>
);
}

View File

@@ -82,7 +82,7 @@ class BlocklistRow extends Component {
return null;
}
if (name === 'movies.sortTitle') {
if (name === 'movieMetadata.sortTitle') {
return (
<TableRowCell key={name}>
<MovieTitleLink

View File

@@ -7,6 +7,7 @@ import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionList
import Link from 'Components/Link/Link';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import styles from './HistoryDetails.css';
@@ -15,6 +16,7 @@ function HistoryDetails(props) {
eventType,
sourceTitle,
data,
downloadId,
shortDateFormat,
timeFormat
} = props;
@@ -23,11 +25,11 @@ function HistoryDetails(props) {
const {
indexer,
releaseGroup,
movieMatchType,
customFormatScore,
nzbInfoUrl,
downloadClient,
downloadClientName,
downloadId,
movieMatchType,
age,
ageHours,
ageMinutes,
@@ -45,33 +47,31 @@ function HistoryDetails(props) {
/>
{
!!indexer &&
indexer ?
<DescriptionListItem
title={translate('Indexer')}
data={indexer}
/>
/> :
null
}
{
!!releaseGroup &&
releaseGroup ?
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('ReleaseGroup')}
data={releaseGroup}
/>
/> :
null
}
{
!!nzbInfoUrl &&
<span>
<DescriptionListItemTitle>
Info URL
</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
</DescriptionListItemDescription>
</span>
customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)}
/> :
null
}
{
@@ -84,6 +84,20 @@ function HistoryDetails(props) {
null
}
{
nzbInfoUrl ?
<span>
<DescriptionListItemTitle>
{translate('InfoUrl')}
</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
</DescriptionListItemDescription>
</span> :
null
}
{
downloadClientNameInfo ?
<DescriptionListItem
@@ -94,27 +108,30 @@ function HistoryDetails(props) {
}
{
!!downloadId &&
downloadId ?
<DescriptionListItem
title={translate('GrabID')}
title={translate('GrabId')}
data={downloadId}
/>
/> :
null
}
{
!!indexer &&
indexer ?
<DescriptionListItem
title={translate('AgeWhenGrabbed')}
data={formatAge(age, ageHours, ageMinutes)}
/>
/> :
null
}
{
!!publishedDate &&
publishedDate ?
<DescriptionListItem
title={translate('PublishedDate')}
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/>
/> :
null
}
</DescriptionList>
);
@@ -134,11 +151,21 @@ function HistoryDetails(props) {
/>
{
!!message &&
downloadId ?
<DescriptionListItem
title={translate('GrabId')}
data={downloadId}
/> :
null
}
{
message ?
<DescriptionListItem
title={translate('Message')}
data={message}
/>
/> :
null
}
</DescriptionList>
);
@@ -146,6 +173,7 @@ function HistoryDetails(props) {
if (eventType === 'downloadFolderImported') {
const {
customFormatScore,
droppedPath,
importedPath
} = data;
@@ -159,21 +187,32 @@ function HistoryDetails(props) {
/>
{
!!droppedPath &&
droppedPath ?
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('Source')}
data={droppedPath}
/>
/> :
null
}
{
!!importedPath &&
importedPath ?
<DescriptionListItem
descriptionClassName={styles.description}
title={translate('ImportedTo')}
data={importedPath}
/>
/> :
null
}
{
customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)}
/> :
null
}
</DescriptionList>
);
@@ -181,20 +220,21 @@ function HistoryDetails(props) {
if (eventType === 'movieFileDeleted') {
const {
reason
reason,
customFormatScore
} = data;
let reasonMessage = '';
switch (reason) {
case 'Manual':
reasonMessage = translate('FileWasDeletedByViaUI');
reasonMessage = translate('DeletedReasonManual');
break;
case 'MissingFromDisk':
reasonMessage = translate('MissingFromDisk');
reasonMessage = translate('DeletedReasonMissingFromDisk');
break;
case 'Upgrade':
reasonMessage = translate('FileWasDeletedByUpgrade');
reasonMessage = translate('DeletedReasonUpgrade');
break;
default:
reasonMessage = '';
@@ -211,6 +251,15 @@ function HistoryDetails(props) {
title={translate('Reason')}
data={reasonMessage}
/>
{
customFormatScore && customFormatScore !== '0' ?
<DescriptionListItem
title={translate('CustomFormatScore')}
data={formatCustomFormatScore(customFormatScore)}
/> :
null
}
</DescriptionList>
);
}
@@ -262,11 +311,21 @@ function HistoryDetails(props) {
/>
{
!!message &&
downloadId ?
<DescriptionListItem
title={translate('GrabId')}
data={downloadId}
/> :
null
}
{
message ?
<DescriptionListItem
title={translate('Message')}
data={message}
/>
/> :
null
}
</DescriptionList>
);
@@ -287,6 +346,7 @@ HistoryDetails.propTypes = {
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
downloadId: PropTypes.string,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired
};

View File

@@ -15,19 +15,19 @@ import styles from './HistoryDetailsModal.css';
function getHeaderTitle(eventType) {
switch (eventType) {
case 'grabbed':
return 'Grabbed';
return translate('Grabbed');
case 'downloadFailed':
return 'Download Failed';
return translate('DownloadFailed');
case 'downloadFolderImported':
return 'Movie Imported';
return translate('MovieImported');
case 'movieFileDeleted':
return 'Movie File Deleted';
return translate('MovieFileDeleted');
case 'movieFileRenamed':
return 'Movie File Renamed';
return translate('MovieFileRenamed');
case 'downloadIgnored':
return 'Download Ignored';
return translate('DownloadIgnored');
default:
return 'Unknown';
return translate('Unknown');
}
}
@@ -37,6 +37,7 @@ function HistoryDetailsModal(props) {
eventType,
sourceTitle,
data,
downloadId,
isMarkingAsFailed,
shortDateFormat,
timeFormat,
@@ -59,6 +60,7 @@ function HistoryDetailsModal(props) {
eventType={eventType}
sourceTitle={sourceTitle}
data={data}
downloadId={downloadId}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
/>
@@ -73,7 +75,7 @@ function HistoryDetailsModal(props) {
isSpinning={isMarkingAsFailed}
onPress={onMarkAsFailedPress}
>
Mark as Failed
{translate('MarkAsFailed')}
</SpinnerButton>
}
@@ -93,6 +95,7 @@ HistoryDetailsModal.propTypes = {
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
downloadId: PropTypes.string,
isMarkingAsFailed: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,

View File

@@ -14,6 +14,7 @@ import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptions
import TablePager from 'Components/Table/TablePager';
import { align, icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import HistoryFilterModal from './HistoryFilterModal';
import HistoryRowConnector from './HistoryRowConnector';
class History extends Component {
@@ -33,6 +34,7 @@ class History extends Component {
columns,
selectedFilterKey,
filters,
customFilters,
totalRecords,
onFilterSelect,
onFirstPagePress,
@@ -70,7 +72,8 @@ class History extends Component {
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={[]}
customFilters={customFilters}
filterModalConnectorComponent={HistoryFilterModal}
onFilterSelect={onFilterSelect}
/>
</PageToolbarSection>
@@ -85,7 +88,7 @@ class History extends Component {
{
!isFetchingAny && hasError &&
<Alert kind={kinds.DANGER}>
{translate('UnableToLoadHistory')}
{translate('HistoryLoadError')}
</Alert>
}
@@ -95,7 +98,7 @@ class History extends Component {
isPopulated && !hasError && !items.length &&
<Alert kind={kinds.INFO}>
{translate('NoHistory')}
{translate('NoHistoryFound')}
</Alert>
}
@@ -144,8 +147,9 @@ History.propTypes = {
moviesError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
selectedFilterKey: PropTypes.string.isRequired,
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
onFilterSelect: PropTypes.func.isRequired,
onFirstPagePress: PropTypes.func.isRequired

View File

@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import withCurrentPage from 'Components/withCurrentPage';
import * as historyActions from 'Store/Actions/historyActions';
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
import History from './History';
@@ -11,11 +12,13 @@ function createMapStateToProps() {
return createSelector(
(state) => state.history,
(state) => state.movies,
(history, movies) => {
createCustomFiltersSelector('history'),
(history, movies, customFilters) => {
return {
isMoviesFetching: movies.isFetching,
isMoviesPopulated: movies.isPopulated,
moviesError: movies.error,
customFilters,
...history
};
}

View File

@@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons, kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './HistoryEventTypeCell.css';
function getIconName(eventType) {
@@ -38,21 +39,21 @@ function getIconKind(eventType) {
function getTooltip(eventType, data) {
switch (eventType) {
case 'grabbed':
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
return translate('MovieGrabbedHistoryTooltip', { indexer: data.indexer, downloadClient: data.downloadClient });
case 'movieFolderImported':
return 'Movie imported from movie folder';
return translate('MovieFolderImportedTooltip');
case 'downloadFolderImported':
return 'Movie downloaded successfully and picked up from download client';
return translate('MovieImportedTooltip');
case 'downloadFailed':
return 'Movie download failed';
return translate('MovieDownloadFailedTooltip');
case 'movieFileDeleted':
return 'Movie file deleted';
return translate('MovieFileDeletedTooltip');
case 'movieFileRenamed':
return 'Movie file renamed';
return translate('MovieFileRenamedTooltip');
case 'downloadIgnored':
return 'Movie Download Ignored';
return translate('MovieDownloadIgnoredTooltip');
default:
return 'Unknown event';
return translate('UnknownEventTooltip');
}
}

View File

@@ -0,0 +1,54 @@
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import FilterModal from 'Components/Filter/FilterModal';
import { setHistoryFilter } from 'Store/Actions/historyActions';
function createHistorySelector() {
return createSelector(
(state: AppState) => state.history.items,
(queueItems) => {
return queueItems;
}
);
}
function createFilterBuilderPropsSelector() {
return createSelector(
(state: AppState) => state.history.filterBuilderProps,
(filterBuilderProps) => {
return filterBuilderProps;
}
);
}
interface HistoryFilterModalProps {
isOpen: boolean;
}
export default function HistoryFilterModal(props: HistoryFilterModalProps) {
const sectionItems = useSelector(createHistorySelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'history';
const dispatch = useDispatch();
const dispatchSetFilter = useCallback(
(payload: unknown) => {
dispatch(setHistoryFilter(payload));
},
[dispatch]
);
return (
<FilterModal
// TODO: Don't spread all the props
{...props}
sectionItems={sectionItems}
filterBuilderProps={filterBuilderProps}
customFilterType={customFilterType}
dispatchSetFilter={dispatchSetFilter}
/>
);
}

View File

@@ -64,6 +64,7 @@ class HistoryRow extends Component {
sourceTitle,
date,
data,
downloadId,
isMarkingAsFailed,
columns,
shortDateFormat,
@@ -98,7 +99,7 @@ class HistoryRow extends Component {
);
}
if (name === 'movies.sortTitle') {
if (name === 'movieMetadata.sortTitle') {
return (
<TableRowCell key={name}>
<MovieTitleLink
@@ -216,10 +217,12 @@ class HistoryRow extends Component {
key={name}
className={styles.details}
>
<IconButton
name={icons.INFO}
onPress={this.onDetailsPress}
/>
<div className={styles.actionContents}>
<IconButton
name={icons.INFO}
onPress={this.onDetailsPress}
/>
</div>
</TableRowCell>
);
}
@@ -233,6 +236,7 @@ class HistoryRow extends Component {
eventType={eventType}
sourceTitle={sourceTitle}
data={data}
downloadId={downloadId}
isMarkingAsFailed={isMarkingAsFailed}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
@@ -257,6 +261,7 @@ HistoryRow.propTypes = {
sourceTitle: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
downloadId: PropTypes.string,
isMarkingAsFailed: PropTypes.bool,
markAsFailedError: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@@ -21,6 +22,7 @@ import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import QueueFilterModal from './QueueFilterModal';
import QueueOptionsConnector from './QueueOptionsConnector';
import QueueRowConnector from './QueueRowConnector';
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
@@ -153,11 +155,16 @@ class Queue extends Component {
isMoviesPopulated,
moviesError,
columns,
selectedFilterKey,
filters,
customFilters,
count,
totalRecords,
isGrabbing,
isRemoving,
isRefreshMonitoredDownloadsExecuting,
onRefreshPress,
onFilterSelect,
...otherProps
} = this.props;
@@ -220,6 +227,15 @@ class Queue extends Component {
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
<FilterMenu
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={customFilters}
filterModalConnectorComponent={QueueFilterModal}
onFilterSelect={onFilterSelect}
/>
</PageToolbarSection>
</PageToolbar>
@@ -233,7 +249,7 @@ class Queue extends Component {
{
!isRefreshing && hasError ?
<Alert kind={kinds.DANGER}>
{translate('FailedToLoadQueue')}
{translate('QueueLoadError')}
</Alert> :
null
}
@@ -241,7 +257,11 @@ class Queue extends Component {
{
isAllPopulated && !hasError && !items.length ?
<Alert kind={kinds.INFO}>
{translate('QueueIsEmpty')}
{
selectedFilterKey !== 'all' && count > 0 ?
translate('QueueFilterHasNoItems') :
translate('QueueIsEmpty')
}
</Alert> :
null
}
@@ -325,13 +345,22 @@ Queue.propTypes = {
moviesError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
selectedFilterKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
customFilters: PropTypes.arrayOf(PropTypes.object).isRequired,
count: PropTypes.number.isRequired,
totalRecords: PropTypes.number,
isGrabbing: PropTypes.bool.isRequired,
isRemoving: PropTypes.bool.isRequired,
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
onRefreshPress: PropTypes.func.isRequired,
onGrabSelectedPress: PropTypes.func.isRequired,
onRemoveSelectedPress: PropTypes.func.isRequired
onRemoveSelectedPress: PropTypes.func.isRequired,
onFilterSelect: PropTypes.func.isRequired
};
Queue.defaultProps = {
count: 0
};
export default Queue;

View File

@@ -6,6 +6,7 @@ import * as commandNames from 'Commands/commandNames';
import withCurrentPage from 'Components/withCurrentPage';
import { executeCommand } from 'Store/Actions/commandActions';
import * as queueActions from 'Store/Actions/queueActions';
import { createCustomFiltersSelector } from 'Store/Selectors/createClientSideCollectionSelector';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
import Queue from './Queue';
@@ -15,12 +16,16 @@ function createMapStateToProps() {
(state) => state.movies,
(state) => state.queue.options,
(state) => state.queue.paged,
(state) => state.queue.status.item,
createCustomFiltersSelector('queue'),
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
(movies, options, queue, isRefreshMonitoredDownloadsExecuting) => {
(movies, options, queue, status, customFilters, isRefreshMonitoredDownloadsExecuting) => {
return {
count: options.includeUnknownMovieItems ? status.totalCount : status.count,
isMoviesFetching: movies.isFetching,
isMoviesPopulated: movies.isPopulated,
moviesError: movies.error,
customFilters,
isRefreshMonitoredDownloadsExecuting,
...options,
...queue
@@ -106,6 +111,10 @@ class QueueConnector extends Component {
this.props.setQueueSort({ sortKey });
};
onFilterSelect = (selectedFilterKey) => {
this.props.setQueueFilter({ selectedFilterKey });
};
onTableOptionChange = (payload) => {
this.props.setQueueTableOption(payload);
@@ -140,6 +149,7 @@ class QueueConnector extends Component {
onLastPagePress={this.onLastPagePress}
onPageSelect={this.onPageSelect}
onSortPress={this.onSortPress}
onFilterSelect={this.onFilterSelect}
onTableOptionChange={this.onTableOptionChange}
onRefreshPress={this.onRefreshPress}
onGrabSelectedPress={this.onGrabSelectedPress}
@@ -162,6 +172,7 @@ QueueConnector.propTypes = {
gotoQueueLastPage: PropTypes.func.isRequired,
gotoQueuePage: PropTypes.func.isRequired,
setQueueSort: PropTypes.func.isRequired,
setQueueFilter: PropTypes.func.isRequired,
setQueueTableOption: PropTypes.func.isRequired,
clearQueue: PropTypes.func.isRequired,
grabQueueItems: PropTypes.func.isRequired,

View File

@@ -0,0 +1,5 @@
.progressBarContainer {
display: flex;
justify-content: center;
width: 100%;
}

View File

@@ -0,0 +1,7 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'progressBarContainer': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -1,116 +1,71 @@
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import { icons, kinds } from 'Helpers/Props';
import Popover from 'Components/Tooltip/Popover';
import { icons, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import QueueStatus from './QueueStatus';
import styles from './QueueDetails.css';
function QueueDetails(props) {
const {
title,
size,
sizeleft,
estimatedCompletionTime,
status,
trackedDownloadState,
trackedDownloadStatus,
statusMessages,
errorMessage,
progressBar
} = props;
const progress = size ? (100 - sizeleft / size * 100) : 0;
const isDownloading = status === 'downloading';
const isPaused = status === 'paused';
const hasWarning = trackedDownloadStatus === 'warning';
const hasError = trackedDownloadStatus === 'error';
if (status === 'pending') {
return (
<Icon
name={icons.PENDING}
title={translate('ReleaseWillBeProcessedInterp', [moment(estimatedCompletionTime).fromNow()])}
/>
);
}
if (
(isDownloading || isPaused) &&
!hasWarning &&
!hasError
) {
const state = isPaused ? translate('Paused') : translate('Downloading');
if (status === 'completed') {
if (errorMessage) {
if (progress < 5) {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.DANGER}
title={translate('ImportFailedInterp', { errorMessage })}
name={icons.DOWNLOADING}
title={`${state} - ${progress.toFixed(1)}% ${title}`}
/>
);
}
if (trackedDownloadStatus === 'warning') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.WARNING}
title={translate('UnableToImportCheckLogs')}
/>
);
}
if (trackedDownloadState === 'importPending') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.PURPLE}
title={`${translate('Downloaded')} - ${translate('WaitingToImport')}`}
/>
);
}
if (trackedDownloadState === 'importing') {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.PURPLE}
title={`${translate('Downloaded')} - ${translate('Importing')}`}
/>
);
}
}
if (errorMessage) {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title={translate('DownloadFailedInterp', { errorMessage })}
<Popover
className={styles.progressBarContainer}
anchor={progressBar}
title={`${state} - ${progress.toFixed(1)}%`}
body={
<div>{title}</div>
}
position={tooltipPositions.LEFT}
/>
);
}
if (status === 'failed') {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title={translate('DownloadFailedCheckDownloadClientForMoreDetails')}
/>
);
}
if (status === 'warning') {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.WARNING}
title={translate('DownloadWarningCheckDownloadClientForMoreDetails')}
/>
);
}
if (progress < 5) {
return (
<Icon
name={icons.DOWNLOADING}
title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}
/>
);
}
return progressBar;
return (
<QueueStatus
sourceTitle={title}
status={status}
trackedDownloadStatus={trackedDownloadStatus}
trackedDownloadState={trackedDownloadState}
statusMessages={statusMessages}
errorMessage={errorMessage}
position={tooltipPositions.LEFT}
/>
);
}
QueueDetails.propTypes = {
@@ -121,8 +76,14 @@ QueueDetails.propTypes = {
status: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
statusMessages: PropTypes.arrayOf(PropTypes.object),
errorMessage: PropTypes.string,
progressBar: PropTypes.node.isRequired
};
QueueDetails.defaultProps = {
trackedDownloadStatus: 'ok',
trackedDownloadState: 'downloading'
};
export default QueueDetails;

View File

@@ -0,0 +1,54 @@
import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import FilterModal from 'Components/Filter/FilterModal';
import { setQueueFilter } from 'Store/Actions/queueActions';
function createQueueSelector() {
return createSelector(
(state: AppState) => state.queue.paged.items,
(queueItems) => {
return queueItems;
}
);
}
function createFilterBuilderPropsSelector() {
return createSelector(
(state: AppState) => state.queue.paged.filterBuilderProps,
(filterBuilderProps) => {
return filterBuilderProps;
}
);
}
interface QueueFilterModalProps {
isOpen: boolean;
}
export default function QueueFilterModal(props: QueueFilterModalProps) {
const sectionItems = useSelector(createQueueSelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'queue';
const dispatch = useDispatch();
const dispatchSetFilter = useCallback(
(payload: unknown) => {
dispatch(setQueueFilter(payload));
},
[dispatch]
);
return (
<FilterModal
// TODO: Don't spread all the props
{...props}
sectionItems={sectionItems}
filterBuilderProps={filterBuilderProps}
customFilterType={customFilterType}
dispatchSetFilter={dispatchSetFilter}
/>
);
}

View File

@@ -61,7 +61,7 @@ class QueueOptions extends Component {
type={inputTypes.CHECK}
name="includeUnknownMovieItems"
value={includeUnknownMovieItems}
helpText={translate('IncludeUnknownMovieItemsHelpText')}
helpText={translate('ShowUnknownMovieItemsHelpText')}
onChange={this.onOptionChange}
/>
</FormGroup>

View File

@@ -0,0 +1,3 @@
.noMessages {
margin-bottom: 10px;
}

View File

@@ -0,0 +1,7 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'noMessages': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -0,0 +1,162 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './QueueStatus.css';
function getDetailedPopoverBody(statusMessages) {
return (
<div>
{
statusMessages.map(({ title, messages }) => {
return (
<div
key={title}
className={messages.length ? undefined: styles.noMessages}
>
{title}
<ul>
{
messages.map((message) => {
return (
<li key={message}>
{message}
</li>
);
})
}
</ul>
</div>
);
})
}
</div>
);
}
function QueueStatus(props) {
const {
sourceTitle,
status,
trackedDownloadStatus,
trackedDownloadState,
statusMessages,
errorMessage,
position,
canFlip
} = props;
const hasWarning = trackedDownloadStatus === 'warning';
const hasError = trackedDownloadStatus === 'error';
// status === 'downloading'
let iconName = icons.DOWNLOADING;
let iconKind = kinds.DEFAULT;
let title = translate('Downloading');
if (status === 'paused') {
iconName = icons.PAUSED;
title = translate('Paused');
}
if (status === 'queued') {
iconName = icons.QUEUED;
title = translate('Queued');
}
if (status === 'completed') {
iconName = icons.DOWNLOADED;
title = translate('Downloaded');
if (trackedDownloadState === 'importPending') {
title += ` - ${translate('WaitingToImport')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'importing') {
title += ` - ${translate('Importing')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'failedPending') {
title += ` - ${translate('WaitingToProcess')}`;
iconKind = kinds.DANGER;
}
}
if (hasWarning) {
iconKind = kinds.WARNING;
}
if (status === 'delay') {
iconName = icons.PENDING;
title = translate('Pending');
}
if (status === 'downloadClientUnavailable') {
iconName = icons.PENDING;
iconKind = kinds.WARNING;
title = translate('PendingDownloadClientUnavailable');
}
if (status === 'failed') {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = translate('DownloadFailed');
}
if (status === 'warning') {
iconName = icons.DOWNLOADING;
iconKind = kinds.WARNING;
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
title = translate('DownloadWarning', { warningMessage });
}
if (hasError) {
if (status === 'completed') {
iconName = icons.DOWNLOAD;
iconKind = kinds.DANGER;
title = translate('ImportFailed', { sourceTitle });
} else {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = translate('DownloadFailed');
}
}
return (
<Popover
anchor={
<Icon
name={iconName}
kind={iconKind}
/>
}
title={title}
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
position={position}
canFlip={canFlip}
/>
);
}
QueueStatus.propTypes = {
sourceTitle: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
statusMessages: PropTypes.arrayOf(PropTypes.object),
errorMessage: PropTypes.string,
position: PropTypes.oneOf(tooltipPositions.all).isRequired,
canFlip: PropTypes.bool.isRequired
};
QueueStatus.defaultProps = {
trackedDownloadStatus: 'ok',
trackedDownloadState: 'downloading',
canFlip: false
};
export default QueueStatus;

View File

@@ -1,39 +1,10 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import { tooltipPositions } from 'Helpers/Props';
import QueueStatus from './QueueStatus';
import styles from './QueueStatusCell.css';
function getDetailedPopoverBody(statusMessages) {
return (
<div>
{
statusMessages.map(({ title, messages }) => {
return (
<div key={title}>
{title}
<ul>
{
messages.map((message) => {
return (
<li key={message}>
{message}
</li>
);
})
}
</ul>
</div>
);
})
}
</div>
);
}
function QueueStatusCell(props) {
const {
sourceTitle,
@@ -44,97 +15,16 @@ function QueueStatusCell(props) {
errorMessage
} = props;
const hasWarning = trackedDownloadStatus === 'warning';
const hasError = trackedDownloadStatus === 'error';
// status === 'downloading'
let iconName = icons.DOWNLOADING;
let iconKind = kinds.DEFAULT;
let title = translate('Downloading');
if (status === 'paused') {
iconName = icons.PAUSED;
title = translate('Paused');
}
if (status === 'queued') {
iconName = icons.QUEUED;
title = translate('Queued');
}
if (status === 'completed') {
iconName = icons.DOWNLOADED;
title = translate('Downloaded');
if (trackedDownloadState === 'importPending') {
title += ` - ${translate('WaitingToImport')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'importing') {
title += ` - ${translate('Importing')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'failedPending') {
title += ` - ${translate('WaitingToProcess')}`;
iconKind = kinds.DANGER;
}
}
if (hasWarning) {
iconKind = kinds.WARNING;
}
if (status === 'delay') {
iconName = icons.PENDING;
title = translate('Pending');
}
if (status === 'DownloadClientUnavailable') {
iconName = icons.PENDING;
iconKind = kinds.WARNING;
title = `${translate('Pending')} - ${translate('DownloadClientUnavailable')}`;
}
if (status === 'failed') {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = translate('DownloadFailed');
}
if (status === 'warning') {
iconName = icons.DOWNLOADING;
iconKind = kinds.WARNING;
const warningMessage = errorMessage || translate('CheckDownloadClientForDetails');
title = translate('DownloadWarning', { warningMessage });
}
if (hasError) {
if (status === 'completed') {
iconName = icons.DOWNLOAD;
iconKind = kinds.DANGER;
title = translate('ImportFailed', { sourceTitle });
} else {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = translate('DownloadFailed');
}
}
return (
<TableRowCell className={styles.status}>
<Popover
anchor={
<Icon
name={iconName}
kind={iconKind}
/>
}
title={title}
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
<QueueStatus
sourceTitle={sourceTitle}
status={status}
trackedDownloadStatus={trackedDownloadStatus}
trackedDownloadState={trackedDownloadState}
statusMessages={statusMessages}
errorMessage={errorMessage}
position={tooltipPositions.RIGHT}
canFlip={false}
/>
</TableRowCell>
);
@@ -150,8 +40,8 @@ QueueStatusCell.propTypes = {
};
QueueStatusCell.defaultProps = {
trackedDownloadStatus: translate('Ok'),
trackedDownloadState: translate('Downloading')
trackedDownloadStatus: 'ok',
trackedDownloadState: 'downloading'
};
export default QueueStatusCell;

View File

@@ -115,11 +115,12 @@ class RemoveQueueItemModal extends Component {
<FormGroup>
<FormLabel>{translate('BlocklistRelease')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blocklist"
value={blocklist}
helpText={translate('BlocklistHelpText')}
helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange}
/>
</FormGroup>
@@ -149,7 +150,7 @@ class RemoveQueueItemModal extends Component {
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
Remove
{translate('Remove')}
</Button>
</ModalFooter>
</ModalContent>

View File

@@ -123,7 +123,7 @@ class RemoveQueueItemsModal extends Component {
type={inputTypes.CHECK}
name="blocklist"
value={blocklist}
helpText={translate('BlocklistHelpText')}
helpText={translate('BlocklistReleaseHelpText')}
onChange={this.onBlocklistChange}
/>
</FormGroup>

View File

@@ -1,6 +1,9 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Tooltip from 'Components/Tooltip/Tooltip';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import formatTime from 'Utilities/Date/formatTime';
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
@@ -25,11 +28,13 @@ function TimeleftCell(props) {
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
return (
<TableRowCell
className={styles.timeleft}
title={translate('DelayingDownloadUntilInterp', [date, time])}
>
-
<TableRowCell className={styles.timeleft}>
<Tooltip
anchor={<Icon name={icons.INFO} />}
tooltip={translate('DelayingDownloadUntil', { date, time })}
kind={kinds.INVERSE}
position={tooltipPositions.TOP}
/>
</TableRowCell>
);
}
@@ -39,11 +44,13 @@ function TimeleftCell(props) {
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
return (
<TableRowCell
className={styles.timeleft}
title={translate('RetryingDownloadInterp', [date, time])}
>
-
<TableRowCell className={styles.timeleft}>
<Tooltip
anchor={<Icon name={icons.INFO} />}
tooltip={translate('RetryingDownloadOn', { date, time })}
kind={kinds.INVERSE}
position={tooltipPositions.TOP}
/>
</TableRowCell>
);
}

View File

@@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import TextInput from 'Components/Form/TextInput';
import Icon from 'Components/Icon';
import Button from 'Components/Link/Button';
@@ -130,7 +131,12 @@ class AddNewMovie extends Component {
<div className={styles.helpText}>
{translate('FailedLoadingSearchResults')}
</div>
<div>{getErrorMessage(error)}</div>
<Alert kind={kinds.WARNING}>{getErrorMessage(error)}</Alert>
<div>
<Link to="https://wiki.servarr.com/radarr/troubleshooting#invalid-response-received-from-tmdb">
{translate('WhySearchesCouldBeFailing')}
</Link>
</div>
</div> : null
}

View File

@@ -66,6 +66,7 @@ class AddNewMovieSearchResult extends Component {
ratings,
folder,
images,
existingMovieId,
isExistingMovie,
isExclusionMovie,
isSmallScreen,
@@ -74,8 +75,7 @@ class AddNewMovieSearchResult extends Component {
monitored,
hasFile,
isAvailable,
queueStatus,
queueState,
movieFile,
runtime,
movieRuntimeFormat,
certification
@@ -120,13 +120,13 @@ class AddNewMovieSearchResult extends Component {
{
isExistingMovie &&
<MovieIndexProgressBar
movieId={existingMovieId}
movieFile={movieFile}
monitored={monitored}
hasFile={hasFile}
status={status}
width={posterWidth}
detailedProgressBar={true}
queueStatus={queueStatus}
queueState={queueState}
isAvailable={isAvailable}
/>
}
@@ -278,6 +278,7 @@ AddNewMovieSearchResult.propTypes = {
ratings: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
existingMovieId: PropTypes.number,
isExistingMovie: PropTypes.bool.isRequired,
isExclusionMovie: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
@@ -286,9 +287,8 @@ AddNewMovieSearchResult.propTypes = {
monitored: PropTypes.bool.isRequired,
hasFile: PropTypes.bool.isRequired,
isAvailable: PropTypes.bool.isRequired,
movieFile: PropTypes.object,
colorImpairedMode: PropTypes.bool,
queueStatus: PropTypes.string,
queueState: PropTypes.string,
runtime: PropTypes.number.isRequired,
movieRuntimeFormat: PropTypes.string.isRequired,
certification: PropTypes.string

View File

@@ -10,17 +10,15 @@ function createMapStateToProps() {
createExistingMovieSelector(),
createExclusionMovieSelector(),
createDimensionsSelector(),
(state) => state.queue.details.items,
(state, { internalId }) => internalId,
(isExistingMovie, isExclusionMovie, dimensions, queueItems, internalId) => {
const firstQueueItem = queueItems.find((q) => q.movieId === internalId && internalId > 0);
(state) => state.settings.ui.item.movieRuntimeFormat,
(isExistingMovie, isExclusionMovie, dimensions, internalId, movieRuntimeFormat) => {
return {
existingMovieId: internalId,
isExistingMovie,
isExclusionMovie,
isSmallScreen: dimensions.isSmallScreen,
queueStatus: firstQueueItem ? firstQueueItem.status : null,
queueState: firstQueueItem ? firstQueueItem.trackedDownloadState : null
movieRuntimeFormat
};
}
);

View File

@@ -5,12 +5,13 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { addRootFolder, deleteRootFolder, fetchRootFolders } from 'Store/Actions/rootFolderActions';
import createRootFoldersSelector from 'Store/Selectors/createRootFoldersSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import ImportMovieSelectFolder from './ImportMovieSelectFolder';
function createMapStateToProps() {
return createSelector(
(state) => state.rootFolders,
createRootFoldersSelector(),
createSystemStatusSelector(),
(rootFolders, systemStatus) => {
return {

View File

@@ -1,6 +1,7 @@
.version {
margin: 0 3px;
font-weight: bold;
font-family: var(--defaultFontFamily);
}
.maintenance {

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
@@ -64,20 +65,20 @@ function AppUpdatedModalContent(props) {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{translate('RadarrUpdated')}
{translate('AppUpdated')}
</ModalHeader>
<ModalBody>
<div dangerouslySetInnerHTML={{ __html: translate('VersionUpdateText', [`<span className=${styles.version}>${version}</span>`]) }} />
<div>
<InlineMarkdown data={translate('AppUpdatedVersion', { version })} blockClassName={styles.version} />
</div>
{
isPopulated && !error && !!update &&
<div>
{
!update.changes &&
<div className={styles.maintenance}>
{translate('MaintenanceRelease')}
</div>
<div className={styles.maintenance}>{translate('MaintenanceRelease')}</div>
}
{

View File

@@ -28,11 +28,11 @@ function ConnectionLostModal(props) {
<ModalBody>
<div>
{translate('ConnectionLostMessage')}
{translate('ConnectionLostToBackend')}
</div>
<div className={styles.automatic}>
{translate('ConnectionLostAutomaticMessage')}
{translate('ConnectionLostReconnect')}
</div>
</ModalBody>
<ModalFooter>

View File

@@ -1,4 +1,5 @@
import SortDirection from 'Helpers/Props/SortDirection';
import { FilterBuilderProp } from './AppState';
export interface Error {
responseJSON: {
@@ -20,6 +21,10 @@ export interface PagedAppSectionState {
pageSize: number;
}
export interface AppSectionFilterState<T> {
filterBuilderProps: FilterBuilderProp<T>[];
}
export interface AppSectionSchemaState<T> {
isSchemaFetching: boolean;
isSchemaPopulated: boolean;

View File

@@ -1,6 +1,7 @@
import InteractiveImportAppState from 'App/State/InteractiveImportAppState';
import CalendarAppState from './CalendarAppState';
import CommandAppState from './CommandAppState';
import HistoryAppState from './HistoryAppState';
import MovieCollectionAppState from './MovieCollectionAppState';
import MovieFilesAppState from './MovieFilesAppState';
import MoviesAppState, { MovieIndexAppState } from './MoviesAppState';
@@ -46,6 +47,7 @@ export interface CustomFilter {
interface AppState {
calendar: CalendarAppState;
commands: CommandAppState;
history: HistoryAppState;
interactiveImport: InteractiveImportAppState;
movieCollections: MovieCollectionAppState;
movieFiles: MovieFilesAppState;

View File

@@ -1,9 +1,10 @@
import AppSectionState from 'App/State/AppSectionState';
import AppSectionState, {
AppSectionFilterState,
} from 'App/State/AppSectionState';
import Movie from 'Movie/Movie';
import { FilterBuilderProp } from './AppState';
interface CalendarAppState extends AppSectionState<Movie> {
filterBuilderProps: FilterBuilderProp<Movie>[];
}
interface CalendarAppState
extends AppSectionState<Movie>,
AppSectionFilterState<Movie> {}
export default CalendarAppState;

View File

@@ -0,0 +1,10 @@
import AppSectionState, {
AppSectionFilterState,
} from 'App/State/AppSectionState';
import History from 'typings/History';
interface HistoryAppState
extends AppSectionState<History>,
AppSectionFilterState<History> {}
export default HistoryAppState;

View File

@@ -1,7 +1,7 @@
import AppSectionState from 'App/State/AppSectionState';
import RecentFolder from 'InteractiveImport/Folder/RecentFolder';
import ImportMode from '../../InteractiveImport/ImportMode';
import InteractiveImport from '../../InteractiveImport/InteractiveImport';
import ImportMode from 'InteractiveImport/ImportMode';
import InteractiveImport from 'InteractiveImport/InteractiveImport';
interface InteractiveImportAppState extends AppSectionState<InteractiveImport> {
originalItems: InteractiveImport[];

View File

@@ -1,6 +1,8 @@
import AppSectionState from 'App/State/AppSectionState';
import MovieCollection from 'typings/MovieCollection';
type MovieCollectionAppState = AppSectionState<MovieCollection>;
interface MovieCollectionAppState extends AppSectionState<MovieCollection> {
itemMap: Record<number, number>;
}
export default MovieCollectionAppState;

View File

@@ -1,41 +1,17 @@
import ModelBase from 'App/ModelBase';
import Language from 'Language/Language';
import { QualityModel } from 'Quality/Quality';
import CustomFormat from 'typings/CustomFormat';
import AppSectionState, { AppSectionItemState, Error } from './AppSectionState';
export interface StatusMessage {
title: string;
messages: string[];
}
export interface Queue extends ModelBase {
languages: Language[];
quality: QualityModel;
customFormats: CustomFormat[];
size: number;
title: string;
sizeleft: number;
timeleft: string;
estimatedCompletionTime: string;
status: string;
trackedDownloadStatus: string;
trackedDownloadState: string;
statusMessages: StatusMessage[];
errorMessage: string;
downloadId: string;
protocol: string;
downloadClient: string;
outputPath: string;
movieHasFile: boolean;
movieId?: number;
}
import Queue from 'typings/Queue';
import AppSectionState, {
AppSectionFilterState,
AppSectionItemState,
Error,
} from './AppSectionState';
export interface QueueDetailsAppState extends AppSectionState<Queue> {
params: unknown;
}
export interface QueuePagedAppState extends AppSectionState<Queue> {
export interface QueuePagedAppState
extends AppSectionState<Queue>,
AppSectionFilterState<Queue> {
isGrabbing: boolean;
grabError: Error;
isRemoving: boolean;

View File

@@ -8,6 +8,7 @@ import Language from 'Language/Language';
import DownloadClient from 'typings/DownloadClient';
import ImportList from 'typings/ImportList';
import Indexer from 'typings/Indexer';
import IndexerFlag from 'typings/IndexerFlag';
import Notification from 'typings/Notification';
import QualityProfile from 'typings/QualityProfile';
import { UiSettings } from 'typings/UiSettings';
@@ -35,12 +36,14 @@ export interface QualityProfilesAppState
extends AppSectionState<QualityProfile>,
AppSectionSchemaState<QualityProfile> {}
export type IndexerFlagSettingsAppState = AppSectionState<IndexerFlag>;
export type LanguageSettingsAppState = AppSectionState<Language>;
export type UiSettingsAppState = AppSectionItemState<UiSettings>;
interface SettingsAppState {
downloadClients: DownloadClientAppState;
importLists: ImportListAppState;
indexerFlags: IndexerFlagSettingsAppState;
indexers: IndexerAppState;
languages: LanguageSettingsAppState;
notifications: NotificationAppState;

View File

@@ -42,9 +42,9 @@ function Agenda(props) {
<div className={styles.agenda}>
{
items.map((item, index) => {
const momentDate = moment(item.inCinemas);
const momentDate = moment(item.sortDate);
const showDate = index === 0 ||
!moment(items[index - 1].inCinemas).isSame(momentDate, 'day');
!moment(items[index - 1].sortDate).isSame(momentDate, 'day');
return (
<AgendaEventConnector

View File

@@ -1,17 +1,28 @@
.event {
display: flex;
overflow-x: hidden;
position: relative;
padding: 5px;
border-bottom: 1px solid var(--borderColor);
font-size: $defaultFontSize;
}
.underlay {
@add-mixin cover;
&:hover {
background-color: var(--tableRowHoverBackgroundColor);
}
}
.link {
composes: link from '~Calendar/Events/CalendarEvent.css';
.overlay {
@add-mixin linkOverlay;
position: relative;
display: flex;
overflow-x: hidden;
font-size: $defaultFontSize;
&:global(.colorImpaired) {
border-left-width: 5px;
}
}
.eventWrapper {
@@ -44,6 +55,8 @@
.statusIcon {
margin-left: 3px;
cursor: default;
pointer-events: all;
}
/*
@@ -75,7 +88,7 @@
}
@media only screen and (max-width: $breakpointSmall) {
.event {
.overlay {
flex-direction: column;
}
@@ -95,6 +108,7 @@
}
}
.dateIcon {
.releaseIcon {
margin-right: 20px;
width: 25px;
}

View File

@@ -3,18 +3,19 @@
interface CssExports {
'continuing': string;
'date': string;
'dateIcon': string;
'downloaded': string;
'event': string;
'eventWrapper': string;
'genres': string;
'link': string;
'missingMonitored': string;
'missingUnmonitored': string;
'movieTitle': string;
'overlay': string;
'queue': string;
'releaseIcon': string;
'statusIcon': string;
'time': string;
'underlay': string;
'unmonitored': string;
}
export const cssExports: CssExports;

View File

@@ -87,25 +87,24 @@ class AgendaEvent extends Component {
const link = `/movie/${titleSlug}`;
return (
<div>
<div className={styles.event}>
<Link
className={classNames(
styles.event,
styles.link
)}
className={styles.underlay}
to={link}
>
<div className={styles.dateIcon}>
/>
<div className={styles.overlay}>
<div className={styles.date}>
{showDate ? startTime.format(longDateFormat) : null}
</div>
<div className={styles.releaseIcon}>
<Icon
name={releaseIcon}
kind={kinds.DEFAULT}
/>
</div>
<div className={styles.date}>
{(showDate) ? startTime.format(longDateFormat) : null}
</div>
<div
className={classNames(
styles.eventWrapper,
@@ -143,9 +142,7 @@ class AgendaEvent extends Component {
}
{
showCutoffUnmetIcon &&
!!movieFile &&
movieFile.qualityCutoffNotMet &&
showCutoffUnmetIcon && !!movieFile && movieFile.qualityCutoffNotMet &&
<Icon
className={styles.statusIcon}
name={icons.MOVIE_FILE}
@@ -154,7 +151,7 @@ class AgendaEvent extends Component {
/>
}
</div>
</Link>
</div>
</div>
);
}

View File

@@ -23,13 +23,11 @@ function createFilterBuilderPropsSelector() {
);
}
interface SeriesIndexFilterModalProps {
interface CalendarFilterModalProps {
isOpen: boolean;
}
export default function CalendarFilterModal(
props: SeriesIndexFilterModalProps
) {
export default function CalendarFilterModal(props: CalendarFilterModalProps) {
const sectionItems = useSelector(createCalendarSelector());
const filterBuilderProps = useSelector(createFilterBuilderPropsSelector());
const customFilterType = 'calendar';

View File

@@ -1,64 +0,0 @@
import classNames from 'classnames';
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import * as calendarViews from 'Calendar/calendarViews';
import CalendarEventConnector from 'Calendar/Events/CalendarEventConnector';
import styles from './CalendarDay.css';
function CalendarDay(props) {
const {
date,
time,
isTodaysDate,
events,
view,
onEventModalOpenToggle
} = props;
return (
<div className={classNames(
styles.day,
view === calendarViews.DAY && styles.isSingleDay
)}
>
{
view === calendarViews.MONTH &&
<div className={classNames(
styles.dayOfMonth,
isTodaysDate && styles.isToday,
!moment(date).isSame(moment(time), 'month') && styles.isDifferentMonth
)}
>
{moment(date).date()}
</div>
}
<div>
{
events.map((event) => {
return (
<CalendarEventConnector
key={event.id}
movieId={event.id}
date={date}
{...event}
onEventModalOpenToggle={onEventModalOpenToggle}
/>
);
})
}
</div>
</div>
);
}
CalendarDay.propTypes = {
date: PropTypes.string.isRequired,
time: PropTypes.string.isRequired,
isTodaysDate: PropTypes.bool.isRequired,
events: PropTypes.arrayOf(PropTypes.object).isRequired,
view: PropTypes.string.isRequired,
onEventModalOpenToggle: PropTypes.func.isRequired
};
export default CalendarDay;

View File

@@ -0,0 +1,67 @@
import classNames from 'classnames';
import moment from 'moment';
import React from 'react';
import * as calendarViews from 'Calendar/calendarViews';
import CalendarEventConnector from 'Calendar/Events/CalendarEventConnector';
import CalendarEvent from 'typings/CalendarEvent';
import styles from './CalendarDay.css';
interface CalendarDayProps {
date: string;
time: string;
isTodaysDate: boolean;
events: CalendarEvent[];
view: string;
onEventModalOpenToggle(...args: unknown[]): unknown;
}
function CalendarDay(props: CalendarDayProps) {
const { date, time, isTodaysDate, events, view, onEventModalOpenToggle } =
props;
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (isTodaysDate && view === calendarViews.MONTH && ref.current) {
ref.current.scrollIntoView();
}
}, [time, isTodaysDate, view]);
return (
<div
ref={ref}
className={classNames(
styles.day,
view === calendarViews.DAY && styles.isSingleDay
)}
>
{view === calendarViews.MONTH && (
<div
className={classNames(
styles.dayOfMonth,
isTodaysDate && styles.isToday,
!moment(date).isSame(moment(time), 'month') &&
styles.isDifferentMonth
)}
>
{moment(date).date()}
</div>
)}
<div>
{events.map((event) => {
return (
<CalendarEventConnector
key={event.id}
{...event}
movieId={event.id}
date={date as string}
onEventModalOpenToggle={onEventModalOpenToggle}
/>
);
})}
</div>
</div>
);
}
export default CalendarDay;

View File

@@ -1,9 +1,22 @@
$fullColorGradient: rgba(244, 245, 246, 0.2);
.event {
overflow-x: hidden;
position: relative;
margin: 4px 2px;
padding: 5px;
border-bottom: 1px solid var(--calendarBorderColor);
border-left: 4px solid var(--calendarBorderColor);
}
.underlay {
@add-mixin cover;
}
.overlay {
@add-mixin linkOverlay;
position: relative;
overflow-x: hidden;
font-size: 12px;
&:global(.colorImpaired) {
@@ -11,18 +24,6 @@
}
}
.link {
composes: link from '~Components/Link/Link.css';
display: block;
color: var(--defaultColor);
&:hover {
color: var(--defaultColor);
text-decoration: none;
}
}
.info,
.movieInfo {
display: flex;
@@ -44,8 +45,15 @@
font-size: $defaultFontSize;
}
.statusContainer {
display: flex;
align-items: center;
}
.statusIcon {
margin-left: 3px;
cursor: default;
pointer-events: all;
}
/*
@@ -55,35 +63,84 @@
.downloaded {
border-left-color: var(--successColor) !important;
&:global(.fullColor) {
background-color: rgba(39, 194, 76, 0.4) !important;
}
&:global(.colorImpaired) {
border-left-color: color(var(--successColor), saturation(+15%)) !important;
border-left-color: color(#27c24c saturation(+15%)) !important;
}
}
.queue {
border-left-color: var(--purple) !important;
&:global(.fullColor) {
background-color: rgba(122, 67, 182, 0.4) !important;
}
}
.unmonitored {
border-left-color: var(--gray) !important;
&:global(.fullColor) {
background-color: rgba(173, 173, 173, 0.5) !important;
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(45deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
}
.missingUnmonitored {
border-left-color: var(--warningColor) !important;
&:global(.fullColor) {
background-color: rgba(255, 165, 0, 0.6) !important;
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(90deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
}
.missingMonitored {
border-left-color: var(--dangerColor) !important;
&:global(.fullColor) {
background-color: rgba(240, 80, 80, 0.6) !important;
}
&:global(.colorImpaired) {
border-left-color: color(#f05050 saturation(+15%)) !important;
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(90deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
}
.continuing {
border-left-color: var(--primaryColor) !important;
&:global(.fullColor) {
background-color: rgba(93, 156, 236, 0.4) !important;
}
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
&:global(.fullColor.colorImpaired) {
background: repeating-linear-gradient(90deg, $fullColorGradient, $fullColorGradient 5px, transparent 5px, transparent 10px);
}
}

View File

@@ -6,13 +6,15 @@ interface CssExports {
'event': string;
'genres': string;
'info': string;
'link': string;
'missingMonitored': string;
'missingUnmonitored': string;
'movieInfo': string;
'movieTitle': string;
'overlay': string;
'queue': string;
'statusContainer': string;
'statusIcon': string;
'underlay': string;
'unmonitored': string;
}
export const cssExports: CssExports;

View File

@@ -25,6 +25,7 @@ class CalendarEvent extends Component {
title,
titleSlug,
genres,
date,
monitored,
certification,
hasFile,
@@ -32,8 +33,8 @@ class CalendarEvent extends Component {
queueItem,
showMovieInformation,
showCutoffUnmetIcon,
colorImpairedMode,
date
fullColorEvents,
colorImpairedMode
} = this.props;
const isDownloading = !!(queueItem || grabbed);
@@ -56,64 +57,71 @@ class CalendarEvent extends Component {
}
return (
<div>
<div
className={classNames(
styles.event,
styles[statusStyle],
colorImpairedMode && 'colorImpaired',
fullColorEvents && 'fullColor'
)}
>
<Link
className={classNames(
styles.event,
styles.link,
styles[statusStyle],
colorImpairedMode && 'colorImpaired'
)}
// component="div"
className={styles.underlay}
to={link}
>
/>
<div className={styles.overlay} >
<div className={styles.info}>
<div className={styles.movieTitle}>
{title}
</div>
{
!!queueItem &&
<span className={styles.statusIcon}>
<CalendarEventQueueDetails
{...queueItem}
/>
</span>
}
<div className={styles.statusContainer}>
{
queueItem ?
<span className={styles.statusIcon}>
<CalendarEventQueueDetails
{...queueItem}
/>
</span> :
null
}
{
!queueItem && grabbed &&
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title={translate('MovieIsDownloading')}
/>
}
{
!queueItem && grabbed ?
<Icon
className={styles.statusIcon}
name={icons.DOWNLOADING}
title={translate('MovieIsDownloading')}
/> :
null
}
{
showCutoffUnmetIcon &&
!!movieFile &&
movieFile.qualityCutoffNotMet &&
<Icon
className={styles.statusIcon}
name={icons.MOVIE_FILE}
kind={kinds.WARNING}
title={translate('QualityCutoffHasNotBeenMet')}
/>
}
{
showCutoffUnmetIcon && !!movieFile && movieFile.qualityCutoffNotMet ?
<Icon
className={styles.statusIcon}
name={icons.MOVIE_FILE}
kind={kinds.WARNING}
title={translate('QualityCutoffHasNotBeenMet')}
/> :
null
}
</div>
</div>
{
showMovieInformation &&
showMovieInformation ?
<div className={styles.movieInfo}>
<div className={styles.genres}>
{joinedGenres}
</div>
</div>
</div> :
null
}
{
showMovieInformation &&
showMovieInformation ?
<div className={styles.movieInfo}>
<div className={styles.genres}>
{eventType.join(', ')}
@@ -121,10 +129,10 @@ class CalendarEvent extends Component {
<div>
{certification}
</div>
</div>
</div> :
null
}
</Link>
</div>
</div>
);
}
@@ -140,16 +148,18 @@ CalendarEvent.propTypes = {
inCinemas: PropTypes.string,
physicalRelease: PropTypes.string,
digitalRelease: PropTypes.string,
date: PropTypes.string.isRequired,
monitored: PropTypes.bool.isRequired,
certification: PropTypes.string,
hasFile: PropTypes.bool.isRequired,
grabbed: PropTypes.bool,
queueItem: PropTypes.object,
showMovieInformation: PropTypes.bool.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
timeFormat: PropTypes.string.isRequired,
colorImpairedMode: PropTypes.bool.isRequired,
date: PropTypes.string.isRequired
// These props come from the connector, not marked as required to appease TS for now.
showMovieInformation: PropTypes.bool,
showCutoffUnmetIcon: PropTypes.bool,
fullColorEvents: PropTypes.bool,
timeFormat: PropTypes.string,
colorImpairedMode: PropTypes.bool
};
CalendarEvent.defaultProps = {

View File

@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import QueueDetails from 'Activity/Queue/QueueDetails';
import CircularProgressBar from 'Components/CircularProgressBar';
import translate from 'Utilities/String/translate';
function CalendarEventQueueDetails(props) {
const {
@@ -13,6 +12,7 @@ function CalendarEventQueueDetails(props) {
status,
trackedDownloadState,
trackedDownloadStatus,
statusMessages,
errorMessage
} = props;
@@ -27,16 +27,15 @@ function CalendarEventQueueDetails(props) {
status={status}
trackedDownloadState={trackedDownloadState}
trackedDownloadStatus={trackedDownloadStatus}
statusMessages={statusMessages}
errorMessage={errorMessage}
progressBar={
<div title={translate('MovieIsDownloadingInterp', [progress.toFixed(1), title])}>
<CircularProgressBar
progress={progress}
size={20}
strokeWidth={2}
strokeColor={'#7a43b6'}
/>
</div>
<CircularProgressBar
progress={progress}
size={20}
strokeWidth={2}
strokeColor={'#7a43b6'}
/>
}
/>
);
@@ -50,6 +49,7 @@ CalendarEventQueueDetails.propTypes = {
status: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
statusMessages: PropTypes.arrayOf(PropTypes.object),
errorMessage: PropTypes.string
};

View File

@@ -8,18 +8,21 @@ import styles from './Legend.css';
function Legend(props) {
const {
view,
showCutoffUnmetIcon,
fullColorEvents,
colorImpairedMode
} = props;
const iconsToShow = [];
const isAgendaView = view === 'agenda';
if (showCutoffUnmetIcon) {
iconsToShow.push(
<LegendIconItem
name={translate('CutoffUnmet')}
icon={icons.MOVIE_FILE}
kind={kinds.WARNING}
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
tooltip={translate('QualityOrLangCutoffHasNotBeenMet')}
/>
);
@@ -29,45 +32,58 @@ function Legend(props) {
<div className={styles.legend}>
<div>
<LegendItem
style='ended'
status="downloaded"
name={translate('DownloadedAndMonitored')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='availNotMonitored'
status="unmonitored"
name={translate('DownloadedButNotMonitored')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
<div>
<LegendItem
style='missingMonitored'
status="missingMonitored"
name={translate('MissingMonitoredAndConsideredAvailable')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='missingUnmonitored'
status="missingUnmonitored"
name={translate('MissingNotMonitored')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
<div>
<LegendItem
style='queue'
status="queue"
name={translate('Queued')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
<LegendItem
style='continuing'
status="continuing"
name={translate('Unreleased')}
isAgendaView={isAgendaView}
fullColorEvents={fullColorEvents}
colorImpairedMode={colorImpairedMode}
/>
</div>
{
iconsToShow.length > 0 &&
<div>
@@ -79,7 +95,9 @@ function Legend(props) {
}
Legend.propTypes = {
view: PropTypes.string.isRequired,
showCutoffUnmetIcon: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired
};

View File

@@ -6,10 +6,12 @@ import Legend from './Legend';
function createMapStateToProps() {
return createSelector(
(state) => state.calendar.options,
(state) => state.calendar.view,
createUISettingsSelector(),
(calendarOptions, uiSettings) => {
(calendarOptions, view, uiSettings) => {
return {
...calendarOptions,
view,
colorImpairedMode: uiSettings.enableColorImpairedMode
};
}

View File

@@ -8,6 +8,7 @@ function LegendIconItem(props) {
name,
icon,
kind,
darken,
tooltip
} = props;
@@ -19,6 +20,7 @@ function LegendIconItem(props) {
<Icon
className={styles.icon}
name={icon}
darken={darken}
kind={kind}
/>
@@ -31,7 +33,12 @@ LegendIconItem.propTypes = {
name: PropTypes.string.isRequired,
icon: PropTypes.object.isRequired,
kind: PropTypes.string.isRequired,
darken: PropTypes.bool.isRequired,
tooltip: PropTypes.string.isRequired
};
LegendIconItem.defaultProps = {
darken: false
};
export default LegendIconItem;

View File

@@ -1,74 +1,37 @@
.legendItemContainer {
margin-right: 5px;
width: 220px;
}
.legendItem {
display: inline-flex;
margin-top: -1px;
vertical-align: middle;
line-height: 16px;
margin: 3px 0;
margin-right: 6px;
padding-left: 5px;
width: 220px;
border-left-width: 4px;
border-left-style: solid;
cursor: default;
}
.legendItemColor {
margin-right: 8px;
width: 30px;
height: 16px;
border-radius: 4px;
/*
* Status
*/
.downloaded {
composes: downloaded from '~Calendar/Events/CalendarEvent.css';
}
.queue {
composes: legendItemColor;
background-color: var(--queueColor);
composes: queue from '~Calendar/Events/CalendarEvent.css';
}
.continuing {
composes: legendItemColor;
background-color: var(--primaryColor);
}
.availNotMonitored {
composes: legendItemColor;
background-color: var(--darkGray);
}
.ended {
composes: legendItemColor;
background-color: var(--successColor);
}
.missingMonitored {
composes: legendItemColor;
background-color: var(--dangerColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(90deg, color(var(--dangerColor) shade(5%)), color(var(--dangerColor) shade(5%)) 5px, color(var(--dangerColor) shade(15%)) 5px, color(var(--dangerColor) shade(15%)) 10px);
}
.unmonitored {
composes: unmonitored from '~Calendar/Events/CalendarEvent.css';
}
.missingUnmonitored {
composes: legendItemColor;
background-color: var(--warningColor);
&:global(.colorImpaired) {
background: repeating-linear-gradient(45deg, var(--warningColor), var(--warningColor) 5px, color(var(--warningColor) tint(15%)) 5px, color(var(--warningColor) tint(15%)) 10px);
}
composes: missingUnmonitored from '~Calendar/Events/CalendarEvent.css';
}
.missingMonitoredColorImpaired {
background: repeating-linear-gradient(90deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
.missingMonitored {
composes: missingMonitored from '~Calendar/Events/CalendarEvent.css';
}
.missingUnmonitoredColorImpaired {
background: repeating-linear-gradient(45deg, var(--colorImpairedGradientDark), var(--colorImpairedGradientDark) 5px, var(--colorImpairedGradient) 5px, var(--colorImpairedGradient) 10px);
}
.legendItemText {
display: inline-block;
.continuing {
composes: continuing from '~Calendar/Events/CalendarEvent.css';
}

View File

@@ -1,18 +1,13 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'availNotMonitored': string;
'continuing': string;
'ended': string;
'downloaded': string;
'legendItem': string;
'legendItemColor': string;
'legendItemContainer': string;
'legendItemText': string;
'missingMonitored': string;
'missingMonitoredColorImpaired': string;
'missingUnmonitored': string;
'missingUnmonitoredColorImpaired': string;
'queue': string;
'unmonitored': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -6,29 +6,31 @@ import styles from './LegendItem.css';
function LegendItem(props) {
const {
name,
style,
status,
isAgendaView,
fullColorEvents,
colorImpairedMode
} = props;
return (
<div className={styles.legendItemContainer}>
<div
className={classNames(
styles.legendItem,
styles[style],
colorImpairedMode && 'colorImpaired'
)}
/>
<div className={classNames(styles.legendItemText, colorImpairedMode && styles[`${style}ColorImpaired`])}>
{name}
</div>
<div
className={classNames(
styles.legendItem,
styles[status],
colorImpairedMode && 'colorImpaired',
fullColorEvents && !isAgendaView && 'fullColor'
)}
>
{name}
</div>
);
}
LegendItem.propTypes = {
name: PropTypes.string.isRequired,
style: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
isAgendaView: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
colorImpairedMode: PropTypes.bool.isRequired
};

View File

@@ -26,14 +26,16 @@ class CalendarOptionsModalContent extends Component {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode
enableColorImpairedMode,
fullColorEvents
} = props;
this.state = {
firstDayOfWeek,
calendarWeekColumnHeader,
timeFormat,
enableColorImpairedMode
enableColorImpairedMode,
fullColorEvents
};
}
@@ -94,6 +96,7 @@ class CalendarOptionsModalContent extends Component {
const {
showMovieInformation,
showCutoffUnmetIcon,
fullColorEvents,
onModalClose
} = this.props;
@@ -136,6 +139,18 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onOptionInputChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('FullColorEvents')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="fullColorEvents"
value={fullColorEvents}
helpText={translate('FullColorEventsHelpText')}
onChange={this.onOptionInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
@@ -176,7 +191,9 @@ class CalendarOptionsModalContent extends Component {
value={timeFormat}
onChange={this.onGlobalInputChange}
/>
</FormGroup><FormGroup>
</FormGroup>
<FormGroup>
<FormLabel>{translate('EnableColorImpairedMode')}</FormLabel>
<FormInputGroup
@@ -187,7 +204,6 @@ class CalendarOptionsModalContent extends Component {
onChange={this.onGlobalInputChange}
/>
</FormGroup>
</Form>
</FieldSet>
</ModalBody>
@@ -209,6 +225,7 @@ CalendarOptionsModalContent.propTypes = {
calendarWeekColumnHeader: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
enableColorImpairedMode: PropTypes.bool.isRequired,
fullColorEvents: PropTypes.bool.isRequired,
dispatchSetCalendarOption: PropTypes.func.isRequired,
dispatchSaveUISettings: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

View File

@@ -21,6 +21,7 @@ function createMapStateToProps() {
return {
...collection,
movies: [...collection.movies].sort((a, b) => b.year - a.year),
genres: Array.from(new Set(allGenres)).slice(0, 3)
};
}

View File

@@ -61,6 +61,7 @@ class CollectionMovie extends Component {
const {
id,
title,
status,
overview,
year,
tmdbId,
@@ -123,11 +124,11 @@ class CollectionMovie extends Component {
<div className={styles.overlay}>
<div className={styles.overlayTitle}>
{title}
{title} {year > 0 ? `(${year})` : ''}
</div>
{
id &&
id ?
<div className={styles.overlayStatus}>
<MovieIndexProgressBar
monitored={monitored}
@@ -138,7 +139,8 @@ class CollectionMovie extends Component {
detailedProgressBar={detailedProgressBar}
isAvailable={isAvailable}
/>
</div>
</div> :
null
}
</div>
</Link>
@@ -171,6 +173,7 @@ CollectionMovie.propTypes = {
id: PropTypes.number,
title: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
status: PropTypes.string.isRequired,
overview: PropTypes.string.isRequired,
monitored: PropTypes.bool,
collectionId: PropTypes.number.isRequired,

View File

@@ -5,7 +5,7 @@
margin: 2px 4px;
border: 1px solid var(--borderColor);
border-radius: 4px;
background-color: #eee;
background-color: var(--inputBackgroundColor);
cursor: default;
}
@@ -17,7 +17,7 @@
padding: 0 4px;
border-left: 4px;
border-left-style: solid;
background-color: var(--white);
background-color: var(--themeLightColor);
color: var(--defaultColor);
}

View File

@@ -14,6 +14,7 @@ class CollectionMovieLabel extends Component {
const {
id,
title,
year,
status,
monitored,
isAvailable,
@@ -35,9 +36,7 @@ class CollectionMovieLabel extends Component {
}
<span>
{
title
}
{title} {year > 0 ? `(${year})` : ''}
</span>
</div>
@@ -62,6 +61,7 @@ class CollectionMovieLabel extends Component {
CollectionMovieLabel.propTypes = {
id: PropTypes.number,
title: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
status: PropTypes.string,
isAvailable: PropTypes.bool,
monitored: PropTypes.bool,

View File

@@ -28,7 +28,6 @@ function calculatePosterWidth(posterSize, isSmallScreen) {
}
function calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions) {
const heights = [
overviewOptions.showPosters ? posterHeight : 75,
isSmallScreen ? columnPaddingSmallScreen : columnPadding
@@ -122,8 +121,8 @@ class CollectionOverviews extends Component {
overviewOptions
} = this.props;
const posterWidth = calculatePosterWidth(overviewOptions.size, isSmallScreen);
const posterHeight = calculatePosterHeight(posterWidth);
const posterWidth = overviewOptions.showPosters ? calculatePosterWidth(overviewOptions.size, isSmallScreen) : 0;
const posterHeight = overviewOptions.showPosters ? calculatePosterHeight(posterWidth) : 0;
const rowHeight = calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions);
this.setState({

View File

@@ -6,9 +6,12 @@ import { filterBuilderTypes, filterBuilderValueTypes, icons } from 'Helpers/Prop
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import FilterBuilderRowValueConnector from './FilterBuilderRowValueConnector';
import HistoryEventTypeFilterBuilderRowValue from './HistoryEventTypeFilterBuilderRowValue';
import ImportListFilterBuilderRowValueConnector from './ImportListFilterBuilderRowValueConnector';
import IndexerFilterBuilderRowValueConnector from './IndexerFilterBuilderRowValueConnector';
import LanguageFilterBuilderRowValue from './LanguageFilterBuilderRowValue';
import MinimumAvailabilityFilterBuilderRowValue from './MinimumAvailabilityFilterBuilderRowValue';
import MovieFilterBuilderRowValue from './MovieFilterBuilderRowValue';
import ProtocolFilterBuilderRowValue from './ProtocolFilterBuilderRowValue';
import QualityFilterBuilderRowValueConnector from './QualityFilterBuilderRowValueConnector';
import QualityProfileFilterBuilderRowValueConnector from './QualityProfileFilterBuilderRowValueConnector';
@@ -58,9 +61,15 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.DATE:
return DateFilterBuilderRowValue;
case filterBuilderValueTypes.HISTORY_EVENT_TYPE:
return HistoryEventTypeFilterBuilderRowValue;
case filterBuilderValueTypes.INDEXER:
return IndexerFilterBuilderRowValueConnector;
case filterBuilderValueTypes.LANGUAGE:
return LanguageFilterBuilderRowValue;
case filterBuilderValueTypes.PROTOCOL:
return ProtocolFilterBuilderRowValue;
@@ -70,6 +79,9 @@ function getRowValueConnector(selectedFilterBuilderProp) {
case filterBuilderValueTypes.QUALITY_PROFILE:
return QualityProfileFilterBuilderRowValueConnector;
case filterBuilderValueTypes.MOVIE:
return MovieFilterBuilderRowValue;
case filterBuilderValueTypes.RELEASE_STATUS:
return ReleaseStatusFilterBuilderRowValue;

View File

@@ -0,0 +1,16 @@
import { FilterBuilderProp } from 'App/State/AppState';
interface FilterBuilderRowOnChangeProps {
name: string;
value: unknown[];
}
interface FilterBuilderRowValueProps {
filterType?: string;
filterValue: string | number | object | string[] | number[] | object[];
selectedFilterBuilderProp: FilterBuilderProp<unknown>;
sectionItem: unknown[];
onChange: (payload: FilterBuilderRowOnChangeProps) => void;
}
export default FilterBuilderRowValueProps;

View File

@@ -1,5 +1,5 @@
.tag {
height: 21px;
display: flex;
&.isLastTag {
.or {
@@ -18,4 +18,5 @@
.or {
margin: 0 3px;
color: var(--themeDarkColor);
line-height: 31px;
}

View File

@@ -2,11 +2,12 @@ import PropTypes from 'prop-types';
import React from 'react';
import TagInputTag from 'Components/Form/TagInputTag';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './FilterBuilderRowValueTag.css';
function FilterBuilderRowValueTag(props) {
return (
<span
<div
className={styles.tag}
>
<TagInputTag
@@ -15,12 +16,13 @@ function FilterBuilderRowValueTag(props) {
/>
{
!props.isLastTag &&
<span className={styles.or}>
or
</span>
props.isLastTag ?
null :
<div className={styles.or}>
{translate('Or')}
</div>
}
</span>
</div>
);
}

View File

@@ -0,0 +1,51 @@
import React from 'react';
import translate from 'Utilities/String/translate';
import FilterBuilderRowValue from './FilterBuilderRowValue';
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
const EVENT_TYPE_OPTIONS = [
{
id: 1,
get name() {
return translate('Grabbed');
},
},
{
id: 3,
get name() {
return translate('Imported');
},
},
{
id: 4,
get name() {
return translate('Failed');
},
},
{
id: 6,
get name() {
return translate('Deleted');
},
},
{
id: 8,
get name() {
return translate('Renamed');
},
},
{
id: 9,
get name() {
return translate('Ignored');
},
},
];
function HistoryEventTypeFilterBuilderRowValue(
props: FilterBuilderRowValueProps
) {
return <FilterBuilderRowValue {...props} tagList={EVENT_TYPE_OPTIONS} />;
}
export default HistoryEventTypeFilterBuilderRowValue;

View File

@@ -0,0 +1,13 @@
import React from 'react';
import { useSelector } from 'react-redux';
import createLanguagesSelector from 'Store/Selectors/createLanguagesSelector';
import FilterBuilderRowValue from './FilterBuilderRowValue';
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
function LanguageFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
const { items } = useSelector(createLanguagesSelector());
return <FilterBuilderRowValue {...props} tagList={items} />;
}
export default LanguageFilterBuilderRowValue;

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { useSelector } from 'react-redux';
import Movie from 'Movie/Movie';
import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector';
import sortByName from 'Utilities/Array/sortByName';
import FilterBuilderRowValue from './FilterBuilderRowValue';
import FilterBuilderRowValueProps from './FilterBuilderRowValueProps';
function MovieFilterBuilderRowValue(props: FilterBuilderRowValueProps) {
const allMovies: Movie[] = useSelector(createAllMoviesSelector());
const tagList = allMovies
.map((movie) => ({ id: movie.id, name: movie.title }))
.sort(sortByName);
return <FilterBuilderRowValue {...props} tagList={tagList} />;
}
export default MovieFilterBuilderRowValue;

View File

@@ -9,6 +9,10 @@
&:hover {
background-color: var(--inputHoverBackgroundColor);
}
&.isDisabled {
cursor: not-allowed;
}
}
.optionCheck {

View File

@@ -12,7 +12,7 @@ import DownloadClientSelectInputConnector from './DownloadClientSelectInputConne
import EnhancedSelectInput from './EnhancedSelectInput';
import EnhancedSelectInputConnector from './EnhancedSelectInputConnector';
import FormInputHelpText from './FormInputHelpText';
import IndexerFlagsSelectInputConnector from './IndexerFlagsSelectInputConnector';
import IndexerFlagsSelectInput from './IndexerFlagsSelectInput';
import IndexerSelectInputConnector from './IndexerSelectInputConnector';
import KeyValueListInput from './KeyValueListInput';
import LanguageSelectInputConnector from './LanguageSelectInputConnector';
@@ -76,7 +76,7 @@ function getComponent(type) {
return RootFolderSelectInputConnector;
case inputTypes.INDEXER_FLAGS_SELECT:
return IndexerFlagsSelectInputConnector;
return IndexerFlagsSelectInput;
case inputTypes.DOWNLOAD_CLIENT_SELECT:
return DownloadClientSelectInputConnector;

View File

@@ -2,8 +2,10 @@
display: flex;
justify-content: flex-end;
margin-right: $formLabelRightMarginWidth;
padding-top: 8px;
min-height: 35px;
text-align: end;
font-weight: bold;
line-height: 35px;
}
.hasError {

View File

@@ -0,0 +1,60 @@
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import EnhancedSelectInput from './EnhancedSelectInput';
interface IndexerFlagsSelectInputProps {
name: string;
indexerFlags: number;
onChange(payload: object): void;
}
const selectIndexerFlagsValues = (selectedFlags: number) =>
createSelector(
(state: AppState) => state.settings.indexerFlags,
(indexerFlags) => {
const value = indexerFlags.items
.filter(
// eslint-disable-next-line no-bitwise
(item) => (selectedFlags & item.id) === item.id
)
.map(({ id }) => id);
const values = indexerFlags.items.map(({ id, name }) => ({
key: id,
value: name,
}));
return {
value,
values,
};
}
);
function IndexerFlagsSelectInput(props: IndexerFlagsSelectInputProps) {
const { indexerFlags, onChange } = props;
const { value, values } = useSelector(selectIndexerFlagsValues(indexerFlags));
const onChangeWrapper = useCallback(
({ name, value }: { name: string; value: number[] }) => {
const indexerFlags = value.reduce((acc, flagId) => acc + flagId, 0);
onChange({ name, value: indexerFlags });
},
[onChange]
);
return (
<EnhancedSelectInput
{...props}
value={value}
values={values}
onChange={onChangeWrapper}
/>
);
}
export default IndexerFlagsSelectInput;

View File

@@ -1,70 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import EnhancedSelectInput from './EnhancedSelectInput';
function createMapStateToProps() {
return createSelector(
(state, { indexerFlags }) => indexerFlags,
(state) => state.settings.indexerFlags,
(selectedFlags, indexerFlags) => {
const value = [];
indexerFlags.items.forEach((item) => {
// eslint-disable-next-line no-bitwise
if ((selectedFlags & item.id) === item.id) {
value.push(item.id);
}
});
const values = indexerFlags.items.map(({ id, name }) => {
return {
key: id,
value: name
};
});
return {
value,
values
};
}
);
}
class IndexerFlagsSelectInputConnector extends Component {
onChange = ({ name, value }) => {
let indexerFlags = 0;
value.forEach((flagId) => {
indexerFlags += flagId;
});
this.props.onChange({ name, value: indexerFlags });
};
//
// Render
render() {
return (
<EnhancedSelectInput
{...this.props}
onChange={this.onChange}
/>
);
}
}
IndexerFlagsSelectInputConnector.propTypes = {
name: PropTypes.string.isRequired,
indexerFlags: PropTypes.number.isRequired,
value: PropTypes.arrayOf(PropTypes.number).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
onChange: PropTypes.func.isRequired
};
export default connect(createMapStateToProps)(IndexerFlagsSelectInputConnector);

View File

@@ -37,6 +37,8 @@ function getType({ type, selectOptionsProviderAction }) {
return inputTypes.OAUTH;
case 'rootFolder':
return inputTypes.ROOT_FOLDER_SELECT;
case 'qualityProfile':
return inputTypes.QUALITY_PROFILE_SELECT;
default:
return inputTypes.TEXT;
}

View File

@@ -3,6 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { addRootFolder } from 'Store/Actions/rootFolderActions';
import createRootFoldersSelector from 'Store/Selectors/createRootFoldersSelector';
import translate from 'Utilities/String/translate';
import RootFolderSelectInput from './RootFolderSelectInput';
@@ -10,7 +11,7 @@ const ADD_NEW_KEY = 'addNew';
function createMapStateToProps() {
return createSelector(
(state) => state.rootFolders,
createRootFoldersSelector(),
(state, { value }) => value,
(state, { includeMissingValue }) => includeMissingValue,
(state, { includeNoChange }) => includeNoChange,

View File

@@ -102,7 +102,7 @@ class UMaskInput extends Component {
</div>
<div className={styles.details}>
<div>
<label>{translate('UMask')}</label>
<label>{translate('Umask')}</label>
<div className={styles.value}>{umask}</div>
</div>
<div>

View File

@@ -12,10 +12,18 @@
.info {
color: var(--infoColor);
&:global(.darken) {
color: color(var(--infoColor) shade(30%));
}
}
.pink {
color: var(--pink);
&:global(.darken) {
color: color(var(--pink) shade(30%));
}
}
.success {

View File

@@ -18,6 +18,7 @@ class Icon extends PureComponent {
kind,
size,
title,
darken,
isSpinning,
...otherProps
} = this.props;
@@ -26,7 +27,8 @@ class Icon extends PureComponent {
<FontAwesomeIcon
className={classNames(
className,
styles[kind]
styles[kind],
darken && 'darken'
)}
icon={name}
spin={isSpinning}
@@ -59,6 +61,7 @@ Icon.propTypes = {
kind: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
darken: PropTypes.bool.isRequired,
isSpinning: PropTypes.bool.isRequired,
fixedWidth: PropTypes.bool.isRequired
};
@@ -66,6 +69,7 @@ Icon.propTypes = {
Icon.defaultProps = {
kind: kinds.DEFAULT,
size: 14,
darken: false,
isSpinning: false,
fixedWidth: false
};

View File

@@ -10,7 +10,8 @@ class InlineMarkdown extends Component {
render() {
const {
className,
data
data,
blockClassName
} = this.props;
// For now only replace links or code blocks (not both)
@@ -47,7 +48,7 @@ class InlineMarkdown extends Component {
markdownBlocks.push(data.substr(endIndex, match.index - endIndex));
}
markdownBlocks.push(<code key={`code-${match.index}`}>{match[0].substring(1, match[0].length - 1)}</code>);
markdownBlocks.push(<code key={`code-${match.index}`} className={blockClassName ?? null}>{match[0].substring(1, match[0].length - 1)}</code>);
endIndex = match.index + match[0].length;
}
@@ -66,7 +67,8 @@ class InlineMarkdown extends Component {
InlineMarkdown.propTypes = {
className: PropTypes.string,
data: PropTypes.string
data: PropTypes.string,
blockClassName: PropTypes.string
};
export default InlineMarkdown;

View File

@@ -3,6 +3,7 @@ import React from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './ModalContent.css';
function ModalContent(props) {
@@ -28,6 +29,7 @@ function ModalContent(props) {
<Icon
name={icons.CLOSE}
size={18}
title={translate('Close')}
/>
</Link>
}

View File

@@ -82,6 +82,7 @@ class PageHeader extends Component {
aria-label="Donate"
to="https://radarr.video/donate"
size={14}
title={translate('Donate')}
/>
<IconButton
className={styles.translate}

View File

@@ -24,6 +24,7 @@ function PageHeaderActionsMenu(props) {
<MenuButton className={styles.menuButton} aria-label="Menu Button">
<Icon
name={icons.INTERACTIVE}
title={translate('Menu')}
/>
</MenuButton>

View File

@@ -51,9 +51,9 @@ class TableOptionsModal extends Component {
let pageSizeError = null;
if (value < 5) {
pageSizeError = 'Page size must be at least 5';
pageSizeError = translate('TablePageSizeMinimum', { minimumValue: '5' });
} else if (value > 250) {
pageSizeError = 'Page size must not exceed 250';
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: '250' });
} else {
this.props.onTableOptionChange({ pageSize: value });
}
@@ -145,13 +145,13 @@ class TableOptionsModal extends Component {
{
hasPageSize ?
<FormGroup>
<FormLabel>{translate('PageSize')}</FormLabel>
<FormLabel>{translate('TablePageSize')}</FormLabel>
<FormInputGroup
type={inputTypes.NUMBER}
name="pageSize"
value={pageSize || 0}
helpText={translate('PageSizeHelpText')}
helpText={translate('TablePageSizeHelpText')}
errors={pageSizeError ? [{ message: pageSizeError }] : undefined}
onChange={this.onPageSizeChange}
/>

View File

@@ -78,7 +78,8 @@ function createMapDispatchToProps(dispatch, props) {
onImportListSyncPress() {
dispatch(executeCommand({
name: commandNames.IMPORT_LIST_SYNC
name: commandNames.IMPORT_LIST_SYNC,
commandFinished: this.dispatchFetchListMovies
}));
}
};

View File

@@ -34,7 +34,8 @@ function AuthenticationRequiredModalContent(props) {
authenticationMethod,
authenticationRequired,
username,
password
password,
passwordConfirmation
} = settings;
const authenticationEnabled = authenticationMethod && authenticationMethod.value !== 'none';
@@ -70,13 +71,15 @@ function AuthenticationRequiredModalContent(props) {
isPopulated && !error ?
<div>
<FormGroup>
<FormLabel>{translate('Authentication')}</FormLabel>
<FormLabel>{translate('AuthenticationMethod')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="authenticationMethod"
values={authenticationMethodOptions}
helpText={translate('AuthenticationMethodHelpText')}
helpTextWarning={authenticationMethod.value === 'none' ? translate('AuthenticationMethodHelpTextWarning') : undefined}
helpLink="https://wiki.servarr.com/radarr/faq#forced-authentication"
onChange={onInputChange}
{...authenticationMethod}
/>
@@ -102,6 +105,7 @@ function AuthenticationRequiredModalContent(props) {
type={inputTypes.TEXT}
name="username"
onChange={onInputChange}
helpTextWarning={username?.value ? undefined : translate('AuthenticationRequiredUsernameHelpTextWarning')}
{...username}
/>
</FormGroup>
@@ -113,9 +117,22 @@ function AuthenticationRequiredModalContent(props) {
type={inputTypes.PASSWORD}
name="password"
onChange={onInputChange}
helpTextWarning={password?.value ? undefined : translate('AuthenticationRequiredPasswordHelpTextWarning')}
{...password}
/>
</FormGroup>
<FormGroup>
<FormLabel>{translate('PasswordConfirmation')}</FormLabel>
<FormInputGroup
type={inputTypes.PASSWORD}
name="passwordConfirmation"
onChange={onInputChange}
helpTextWarning={passwordConfirmation?.value ? undefined : translate('AuthenticationRequiredPasswordConfirmationHelpTextWarning')}
{...passwordConfirmation}
/>
</FormGroup>
</div> :
null
}

View File

@@ -2,10 +2,13 @@ export const BOOL = 'bool';
export const BYTES = 'bytes';
export const DATE = 'date';
export const DEFAULT = 'default';
export const HISTORY_EVENT_TYPE = 'historyEventType';
export const INDEXER = 'indexer';
export const LANGUAGE = 'language';
export const PROTOCOL = 'protocol';
export const QUALITY = 'quality';
export const QUALITY_PROFILE = 'qualityProfile';
export const MOVIE = 'movie';
export const RELEASE_STATUS = 'releaseStatus';
export const MINIMUM_AVAILABILITY = 'minimumAvailability';
export const TAG = 'tag';

View File

@@ -33,7 +33,6 @@ const columns = [
},
{
name: 'rejections',
columnLabel: () => translate('Rejections'),
label: React.createElement(Icon, {
name: icons.DANGER,
title: () => translate('Rejections')
@@ -93,7 +92,6 @@ const columns = [
},
{
name: 'customFormatScore',
columnLabel: () => translate('CustomFormatScore'),
label: React.createElement(Icon, {
name: icons.SCORE,
title: () => translate('CustomFormatScore')
@@ -103,7 +101,6 @@ const columns = [
},
{
name: 'indexerFlags',
columnLabel: () => translate('IndexerFlags'),
label: React.createElement(Icon, {
name: icons.FLAG,
title: () => translate('IndexerFlags')

View File

@@ -52,11 +52,7 @@
width: 75px;
}
.title {
composes: cell;
}
.title div {
.titleContent {
overflow-wrap: break-word;
}

View File

@@ -19,7 +19,7 @@ interface CssExports {
'quality': string;
'rejected': string;
'size': string;
'title': string;
'titleContent': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -63,12 +63,12 @@ function getDownloadTooltip(
if (isGrabbing) {
return '';
} else if (isGrabbed) {
return translate('AddToDownloadQueue');
return translate('AddedToDownloadQueue');
} else if (grabError) {
return grabError;
}
return translate('AddedToDownloadQueue');
return translate('AddToDownloadQueue');
}
interface InteractiveSearchRowProps {
@@ -246,10 +246,12 @@ function InteractiveSearchRow(props: InteractiveSearchRowProps) {
) : null}
</TableRowCell>
<TableRowCell className={styles.title}>
<Link to={infoUrl} title={title}>
<div>{title}</div>
</Link>
<TableRowCell>
<div className={styles.titleContent}>
<Link to={infoUrl} title={title}>
{title}
</Link>
</div>
</TableRowCell>
<TableRowCell className={styles.indexer}>{indexer}</TableRowCell>

View File

@@ -202,6 +202,12 @@
.headerContent {
padding: 15px;
}
.title {
font-weight: 300;
font-size: 30px;
line-height: 30px;
}
}
@media only screen and (max-width: $breakpointLarge) {

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import TextTruncate from 'react-text-truncate';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import ImdbRating from 'Components/ImdbRating';
import InfoLabel from 'Components/InfoLabel';
@@ -43,7 +44,7 @@ import MovieCollectionLabelConnector from './../MovieCollectionLabelConnector';
import MovieCastPostersConnector from './Credits/Cast/MovieCastPostersConnector';
import MovieCrewPostersConnector from './Credits/Crew/MovieCrewPostersConnector';
import MovieDetailsLinks from './MovieDetailsLinks';
import MovieReleaseDatesConnector from './MovieReleaseDatesConnector';
import MovieReleaseDates from './MovieReleaseDates';
import MovieStatusLabel from './MovieStatusLabel';
import MovieTagsConnector from './MovieTagsConnector';
import MovieTitlesTable from './Titles/MovieTitlesTable';
@@ -53,11 +54,7 @@ const defaultFontSize = parseInt(fonts.defaultFontSize);
const lineHeight = parseFloat(fonts.lineHeight);
function getFanartUrl(images) {
const fanartImage = _.find(images, { coverType: 'fanart' });
if (fanartImage) {
// Remove protocol
return fanartImage.url.replace(/^https?:/, '');
}
return _.find(images, { coverType: 'fanart' })?.url;
}
function getExpandedState(newState) {
@@ -289,7 +286,7 @@ class MovieDetails extends Component {
onMonitorTogglePress,
onRefreshPress,
onSearchPress,
queueItems,
queueItem,
movieRuntimeFormat
} = this.props;
@@ -436,7 +433,7 @@ class MovieDetails extends Component {
}
title={translate('ReleaseDates')}
body={
<MovieReleaseDatesConnector
<MovieReleaseDates
inCinemas={inCinemas}
physicalRelease={physicalRelease}
digitalRelease={digitalRelease}
@@ -547,7 +544,7 @@ class MovieDetails extends Component {
hasMovieFiles={hasMovieFiles}
monitored={monitored}
isAvailable={isAvailable}
queueItem={(queueItems.length > 0) ? queueItems[0] : null}
queueItem={queueItem}
/>
</span>
</InfoLabel>
@@ -634,24 +631,27 @@ class MovieDetails extends Component {
<div className={styles.contentContainer}>
{
!isFetching && movieFilesError &&
<div>
!isFetching && movieFilesError ?
<Alert kind={kinds.DANGER}>
{translate('LoadingMovieFilesFailed')}
</div>
</Alert> :
null
}
{
!isFetching && movieCreditsError &&
<div>
!isFetching && movieCreditsError ?
<Alert kind={kinds.DANGER}>
{translate('LoadingMovieCreditsFailed')}
</div>
</Alert> :
null
}
{
!isFetching && extraFilesError &&
<div>
!isFetching && extraFilesError ?
<Alert kind={kinds.DANGER}>
{translate('LoadingMovieExtraFilesFailed')}
</div>
</Alert> :
null
}
<Tabs selectedIndex={selectedTabIndex} onSelect={this.onTabSelect}>
@@ -830,7 +830,7 @@ MovieDetails.propTypes = {
onRefreshPress: PropTypes.func.isRequired,
onSearchPress: PropTypes.func.isRequired,
onGoToMovie: PropTypes.func.isRequired,
queueItems: PropTypes.arrayOf(PropTypes.object),
queueItem: PropTypes.object,
movieRuntimeFormat: PropTypes.string.isRequired
};

View File

@@ -145,6 +145,8 @@ function createMapStateToProps() {
return acc;
}, []);
const queueItem = queueItems.find((item) => item.movieId === movie.id);
return {
...movie,
alternateTitles,
@@ -165,7 +167,7 @@ function createMapStateToProps() {
nextMovie,
isSmallScreen: dimensions.isSmallScreen,
isSidebarVisible,
queueItems,
queueItem,
movieRuntimeFormat
};
}

View File

@@ -1,67 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import { icons } from 'Helpers/Props';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import styles from './MovieReleaseDates.css';
function MovieReleaseDates(props) {
const {
showRelativeDates,
shortDateFormat,
timeFormat,
inCinemas,
physicalRelease,
digitalRelease
} = props;
return (
<div>
{
!!inCinemas &&
<div >
<div className={styles.dateIcon}>
<Icon
name={icons.IN_CINEMAS}
/>
</div>
{getRelativeDate(inCinemas, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
</div>
}
{
!!digitalRelease &&
<div >
<div className={styles.dateIcon}>
<Icon
name={icons.MOVIE_FILE}
/>
</div>
{getRelativeDate(digitalRelease, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
</div>
}
{
!!physicalRelease &&
<div >
<div className={styles.dateIcon}>
<Icon
name={icons.DISC}
/>
</div>
{getRelativeDate(physicalRelease, shortDateFormat, showRelativeDates, { timeFormat, timeForToday: false })}
</div>
}
</div>
);
}
MovieReleaseDates.propTypes = {
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
longDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
inCinemas: PropTypes.string,
physicalRelease: PropTypes.string,
digitalRelease: PropTypes.string
};
export default MovieReleaseDates;

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