Compare commits

...

255 commits

Author SHA1 Message Date
bakerboy448
d2330a3232
Bump to 2.13.3 2025-08-17 14:42:19 -04:00
Weblate
4cb306780f Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: mrchonks <chonkstv@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translation: Servarr/Lidarr
2025-08-16 10:35:00 -05:00
Mark McDowall
393db165f3 New: Move auth success logging to debug
Closes #7978

(cherry picked from commit 9ebe043bd94d036fe2ab45f3bb3f882cda48e211)
2025-08-12 12:22:25 -05:00
Weblate
eb861f06d3 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: ArLab1 <arnaud.laberge@hotmail.com>
Co-authored-by: Gallyam Biktashev <gallyamb@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: mrchonks <chonkstv@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translation: Servarr/Lidarr
2025-08-11 20:15:07 -05:00
bakerboy448
6f1b370772
docs: add metadata notice & link GHI
[skip ci]
2025-08-05 21:27:28 -05:00
bakerboy448
074f06442a
Bump to 2.13.2 2025-08-03 01:12:50 -05:00
Meyn
fef111d396 Bump SixLabors.ImageSharp to 3.1.11
Signed-off-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
2025-07-31 16:28:18 -05:00
jasonpatrickellykrause
76b7713870 Fixed: Clarify monitor language for new and future albums. 2025-07-31 16:28:18 -05:00
bakerboy448
d50ed84541 Skip tests temporally 2025-07-31 16:28:18 -05:00
Weblate
002e8f5b69 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: ArLab1 <arnaud.laberge@hotmail.com>
Co-authored-by: Clean Seat <clean.seat9057@fastmail.com>
Co-authored-by: Donato Battista <donato.donelio@gmail.com>
Co-authored-by: Gallyam Biktashev <gallyamb@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: Matteo Rettore <matte.rettore@gmail.com>
Co-authored-by: Oleksii Ilienko <assada.ua@gmail.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Siujeon <yeungsiujeon@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: josef <josef.holzapfel@proton.me>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_Hans/
Translation: Servarr/Lidarr
2025-07-31 16:17:49 -05:00
bakerboy448
c7b8aa8a04 Skip tests temporally 2025-07-13 20:06:05 -05:00
Bogdan
91f06801ca
Bump version to 2.13.1 2025-06-15 09:22:00 +03:00
Bogdan
dc61618711 Save Publish Dates as UTC for grabbed albums 2025-06-12 11:35:35 +03:00
Bogdan
fd00a5627c Fixed: Improve error message for queue items from Transmission
(cherry picked from commit 0ae07898ba6f85299e739d38ab1dd900c39e91d2)
2025-06-12 11:32:16 +03:00
Bogdan
66ea1b1dfb Fixed: Avoid requests without categories for FileList
(cherry picked from commit 4728fa29ef578a7ff33cf16a4e6b46689c4be1b4)
2025-06-12 11:10:06 +03:00
Bogdan
72fa05cf41 Fixed: Sending notifications for Custom Script with unparsed artist
(cherry picked from commit 76b1130b6811454fa6b1e80e0b2012c24c4ae8fa)
2025-06-12 11:06:46 +03:00
Bogdan
c51b5c6fba Log when expected track file is missing from disk on upgrade
(cherry picked from commit 1047e71b7d78812f2fa04b150fc6774efc1a6af8)
2025-06-12 11:05:21 +03:00
Bogdan
efebab9ba2 Update default log level message
(cherry picked from commit 817d13e85c89d1f10abab09a8f63272a46f5d0b6)
2025-06-12 11:03:29 +03:00
Mark McDowall
47c32c9963 Improve messaging when NZB contains invalid XML
(cherry picked from commit 728df146ada115a367bf1ce808482a4625e6098d)
2025-06-12 10:56:57 +03:00
Stevie Robinson
9f229bb684 Ensure Custom Format Maximum Size won't overflow
(cherry picked from commit a50d2562649bbe77d0feb9fbfc594d56952e0a5e)
2025-06-12 10:56:14 +03:00
Mark McDowall
f9b2e57696 Increase maximum backup restoration size to 5GB
(cherry picked from commit e38deb34221ebf131adcce9551774898f46b1f7f)
2025-06-12 10:55:49 +03:00
carrossos
4b48edab0a Treat HTTP 410 response for failed download similarly to HTTP 404
(cherry picked from commit 818ae02a7a8f0a8ea0a44e0015e2667d96453332)
2025-06-12 10:55:02 +03:00
Stevie Robinson
e087574de7 New: Ignore volumes containing .timemachine from Disk Space
(cherry picked from commit a853c537db0a6bd499a2277987dc170d2a1f5645)
2025-06-12 10:54:16 +03:00
Bogdan
8877cf99f1 Use the thrown exception in http timeout handling
(cherry picked from commit 14e324ee30694ae017a39fd6f66392dc2d104617)
2025-06-12 10:53:59 +03:00
Stevie Robinson
a56e5b3f9a New: Don't allow remote path to start with space
(cherry picked from commit 5ba3ff598770fdf9e5a53d490c8bcbdd6a59c4cc)

Fixed validation for Remote Path Mapping

(cherry picked from commit bf34b4309402ce529a8c04de70f44b28948761f4)
2025-06-12 10:52:01 +03:00
Stevie Robinson
5bb1949ea2 Fixed: Include network drive types in Disk Space
(cherry picked from commit 9ffcd141a515e99604881a4ef383dadafef31eeb)
2025-06-12 10:47:17 +03:00
Bogdan
979042948d Fixed: Quality sliders on some browsers 2025-06-12 10:47:00 +03:00
Mark McDowall
ebe59b18d9 Sync react-slider props for Quality sliders with upstream
(cherry picked from commit 9dab2ba6e4316879e4db8db47363476a5c4f13b2)
2025-06-12 10:46:53 +03:00
Bogdan
086a451dff Follow redirects for usenet grabs on non-prod builds
(cherry picked from commit 1cdca8ef3e47e19c9264db6b322161b615b20294)
2025-06-12 10:39:37 +03:00
Mark McDowall
1bcb82eed0 Prevent should refresh artists and albums from failing
(cherry picked from commit 3eed84c67938fed308e562e69cf7bcd727063803)
2025-06-12 10:38:23 +03:00
Mark McDowall
ae9b4cec75 New: Update wording when removing a root folder
(cherry picked from commit 51c17fd3122f7b96a4155593d465ba32870d0c91)
2025-06-12 10:35:09 +03:00
Mark McDowall
ed777de015 Fixed: Escape backticks in discord notifications
(cherry picked from commit 70c74fc1769f2094a14faaa103c49d744534be9f)
2025-06-12 10:30:03 +03:00
Bogdan
96f956a5d6 Fix fullscreen automation screenshots 2025-06-10 13:13:38 +03:00
Bogdan
68a8f40746 Fixed translations for the updates page 2025-06-09 23:33:06 +03:00
Bogdan
c518cf63e7
Bump version to 2.13.0 2025-06-09 20:28:15 +03:00
Bogdan
da55b8578a Bump IPAddressRange and SixLabors.ImageSharp 2025-06-09 15:21:12 +03:00
Bogdan
234c29ef49
Bump version to 2.12.4 2025-06-08 10:31:50 +03:00
Weblate
de169e8a1f Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: CandyD-1992 <259215606@qq.com>
Co-authored-by: Ilbebino <tommasobellandi08@gmail.com>
Co-authored-by: NanderTGA <nander.roobaert@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2025-06-06 21:24:28 +03:00
Bogdan
4b300a448a Skip tests temporally 2025-06-06 18:06:09 +03:00
Gykes
785bcfda0b
Fixed: Sort artists by genre on index table view 2025-06-03 09:13:36 +03:00
Bogdan
94ea751ad2 Ignore Jetbrains IntelliJ Workspace Directories 2025-06-02 11:18:44 +03:00
Bogdan
0c172b58f1
Bump version to 2.12.3 2025-06-01 10:40:54 +03:00
Weblate
ea2ee70208 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Discover999 <13189912235@163.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: arsenius88 <arsenovich_andr@ukr.net>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_Hans/
Translation: Servarr/Lidarr
2025-05-25 16:19:12 +03:00
Bogdan
8b63928a25
Bump version to 2.12.2 2025-05-25 16:18:38 +03:00
Bogdan
7217e891f7 New: Real time UI updates for provider changes
(cherry picked from commit 20ef22be94f4bdb5633ddfb080e91c8d5b0229da)

Closes #5178
2025-05-22 19:36:21 +03:00
Bogdan
345bbcd992
Bump version to 2.12.1 2025-05-04 21:08:35 +03:00
Bogdan
bd9d7ba085 Fixed: Parsing FLAC24 as FLAC 24-bit 2025-05-01 17:26:52 +03:00
Bogdan
3937bebfea Add plugins branch to the bug report template 2025-05-01 15:57:05 +03:00
Bogdan
767b0930a5 Bump caniuse db 2025-04-28 15:02:42 +03:00
Bogdan
c3f0fc640c Bump core-js to 3.41 2025-04-28 15:01:30 +03:00
Bogdan
9dbcc79436 Bump version to 2.12.0 2025-04-28 15:00:29 +03:00
Bogdan
3dd04cecbf Skip spotify mapping tests 2025-04-28 12:41:35 +03:00
Bogdan
d8850af019 Increase input sizes in edit artist modal
Closes #5294
2025-04-17 12:36:02 +03:00
Bogdan
fbfd24e226
Bump version to 2.11.2 2025-04-13 10:08:22 +03:00
Weblate
d9562c701e Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: Ste <stefanucciu@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translation: Servarr/Lidarr
2025-04-08 15:40:25 +03:00
Servarr
d21ad2ad68 Automated API Docs update 2025-04-08 15:38:55 +03:00
Bogdan
556f0ea54b Fixed: Disallow tags creation with empty label 2025-04-08 15:30:20 +03:00
Bogdan
e4a36ca388 Log delete statements only once 2025-04-08 15:29:28 +03:00
Bogdan
1045684935 Bump Selenium.WebDriver.ChromeDriver 2025-04-08 15:28:37 +03:00
Mark McDowall
9ba71ae6b1 Update WikiUrl type in API docs
(cherry picked from commit 9bd619ccfe074abe396bbf043a36a5be18a7ba4b)
2025-04-08 15:28:06 +03:00
Mark McDowall
89b9352fef Fixed: Set output encoding to UTF-8 when running external processes
(cherry picked from commit f8e57b09856278a6d0c65f18704e96a33459687d)
2025-04-08 15:26:59 +03:00
Mark McDowall
c83332e58c New: Prevent Remote Path Mapping local folder being set to System folder or '/'
(cherry picked from commit 0f904e091702a2ac53771ee3aeb5aafe62688035)
2025-04-08 15:26:38 +03:00
Bogdan
4677a1115a
Bump linux agent to ubuntu-22.04 2025-04-02 00:11:01 +03:00
Weblate
6150a57596 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Maxime Surrel <maxime.surrel@live.fr>
Co-authored-by: Moxitech <moxitogame59@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Xing Wang <wxing82@outlook.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2025-03-30 10:17:56 +03:00
Bogdan
13f6b1a086
Bump version to 2.11.1 2025-03-30 10:17:14 +03:00
Bogdan
8027ab5d2e
Include invalid path in exception message when failing to normalize 2025-03-28 09:57:57 +02:00
Bogdan
5bdc119b98 Fixed: Include Track for history/since
Fixes #5421
2025-03-27 19:47:03 +02:00
Bogdan
1b9b57ae9b Bump browserslist-db 2025-03-25 21:13:13 +02:00
Mark McDowall
c28a97cafd Fixed: Deleting artist folder fails when files/folders aren't instantly removed
(cherry picked from commit c84699ed5d5a2f59f236c26a8999d25a1102ec02)
2025-03-25 21:11:58 +02:00
Bogdan
099d19a04d Cleanup unused sorting fields for bulk manage providers
(cherry picked from commit 6115236d3853f70a18b73aef15ebe4e18ab48e40)
2025-03-25 21:11:32 +02:00
Bogdan
d381463b60 New: Display indexer in download failed details
(cherry picked from commit a324052debf63a8db73a2f3c79201864892bb62c)
2025-03-25 21:10:19 +02:00
Bogdan
a86bd8e862 Fixed: Inherit indexer, size and release group for marked as failed history
(cherry picked from commit e08c9d5501e65aabce3456b2dd7571867508d88f)
2025-03-25 21:08:13 +02:00
Mark McDowall
4bea38ab9c Improve logging when login fails due to CryptographicException
(cherry picked from commit 1449941471cbb8885e9298317b9a30f2576d7941)
2025-03-25 21:03:41 +02:00
Bogdan
950c51bc59 Fixed: Priority validation for indexers and download clients
(cherry picked from commit f0e320f3aa501f120721503b8256f464a31be783)
2025-03-25 21:00:43 +02:00
Mark McDowall
18f13fe7f8 Fixed: Allow tables to scroll on tablets in portrait mode
(cherry picked from commit 5fb632eb46cf77ea4f61d407f6429d9c32dba766)
2025-03-25 20:58:58 +02:00
Bogdan
f8d4b3a59b Bump NLog, Npgsql, System.Memory and System.ValueTuple 2025-03-23 13:10:50 +02:00
Bogdan
5cf9624e55
Bump version to 2.11.0 2025-03-23 13:02:07 +02:00
Mark McDowall
81895f8033 Fixed: Drop downs flickering in some cases
(cherry picked from commit 3b024443c5447b7638a69a99809bf44b2419261f)

Closes #5386
2025-03-23 09:42:29 +02:00
Weblate
a1c2bfa527 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2025-03-16 11:38:19 +02:00
Bogdan
33049910de
Bump version to 2.10.3 2025-03-16 10:47:47 +02:00
Bogdan
6dd87fd348
Bump version to 2.10.2 2025-03-09 11:52:12 +02:00
Bogdan
9314eb34ab Fixed: Displaying warnings for automatic failed imports in queue 2025-03-08 15:44:52 +02:00
Bogdan
84b91ba6c1 Bump Polly to 8.5.2 2025-03-07 23:18:35 +02:00
Bogdan
6c6f92fbed Bump SixLabors.ImageSharp to 3.1.7 2025-03-07 23:17:29 +02:00
Bogdan
1e42ae94aa Fix Completed Download Service tests 2025-03-06 19:49:26 +02:00
Servarr
29f5810865 Automated API Docs update 2025-03-06 19:24:11 +02:00
Bogdan
342c82aa1f Fixed: Avoid notifications on reprocessing failed items in queue 2025-03-06 19:15:59 +02:00
Bogdan
5a3f879442 Fixed: Sending import failure notifications to webhook/notifiarr 2025-03-06 17:28:36 +02:00
Mark McDowall
6e57c14e57 Fixed: Marking queued item as failed not blocking the correct Torrent Info Hash
(cherry picked from commit 4b186e894e4e229a435c077e00c65b67ca178333)

Fixes #4977
Closes #4988
2025-03-06 16:34:28 +02:00
Bogdan
9fc549b43b Fixed: Replace diacritics in Clean Title naming tokens 2025-03-05 20:12:15 +02:00
Weblate
a2201001c5 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: youngjimisme <977671346@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2025-03-05 20:03:53 +02:00
Mark McDowall
8c99280f07 Fixed: Adding albums with unknown items in queue 2025-03-05 19:59:56 +02:00
Bogdan
07db508580 Fixed: Calculating custom formats for queue 2025-03-05 19:59:56 +02:00
Bogdan
031f32a52c Fixed: Refresh cache for tracked queue on artist/album add or removal
Prevents a NullRef in CompletedDownloadService.VerifyImport when publishing DownloadCompletedEvent for an already cached tracked download
2025-03-05 19:59:56 +02:00
Mark McDowall
2997c16346 Fixed: Reprocessing items that were previously blocked during importing
(cherry picked from commit bce848facf8aeaeac6a1d59c92941d00589034a4)
2025-03-05 19:59:56 +02:00
Mark McDowall
a1a53dbb5e New: Improve UI status when downloads cannot be imported automatically
(cherry picked from commit 6d5ff9c4d6993d16848980aea499a45b1b51d95c)
2025-03-05 19:59:56 +02:00
Mark McDowall
e8bb78e5bb New: Improve messaging if release is in queue because all tracks in release were not imported
(cherry picked from commit 2728bf79ca41bc372de515cb09e1034a8c006c2b)
2025-03-05 19:59:56 +02:00
Bogdan
6292f223ac Fixed: Attempt to ensure all import results are imported
Fixes #2746
Closes #4815
2025-03-05 19:53:11 +02:00
Bogdan
f4dc294ab3 Fixed: Instance name must contain application name 2025-03-01 13:35:07 +02:00
Bogdan
23611cb116
Bump version to 2.10.1 2025-02-23 12:16:19 +02:00
Bogdan
f177345d01 Fixed: Avoid checking for free space if other specifications fail first 2025-02-22 21:35:18 +02:00
Bogdan
ec050a7b3c Fixed: Prevent NullRef for webhooks when Artist Metadata is not set
Fixes #5368
2025-02-22 16:36:25 +02:00
Mark McDowall
860bd04c59 New: Add artist tags to Webhook and Notifiarr events
(cherry picked from commit cc0a284660f139d5f47b27a2c389973e5e888587)

Closes #4805
2025-02-22 16:26:26 +02:00
Mark McDowall
261f30d268 New: Genres and Images for Webhooks and Notifiarr
(cherry picked from commit fd3dd1ab7dc86cd9e231fa432cc8d2772d5a4bad)

Closes #4832
2025-02-22 16:26:00 +02:00
Weblate
36998abba0 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_Hans/
Translation: Servarr/Lidarr
2025-02-19 17:25:54 +02:00
Mark McDowall
ad12617694 Cleanse console log messages
(cherry picked from commit 609e964794e17343f63e1ecff3fef323e3d284ff)
2025-02-19 17:09:39 +02:00
Stevie Robinson
be115da157 Fixed: Fallback to Instance Name for Discord notifications
(cherry picked from commit b99e06acc0a3ecae2857d9225b35424c82c67a2b)
2025-02-19 17:06:58 +02:00
Chaz Harris
664b972494
Fixed: Custom Lists using only ArtistMusicBrainzId (#5399)
When using a JSON list that consists of only MusicBrainzId's the list is being filtered.
2025-02-16 21:03:41 +02:00
Bogdan
2b2fd5a175 Fix download links for FileList when passkey contains spaces 2025-02-16 12:20:27 +02:00
Chaz Harris
d8222c066c
Bump devcontainer nodejs version to 20 (#5398) 2025-02-15 11:47:22 +02:00
Weblate
bc6417229e Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Gallyam Biktashev <gallyamb@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translation: Servarr/Lidarr
2025-02-07 19:15:05 -06:00
Bogdan
e0e17a2ea7 Building docs on ARM
Co-authored-by: Mark McDowall <mark@mcdowall.ca>
(cherry picked from commit 147e732c9ca7a4c289d4f6386f1277650e11f15b)
(cherry picked from commit dd900eb7395144b6d299f10fe9475d49d194664e)
2025-02-06 00:54:20 +02:00
Bogdan
5bf2ae9e6f
Bump version to 2.10.0 2025-02-03 14:11:43 +02:00
Bogdan
8e01ba5f21
Bump version to 2.9.6 2025-02-02 12:48:53 +02:00
Mark McDowall
45e8ecffa0 Fixed: Ignore special folders inside Blackhole watch folders
(cherry picked from commit e79dd6f8e689617b1fd9f96c639ac300669112c5)
2025-02-01 23:52:09 +02:00
Bogdan
3c4b438d27 Fixed: Health warning for downloading inside root folders
(cherry picked from commit 1e9fd02e9d2bf57247adcac5728e2a0d2b084b86)

Fixes #5384
2025-02-01 23:51:13 +02:00
Bogdan
8fd79d7291 New: Prefer newer Usenet releases
(cherry picked from commit 6a439f03273b376feda713ef04a6912fc3af9d0a)
2025-02-01 23:45:27 +02:00
Weblate
477a799b8a Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Dani Talens <databio@gmail.com>
Co-authored-by: Mailme Dashite <mailmedashite@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translation: Servarr/Lidarr
2025-02-01 23:45:21 +02:00
Bogdan
51a38bc648 Fix logging message for directory watcher error 2025-01-28 21:57:30 +02:00
Weblate
917f705695 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Dani Talens <databio@gmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Pieterjan Van Saet <hybridfox2@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: ahgharaghani <ah.gharaghani@gmail.com>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: warkurre86 <tom.novo.86@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fa/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_Hans/
Translation: Servarr/Lidarr
2025-01-28 21:55:36 +02:00
Bogdan
5a1092b511 Prevent page crash on console.error being used with non-string values
(cherry picked from commit 963395b9695a28af6bc7dd398e9eea18c834c3c9)
2025-01-28 21:49:43 +02:00
jcassette
ef2c6366c4 New: reflink support for ZFS
(cherry picked from commit a840bb542362d58006b6cc27affd58ee6b965b80)

Closes #5369
2025-01-19 17:32:47 +02:00
Bogdan
1ffb82e364
Bump version to 2.9.5 2025-01-19 17:16:09 +02:00
Bogdan
e2f8753a6a Improve messaging for no mediums on album details 2025-01-17 00:42:08 +02:00
Gauthier
739019498f New: Add headers setting in webhook connection
(cherry picked from commit 78fb20282de73c0ea47375895a807235385d90e3)

Closes #5242
2025-01-15 23:06:47 +02:00
Bogdan
396b2ae7c1 Bump SonarCloud azure extension for UI analysis to 3.X 2025-01-14 11:19:52 +02:00
Qstick
0216616738 Bump SonarCloud azure extension to 3.X
(cherry picked from commit 5fac3486130df3b316dd882d676ca13ecb697b59)
2025-01-14 10:09:06 +02:00
Mark McDowall
82e0b628cc Fixed: Parsing of release names with colon in the title
(cherry picked from commit ec698c2cf7df1e1182ffa2f4505fe0872e2d08bc)
2025-01-14 04:16:12 +02:00
Weblate
014f8a58b1 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Mickaël O <mickael.ouillon@ac-bordeaux.fr>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translation: Servarr/Lidarr
2025-01-12 15:18:26 +02:00
Bogdan
5cbb2848c7
Bump version to 2.9.4 2025-01-12 15:17:02 +02:00
Weblate
554cf8ec55 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Alexander Balya <alexander.balya@gmail.com>
Co-authored-by: Ano10 <Ano10@users.noreply.translate.servarr.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Matti Meikäläinen <diefor-93@hotmail.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Tommy Au <smarttommyau@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: marapavelka <mara.pavelka@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_TW/
Translation: Servarr/Lidarr
2025-01-05 13:17:21 +02:00
Stevie Robinson
4ff6c71456 Fixed: Listening on all IPv4 Addresses
(cherry picked from commit 035c474f10c257331a5f47e863d24af82537e335)
2025-01-05 13:16:36 +02:00
Stevie Robinson
7cfcf01ae3 Fixed: qBittorrent Ratio Limit Check
(cherry picked from commit 4dcc015fb19ceb57d2e8f4985c5137e765829d1c)
2025-01-05 13:16:24 +02:00
Bogdan
17c5c66e54
Bump version to 2.9.3 2025-01-05 13:16:11 +02:00
Bogdan
40dab8deb9 Check if backup folder is writable on backup
(cherry picked from commit 8aad79fd3e14eb885724a5e5790803c289be2f25)

Closes #5348
2024-12-31 12:20:10 +02:00
Bogdan
39f0e4d989 Suggest adding IP to RPC whitelist for on failed Transmission auth
(cherry picked from commit f05e552e8e6dc02cd26444073ab9a678dcb36492)
2024-12-31 12:19:06 +02:00
Bogdan
35a46eca7b
Bump version to 2.9.2 2024-12-30 01:01:47 +02:00
Mark McDowall
79b29f39f9 Don't send session information to Sentry
(cherry picked from commit fae24e98fb9230c2f3701caef457332952c6723f)
2024-12-28 16:00:11 +02:00
Bruno Garcia
0e19c03e9a Update Sentry SDK add features
Co-authored-by: Stefan Jandl <reg@bitfox.at>
(cherry picked from commit 6377c688fc7b35749d608bf62796446bb5bcb11b)
2024-12-28 16:00:11 +02:00
Weblate
e6388cab94 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Tommy Au <smarttommyau@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_TW/
Translation: Servarr/Lidarr
2024-12-22 17:18:20 +02:00
Bogdan
47e504fbc9 Add translations for some download client settings 2024-12-22 17:17:04 +02:00
Mika
1a40839202 Add file-count for Transmission RPC
(cherry picked from commit 23c741fd001582fa363c2723eff9facd3091618b)
2024-12-22 17:17:04 +02:00
Bogdan
25a80aa29d Avoid default category on existing Transmission configurations
Co-authored-by: Mark McDowall <mark@mcdowall.ca>
(cherry picked from commit bd656ae7f66fc9224ef2a57857152ee5d54d54f8)
2024-12-22 17:17:04 +02:00
Bogdan
7255126af5 New: Labels support for Transmission 4.0
(cherry picked from commit 675e3cd38a14ea33c27f2d66a4be2bf802e17d88)
2024-12-22 17:17:04 +02:00
Bogdan
166f87ae68 Include exception message in LidarrAPI failure message
(cherry picked from commit 1e89a1a3cb8fa83e4415b047513cbecacbebc59c)

Closes #5176
2024-12-22 16:21:13 +02:00
Bogdan
babdf10273
Bump version to 2.9.1 2024-12-22 13:26:41 +02:00
Bogdan
19c2994ff3 Skip spotify mapping tests 2024-12-21 16:08:38 +02:00
Bogdan
e420ee0645 Bump NLog, IPAddressRange, Polly, ImageSharp, Npgsql, System.Memory and Ical.Net
Closes #5333
2024-12-21 15:39:06 +02:00
Bogdan
78469a96c9 Bump MailKit to 4.8.0 and Microsoft.Data.SqlClient to 2.1.7
Closes #5332
2024-12-21 15:34:04 +02:00
Servarr
bc6df548fc Automated API Docs update 2024-12-17 19:50:37 +02:00
Stevie Robinson
797e4c773e Replace URLs in translations with tokens
(cherry picked from commit 98d60e1a8e9abce6b31b3cdd745eff0fed181458)
2024-12-17 18:49:21 +02:00
Weblate
119141723a Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2024-12-17 18:45:19 +02:00
Mark McDowall
fd1719e58c Fixed: Artists without tags bypassing tags on Download Client
(cherry picked from commit c0e264cfc520ee387bfc882c95a5822c655e0d9b)

Fix typo about download clients comment

(cherry picked from commit c39fb4fe6f0ed5e1dc2aa33f4455a4d0c760063b)

Closes #5309
Closes #5318
2024-12-17 18:30:39 +02:00
Mark McDowall
41612708ff Sync TimeSpanConverter with upstream
Co-authored-by: Bogdan <mynameisbogdan@users.noreply.github.com>

(cherry picked from commit 1374240321f08d1400faf95e84217e4b7a2d116b)

Closes #5301
2024-12-17 18:28:20 +02:00
Bogdan
535caf1324 Add return type for artist/album lookup endpoint
Closes #5282
2024-12-17 18:27:00 +02:00
Mark McDowall
eb3c7d6990 Update React
(cherry picked from commit 4491df3ae7530f2167beebc3548dd01fd2cc1a12)

Towards #5264
2024-12-17 18:25:56 +02:00
Mark McDowall
4c603e24f6 Support Postgres with non-standard version string
(cherry picked from commit 40f4ef27b22113c1dae0d0cbdee8205132bed68a)

Closes #5267
2024-12-17 18:19:06 +02:00
Bogdan
ec93c33aa9 Console warnings for missing translations on development builds
(cherry picked from commit 67a1ecb0fea4e6c7dfdb68fbe3ef30d4c22398d8)

Closes #5239
2024-12-17 18:10:08 +02:00
Mark McDowall
afb3fd5bd5 Upgrade typescript-eslint packages to 8.181.1
(cherry picked from commit ed10b63fa0c161cac7e0a2084e53785ab1798208)

Closes #5325
2024-12-17 17:41:10 +02:00
Mark McDowall
198a13755f Upgrade TypeScript and core-js
(cherry picked from commit 148480909917f69ff3b2ca547ccb4716dd56606e)

Closes #5306
2024-12-17 17:38:24 +02:00
Bogdan
44a5654918 Log adding missing artist messages as info 2024-12-17 15:48:45 +02:00
Mark McDowall
8aa0754843 Upgrade Font Awesome to 6.7.1
(cherry picked from commit 016b5718386593c030f14fcac307c93ee1ceeca6)
2024-12-17 15:47:58 +02:00
Mark McDowall
c42e96b55d Upgrade babel to 7.26.0
(cherry picked from commit bfcd017012730c97eb587ae2d2e91f72ee7a1de3)
2024-12-17 15:47:45 +02:00
Bogdan
f92935e3d2 Set minor version for core-js in babel/preset-env
(cherry picked from commit 2e83d59f61957cbc2171bef097fe2410e72729ad)
2024-12-17 15:47:05 +02:00
Bogdan
13bb8f5089 Bump version to 2.9.0 2024-12-16 21:04:53 +02:00
Bogdan
ad084cdf91 Fixed: Parse FLAC 24-bit/24_bit/24.bit as FLAC 24bit 2024-12-16 16:29:05 +02:00
Bogdan
4bcdc49777 Fixed: Refresh backup list on deletion
(cherry picked from commit 3b00112447361b19c04851a510e63f812597a043)
2024-12-15 05:30:39 +02:00
Mark McDowall
502cb20898 Fixed: Error getting processes in some cases
(cherry picked from commit b552d4e9f7ca7388404aa0d52566010a54cb0244)
2024-12-15 05:30:28 +02:00
Weblate
0fd6c263b1 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Dani Talens <databio@gmail.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Tomer Horowitz <tomerh2001@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: hhjuhl <hans@kopula.dk>
Co-authored-by: keysuck <joshkkim@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translation: Servarr/Lidarr
2024-12-14 02:34:55 +02:00
Bogdan
11af8a5e05
Bump version to 2.8.2 2024-12-08 18:17:10 +02:00
Servarr
88196340a8 Automated API Docs update 2024-12-04 17:50:06 +02:00
Weblate
4048f2bd72 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Albrt9527 <2563009889@qq.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Ardenet <1213193613@qq.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: Mizuyoru_TW <mizuyoru.tw@gmail.com>
Co-authored-by: Robin Dadswell <robin@robindadswell.tech>
Co-authored-by: Rodion <rodyon009@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: farebyting <farelbyting@gmail.com>
Co-authored-by: mryx007 <mryx@mail.de>
Co-authored-by: sagstickler <sags.tickler.0v@icloud.com>
Co-authored-by: thelooter <evekolb2204@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_HANS/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_Hans/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_TW/
Translation: Servarr/Lidarr
2024-12-04 17:37:40 +02:00
Bogdan
1f76f6cb19 Fixed: Trimming disabled logs database
(cherry picked from commit d5dff8e8d6301b661a713702e1c476705423fc4f)
2024-12-04 17:36:14 +02:00
Jared
cc409d50f5 New: Config file setting to disable log database
(cherry picked from commit 92eab4b2e26741b7f20b6b7177418402cb15a3aa)
2024-12-04 17:36:14 +02:00
Mark McDowall
14716a1405 New: Support for new SABnzbd history retention values
(cherry picked from commit e361f18837d98c089f7dc9c0190221ca8e2cf225)
2024-12-04 17:33:23 +02:00
Mark McDowall
f3a697ca68 Deluge communication improvements
(cherry picked from commit 183b8b574a4dd948b5fada94d0e645b87710f223)
2024-12-04 17:33:03 +02:00
soup
f87a8fa9f5 New: Add config file setting for CGNAT authentication bypass
(cherry picked from commit 4c41a4f368046f73f82306bbd73bec992392938b)
2024-12-04 17:32:39 +02:00
Bogdan
b298bfd932 Fix tags help message 2024-12-04 16:04:39 +02:00
Bogdan
ecb7d9f6a6 Fix adding delay profile error message 2024-12-04 15:44:36 +02:00
Gylesie
eef55f65c6 Remove unnecessary heap allocations in local IP check
(cherry picked from commit ed536a85ad5f2062bf6f01f80efddb19fa935f63)
2024-12-02 02:33:25 +02:00
d-rez
beabad5e3a
Fixed: Updated Base Url for Redacted (#5250) 2024-11-28 00:14:39 +02:00
Mark McDowall
2b1684a793 Webpack web target
(cherry picked from commit a90866a73e6cff9a286c23e60c74672f4c0d317a)
2024-11-27 12:24:16 +02:00
Mark McDowall
f23d75d031 Fixed: Normalize unicode characters when comparing paths for equality
(cherry picked from commit ceeec091f85d0094e07537b7f62f18292655a710)
2024-11-17 11:33:58 +02:00
Mark McDowall
abe0090f94 Fixed: Allow files to be moved from Torrent Blackhole even when remove is disabled
(cherry picked from commit f739fd0900695e2ff312d13985c87d84ae00ea75)
2024-11-17 11:33:46 +02:00
Bogdan
8d32a532e4 Pin ReportGenerator in Azure Pipelines for .NET 6
(cherry picked from commit 50ce480abf043140e209d2d2959fbea8dd5dd2ab)
2024-11-15 15:46:49 -06:00
Weblate
a3b78aacdc Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translation: Servarr/Lidarr
2024-11-07 16:57:42 +02:00
Weblate
c43a141b65 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/lv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_TW/
Translation: Servarr/Lidarr
2024-11-07 08:39:10 -06:00
Servarr
d2f5feab5d Automated API Docs update 2024-11-06 17:15:50 -06:00
Mark McDowall
cfb517a90f Fixed: Filtering queue by multiple qualities
(cherry picked from commit b8af3af9f16db96337832c2989f4e7ff3dc2ed30)

Closes #5223
2024-11-04 19:04:13 +02:00
Bogdan
3f81e0254f Fixed: Loading queue with pending releases for deleted artists
(cherry picked from commit 38c0135d7cd05b22bede934f8571c439dcf70a88)

Closes #5214
2024-11-04 18:59:04 +02:00
Bogdan
29d17c6347 Fixed: Root folder existence for import lists health check
Closes #5218
2024-11-04 18:57:10 +02:00
Bogdan
23f7dc3d3c Show an artist path as example in Mount Health Check
Closes #5225
2024-11-04 18:54:11 +02:00
Mark McDowall
e39e990696 New: Use instance name in PWA manifest
(cherry picked from commit 1fcfb88d2aa0126c4b3c878c8e310311ea57d04d)

Closes #5203
2024-11-04 18:46:05 +02:00
Mark McDowall
0c2ede48e8 Fixed: PWA Manifest with URL base
(cherry picked from commit aedcd046fc4fc621dae4b231cc80d4b269a69177)

Closes #5203

Fixed: PWA Manifest images

(cherry picked from commit da7d17f5e826d5273dba0b4f73227ffc8ed8a6c7)

Closes #5030
2024-11-04 18:45:33 +02:00
Mark McDowall
ca23ac3011 Rename Manage Custom Formats to Manage Formats
(cherry picked from commit 0f225b05c00add562c9a6aa8cc4cf494e83176c1)

Closes #5207
2024-11-04 18:40:17 +02:00
Bogdan
e50e79167a Fixed: Cleaning the French preposition 'à' from names
(cherry picked from commit 22005dc8c500cd77e4a710248582cd4a0036988f)

Closes #5213
2024-11-04 18:39:09 +02:00
Weblate
fd3f493eb6 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Lars <lars.erik.heloe@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: mytelegrambot <lacsonluxur@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translation: Servarr/Lidarr
2024-11-04 18:30:15 +02:00
Mark McDowall
f6d3481e38 New: Add individual edit to Manage Custom Formats
(cherry picked from commit e006b405323c276eb5b7f2dd97b97c80394a6930)
2024-11-04 18:27:55 +02:00
Mark McDowall
e04c28fe2d Use current time for cache break in development
(cherry picked from commit 020ed32fcfab1c6fbe57af5ea650300272c93fd7)
2024-11-04 18:27:25 +02:00
Bogdan
030300c896 Bump version to 2.8.1 2024-11-03 11:42:29 +02:00
Weblate
50872f3629 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Daniel <statoxxl@gmail.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: HUi <huynguyeexn@gmail.com>
Co-authored-by: Kuzmich <kuzmich55@gmail.com>
Co-authored-by: Moon55 <dylan.gurdak@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: http://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translation: Servarr/Lidarr
2024-11-02 21:13:14 +02:00
Bogdan
3232e9ab94 Fixed: Initial state for qBittorrent v5.0
(cherry picked from commit ff724b7f4099284b8062f1625cf07b7822782edf)

Closes #5187
2024-10-27 09:27:40 +02:00
Bogdan
8623b4410c Inherit trigger from pushed command models
(cherry picked from commit 0bc4903954b955fce0c368ef7fd2a6f3761d6a93)

Closes #5181
2024-10-27 09:19:04 +02:00
Bogdan
a843a46fbe Improve message for grab errors due to no matching tags
Co-authored-by: zakary <zak@ary.dev>
(cherry picked from commit df672487cf1d5f067849367a2bfb0068defc315d)

Closes #5182
2024-10-27 09:16:10 +02:00
Hadrien Patte
54a758a1b8 Use OperatingSystem class to get OS information
(cherry picked from commit 135b5c2ddd8f0a274b0d59eb07f75aaf1446b9da)
2024-10-27 09:13:46 +02:00
Bogdan
ca5379f817 Fixed: Natural sorting for tags list in the UI
(cherry picked from commit 10b55bbee656774a81541904d6dbb2fd5c8c9b7a)
2024-10-27 04:57:15 +02:00
Bogdan
96b51a02e2 Fixed: Status check for completed directories in Deluge
(cherry picked from commit 33139d4b53c1adad769c7e2b0510e8990c66b84a)
2024-10-27 04:57:00 +02:00
Bogdan
f7acd57f73 Cleanse exceptions in event logs
(cherry picked from commit 404e6d68ea526ab521cd39ecda1bf3b02285765d)
2024-10-27 04:56:38 +02:00
Bogdan
20eb61dbc6 Bump version to 2.8.0 2024-10-26 09:11:29 +03:00
Weblate
cb6608975e Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Ardenet <1213193613@qq.com>
Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/id/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_TW/
Translation: Servarr/Lidarr
2024-10-26 07:50:24 +03:00
Bogdan
2db04a3452 Rename unused CancelMessageText 2024-10-20 11:11:10 +03:00
Bogdan
5cead5f7ff Bump version to 2.7.1 2024-10-20 08:04:01 +03:00
Weblate
850c08dda3 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: JoseFilipeFerreira <jose.filipe.matos.ferreira@gmail.com>
Co-authored-by: Kuzmich <kuzmich55@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translation: Servarr/Lidarr
2024-10-20 06:07:14 +03:00
Bogdan
f005695b48 Skip spotify mapping tests 2024-10-20 06:05:35 +03:00
Mark McDowall
55626594c5 New: Allow major version updates to be installed
(cherry picked from commit 0e95ba2021b23cc65bce0a0620dd48e355250dab)
2024-10-20 06:05:35 +03:00
Bogdan
c645afc389 Bump frontend packages 2024-10-13 23:32:20 +03:00
Bogdan
33d6169882 Bump dotnet to 6.0.35 2024-10-13 23:32:20 +03:00
Hadrien Patte
c750f4764f
Sync OsInfo with upstream (#5163) 2024-10-09 19:52:58 +03:00
Bogdan
c6c52c4117 Bump version to 2.7.0 2024-10-09 14:06:33 +03:00
Servarr
0e9a5fbd04 Automated API Docs update 2024-10-08 15:48:17 +03:00
Weblate
f3cd49a2da Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Ardenet <1213193613@qq.com>
Co-authored-by: FloatStream <1213193613@qq.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: Mathias <mathias@rodilbach.dk>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: angelsky11 <angelsky11@gmail.com>
Co-authored-by: anne <gagatebis@hotmail.com>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: jsain <josip.sain@gmail.com>
Co-authored-by: liuwqq <843384478@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2024-10-08 02:20:10 +03:00
Mark McDowall
1a74118d6b New: Use 307 redirect for requests missing URL Base
(cherry picked from commit 39074b0b1d040969f86d787c2346d5ed5a9f72dc)
2024-10-08 02:19:48 +03:00
Bogdan
9850823298 Fixed: Error updating providers with ID missing from JSON
(cherry picked from commit c435fcd685cc97e98d14f747227eefd39e4d1164)
2024-10-08 02:19:36 +03:00
Bogdan
bd7d25f963 Fixed: Sorting by title and release dates in Select Album modal
Fixes #5145
Closes #5125

Co-authored-by: Mark McDowall <mark@mcdowall.ca>
2024-10-07 16:49:27 +03:00
Bogdan
3a5012655e Bump macOS runner version to 13 2024-10-06 16:22:42 +03:00
Bogdan
ba55a4778a Bump version to 2.6.4 2024-10-06 12:03:57 +03:00
Servarr
ff91589f73 Automated API Docs update 2024-10-03 21:13:18 +03:00
Bogdan
2caba01123 Fix tests for nested artist folders 2024-10-03 15:18:59 +03:00
Bogdan
4588bc4a7e Fixed: Manual importing to nested artist folders 2024-10-03 14:47:36 +03:00
ManiMatter
3381ffc311
New: Last Searched column on Wanted screens (#5084)
* Added lastSearchTime to API & Wanted Screens.
2024-10-02 22:52:27 +03:00
ManiMatter
f705603211
Updated .gitignore to remove duplications and add '_temp*' (#5142) 2024-10-02 22:50:43 +03:00
Bogdan
715274bcc7 Improve indexer name for RED 2024-10-02 09:35:45 +03:00
Bogdan
9b063aa291 Bump version to 2.6.3 2024-09-29 08:19:05 +03:00
Robin Dadswell
af4ff00476 Fixed: Telegram log message including token
(cherry picked from commit a7cb264cc8013d9a56aee7d5e41acfd76cde5f96)
2024-09-28 05:06:49 +03:00
Bogdan
b14c647c86 Display naming example errors when all fields are empty
(cherry picked from commit 768af433d1655c587a9eee9b100f306ba4345f88)
2024-09-28 05:06:23 +03:00
Bogdan
00cca22dc7 Fixed: Adding individual albums for existing artists 2024-09-23 03:48:14 +03:00
Emmanuel Ferdman
73fddd5201 Update license reference
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-09-22 21:55:42 +03:00
Bogdan
b7e5a745a1 Bump version to 2.6.2 2024-09-22 07:46:32 +03:00
Servarr
b67533bccf Automated API Docs update 2024-09-22 05:22:37 +03:00
Bogdan
21d9ecccd6 Fixed: Improve validation for individual album adding 2024-09-22 04:48:21 +03:00
momo
34c9300cbf Fix description for API key as query parameter
(cherry picked from commit 30c36fdc3baa686102ff124833c7963fc786f251)
2024-09-21 21:18:39 +03:00
Mark McDowall
3e5af06622 Fixed: Unable to login when instance name contained brackets 2024-09-21 02:51:27 +03:00
Bogdan
ccce4f5cc0
New: Show warning in queue if download contains executable or archive file and no audio file was detected (#5106)
* Improve handling of releases without audio files

New: Show warning in queue if download contains executable or archive file and no audio file was detected

(cherry picked from commit b15b6a079846b21cac8476820fce9cde81732291)

* New: Add additional archive exentions

(cherry picked from commit 750a9353f82da4e016bee25e0c625cd6d8613b57)

---------

Co-authored-by: Mark McDowall <mark@mcdowall.ca>
2024-09-18 01:26:48 +03:00
Mark McDowall
5947b4642c New: Check for available space before grabbing
(cherry picked from commit 4b5ff3927d3c123f9e3a2bc74328323fab1b0745)

Closes #5095
2024-09-17 17:40:56 +03:00
Mark McDowall
856ac2ffa5 New: Add exception to SSL Certificate validation message
(cherry picked from commit d84c4500949a530fac92d73f7f2f8e8462b37244)

Closes #5103
2024-09-17 17:40:56 +03:00
Bogdan
b2a4c75cce Fixed: Refresh tags after updating autotags
(cherry picked from commit 6a332b40ac94e6e7c23217074da8e18e0ca3a319)

Closes #5093
2024-09-17 17:40:56 +03:00
Mark McDowall
2818f4e073 New: Use instance name in forms authentication cookie name
(cherry picked from commit 97ebaf279650082c6baee9563ef179921c5ed25a)

Closes #5102
2024-09-17 17:40:56 +03:00
Weblate
60fe75877b Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: genoher <genoher@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translation: Servarr/Lidarr
2024-09-17 17:16:04 +03:00
Bogdan
2170ada8a2 Fixed: Linking autotags with tag specification to all tags
(cherry picked from commit a929548ae38d2c5504b961f22131897508d26470)
2024-09-16 17:03:47 +03:00
Bogdan
c26c0d5bd6 Bump version to 2.6.1 2024-09-15 15:55:04 +03:00
Weblate
d17c6a9b3e Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: FloatStream <1213193613@qq.com>
Co-authored-by: Kuzmich55 <kuzmich55@gmail.com>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2024-09-10 23:46:26 +03:00
Bogdan
af6c0cc6f5 Display secondary types on album details 2024-09-08 17:12:30 +03:00
Bogdan
21344361e4 Add weblate widget 2024-09-08 11:30:19 +03:00
Weblate
8efb602531 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Dream <seth.gecko.rr@gmail.com>
Co-authored-by: FloatStream <1213193613@qq.com>
Co-authored-by: Gabriel Markowski <gmarkowski62@gmail.com>
Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Jason54 <jason54700.jg@gmail.com>
Co-authored-by: Kerk en IT <info@kerkenit.nl>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: MattiaPell <mattiapellegrini16@gmail.com>
Co-authored-by: Nota Inutilis <hugo@notainutilis.fr>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/lidarr/zh_CN/
Translation: Servarr/Lidarr
2024-09-04 19:45:28 +03:00
Bogdan
bd5f171fa9 Bump version to 2.6.0 2024-09-01 10:29:06 +03:00
374 changed files with 10162 additions and 5543 deletions

View file

@ -6,7 +6,7 @@
"features": {
"ghcr.io/devcontainers/features/node:1": {
"nodeGypDependencies": true,
"version": "16",
"version": "20",
"nvmVersion": "latest"
}
},

View file

@ -60,6 +60,7 @@ body:
- Master
- Develop
- Nightly
- Plugins (experimental)
- Other (This issue will be closed)
validations:
required: true

35
.gitignore vendored
View file

@ -121,6 +121,7 @@ _artifacts
_rawPackage/
_dotTrace*
_tests/
_temp*
*.Result.xml
coverage*.xml
coverage*.json
@ -139,12 +140,6 @@ project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
#VS outout folders
bin
obj
output/*
# macOS metadata files
._*
.DS_Store
@ -163,34 +158,12 @@ Thumbs.db
/tools/Addins/*
packages.config.md5sum
# Common IntelliJ Platform excludes
# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/.idea.Radarr.Posix
**/.idea/.idea.Radarr.Windows
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# ignore node_modules symlink
node_modules
node_modules.nosync
# API doc generation
.config/
# Ignore Jetbrains IntelliJ Workspace Directories
.idea/

View file

@ -1,6 +1,7 @@
# Lidarr
[![Build Status](https://dev.azure.com/Lidarr/Lidarr/_apis/build/status/lidarr.Lidarr?branchName=develop)](https://dev.azure.com/Lidarr/Lidarr/_build/latest?definitionId=1&branchName=develop)
[![Translation status](https://translate.servarr.com/widget/servarr/lidarr/svg-badge.svg)](https://translate.servarr.com/engage/servarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/lidarr.svg)](https://wiki.servarr.com/lidarr/installation#docker)
![Github Downloads](https://img.shields.io/github/downloads/lidarr/lidarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/lidarr/backers/badge.svg)](#backers)
@ -8,6 +9,9 @@
Lidarr is a music collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new tracks from your favorite artists and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.
> [!WARNING]
> NOTICE - The Lidarr Metadata Server is currently down impacting adding artists, etc. Please follow [GHI 5498](https://github.com/Lidarr/Lidarr/issues/5498) or see Discord for detaila.
## Major Features Include:
* Support for major platforms: Windows, Linux, macOS, Raspberry Pi, etc.

View file

@ -9,18 +9,18 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '2.5.3'
majorVersion: '2.13.3'
minorVersion: $[counter('minorVersion', 1076)]
lidarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(lidarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '6.0.424'
dotnetVersion: '6.0.427'
nodeVersion: '20.X'
innoVersion: '6.2.0'
windowsImage: 'windows-2022'
linuxImage: 'ubuntu-20.04'
macImage: 'macOS-12'
linuxImage: 'ubuntu-22.04'
macImage: 'macOS-13'
trigger:
branches:
@ -1120,19 +1120,19 @@ stages:
vmImage: ${{ variables.windowsImage }}
steps:
- checkout: self # Need history for Sonar analysis
- task: SonarCloudPrepare@2
- task: SonarCloudPrepare@3
env:
SONAR_SCANNER_OPTS: ''
inputs:
SonarCloud: 'SonarCloud'
organization: 'lidarr'
scannerMode: 'CLI'
scannerMode: 'cli'
configMode: 'manual'
cliProjectKey: 'lidarr_Lidarr.UI'
cliProjectName: 'LidarrUI'
cliProjectVersion: '$(lidarrVersion)'
cliSources: './frontend'
- task: SonarCloudAnalyze@2
- task: SonarCloudAnalyze@3
- job: Api_Docs
displayName: API Docs
@ -1208,12 +1208,12 @@ stages:
submodules: true
- powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service
- task: SonarCloudPrepare@2
- task: SonarCloudPrepare@3
condition: eq(variables['System.PullRequest.IsFork'], 'False')
inputs:
SonarCloud: 'SonarCloud'
organization: 'lidarr'
scannerMode: 'MSBuild'
scannerMode: 'dotnet'
projectKey: 'lidarr_Lidarr'
projectName: 'Lidarr'
projectVersion: '$(lidarrVersion)'
@ -1226,10 +1226,10 @@ stages:
./build.sh --backend -f net6.0 -r win-x64
TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
displayName: Coverage Unit Tests
- task: SonarCloudAnalyze@2
- task: SonarCloudAnalyze@3
condition: eq(variables['System.PullRequest.IsFork'], 'False')
displayName: Publish SonarCloud Results
- task: reportgenerator@5
- task: reportgenerator@5.3.11
displayName: Generate Coverage Report
inputs:
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'

15
docs.sh
View file

@ -1,13 +1,18 @@
#!/bin/bash
set -e
FRAMEWORK="net6.0"
PLATFORM=$1
ARCHITECTURE="${2:-x64}"
if [ "$PLATFORM" = "Windows" ]; then
RUNTIME="win-x64"
RUNTIME="win-$ARCHITECTURE"
elif [ "$PLATFORM" = "Linux" ]; then
RUNTIME="linux-x64"
RUNTIME="linux-$ARCHITECTURE"
elif [ "$PLATFORM" = "Mac" ]; then
RUNTIME="osx-x64"
RUNTIME="osx-$ARCHITECTURE"
else
echo "Platform must be provided as first arguement: Windows, Linux or Mac"
echo "Platform must be provided as first argument: Windows, Linux or Mac"
exit 1
fi
@ -35,7 +40,7 @@ dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p
dotnet new tool-manifest
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Lidarr.Api.V1/openapi.json "$outputFolder/net6.0/$RUNTIME/$application" v1 &
dotnet tool run swagger tofile --output ./src/Lidarr.Api.V1/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v1 &
sleep 45

View file

@ -26,6 +26,7 @@ module.exports = (env) => {
const config = {
mode: isProduction ? 'production' : 'development',
devtool: isProduction ? 'source-map' : 'eval-source-map',
target: 'web',
stats: {
children: false
@ -134,6 +135,12 @@ module.exports = (env) => {
{
source: 'frontend/src/Content/robots.txt',
destination: path.join(distFolder, 'Content/robots.txt')
},
// manifest.json and browserconfig.xml
{
source: 'frontend/src/Content/*.(json|xml)',
destination: path.join(distFolder, 'Content')
}
]
}
@ -181,7 +188,7 @@ module.exports = (env) => {
loose: true,
debug: false,
useBuiltIns: 'entry',
corejs: 3
corejs: '3.41'
}
]
]

View file

@ -172,7 +172,8 @@ function HistoryDetails(props) {
if (eventType === 'downloadFailed') {
const {
message
message,
indexer
} = data;
return (
@ -192,6 +193,14 @@ function HistoryDetails(props) {
null
}
{
indexer ? (
<DescriptionListItem
title={translate('Indexer')}
data={indexer}
/>
) : null}
{
message ?
<DescriptionListItem

View file

@ -57,30 +57,40 @@ function QueueStatusCell(props) {
if (status === 'paused') {
iconName = icons.PAUSED;
title = 'Paused';
title = translate('Paused');
}
if (status === 'queued') {
iconName = icons.QUEUED;
title = 'Queued';
title = translate('Queued');
}
if (status === 'completed') {
iconName = icons.DOWNLOADED;
title = 'Downloaded';
title = translate('Downloaded');
if (trackedDownloadState === 'importBlocked') {
title += ` - ${translate('UnableToImportAutomatically')}`;
iconKind = kinds.WARNING;
}
if (trackedDownloadState === 'importFailed') {
title += ` - ${translate('ImportFailed', { sourceTitle })}`;
iconKind = kinds.WARNING;
}
if (trackedDownloadState === 'importPending') {
title += ' - Waiting to Import';
title += ` - ${translate('WaitingToImport')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'importing') {
title += ' - Importing';
title += ` - ${translate('Importing')}`;
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'failedPending') {
title += ' - Waiting to Process';
title += ` - ${translate('WaitingToProcess')}`;
iconKind = kinds.DANGER;
}
}
@ -91,36 +101,38 @@ function QueueStatusCell(props) {
if (status === 'delay') {
iconName = icons.PENDING;
title = 'Pending';
title = translate('Pending');
}
if (status === 'downloadClientUnavailable') {
iconName = icons.PENDING;
iconKind = kinds.WARNING;
title = 'Pending - Download client is unavailable';
title = translate('PendingDownloadClientUnavailable');
}
if (status === 'failed') {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = 'Download failed';
title = translate('DownloadFailed');
}
if (status === 'warning') {
iconName = icons.DOWNLOADING;
iconKind = kinds.WARNING;
title = `Download warning: ${errorMessage || 'check download client for more details'}`;
const warningMessage =
errorMessage || translate('CheckDownloadClientForDetails');
title = translate('DownloadWarning', { warningMessage });
}
if (hasError) {
if (status === 'completed') {
iconName = icons.DOWNLOAD;
iconKind = kinds.DANGER;
title = `Import failed: ${sourceTitle}`;
title = translate('ImportFailed', { sourceTitle });
} else {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = 'Download failed';
title = translate('DownloadFailed');
}
}

View file

@ -8,17 +8,17 @@ function ArtistMonitorNewItemsOptionsPopoverContent() {
<DescriptionList>
<DescriptionListItem
title={translate('AllAlbums')}
data="Monitor all new albums"
data={translate('MonitorAllAlbums')}
/>
<DescriptionListItem
title={translate('NewAlbums')}
data="Monitor new albums released after the newest existing album"
data={translate('MonitorNewAlbumsData')}
/>
<DescriptionListItem
title={translate('None')}
data="Don't monitor any new albums"
data={translate('MonitorNoAlbumsData')}
/>
</DescriptionList>
);

View file

@ -20,6 +20,7 @@ interface Album extends ModelBase {
monitored: boolean;
releaseDate: string;
statistics: Statistics;
lastSearchTime?: string;
isSaving?: boolean;
}

View file

@ -121,6 +121,8 @@
.releaseDate,
.sizeOnDisk,
.albumType,
.secondaryTypes,
.qualityProfileName,
.links,
.tags {

View file

@ -3,6 +3,7 @@
interface CssExports {
'albumNavigationButton': string;
'albumNavigationButtons': string;
'albumType': string;
'alternateTitlesIconContainer': string;
'backdrop': string;
'backdropOverlay': string;
@ -20,6 +21,7 @@ interface CssExports {
'overview': string;
'qualityProfileName': string;
'releaseDate': string;
'secondaryTypes': string;
'sizeOnDisk': string;
'tags': string;
'title': string;

View file

@ -192,6 +192,7 @@ class AlbumDetails extends Component {
duration,
overview,
albumType,
secondaryTypes,
statistics = {},
monitored,
releaseDate,
@ -204,6 +205,7 @@ class AlbumDetails extends Component {
isFetching,
isPopulated,
albumsError,
tracksError,
trackFilesError,
hasTrackFiles,
shortDateFormat,
@ -396,10 +398,11 @@ class AlbumDetails extends Component {
<div className={styles.details}>
<div>
{
!!duration &&
duration ?
<span className={styles.duration}>
{formatDuration(duration)}
</span>
</span> :
null
}
<HeartRating
@ -418,14 +421,15 @@ class AlbumDetails extends Component {
title={translate('ReleaseDate')}
size={sizes.LARGE}
>
<Icon
name={icons.CALENDAR}
size={17}
/>
<span className={styles.releaseDate}>
{moment(releaseDate).format(shortDateFormat)}
</span>
<div>
<Icon
name={icons.CALENDAR}
size={17}
/>
<span className={styles.releaseDate}>
{moment(releaseDate).format(shortDateFormat)}
</span>
</div>
</Label>
<Tooltip
@ -434,16 +438,15 @@ class AlbumDetails extends Component {
className={styles.detailsLabel}
size={sizes.LARGE}
>
<Icon
name={icons.DRIVE}
size={17}
/>
<span className={styles.sizeOnDisk}>
{
formatBytes(sizeOnDisk || 0)
}
</span>
<div>
<Icon
name={icons.DRIVE}
size={17}
/>
<span className={styles.sizeOnDisk}>
{formatBytes(sizeOnDisk)}
</span>
</div>
</Label>
}
tooltip={
@ -459,32 +462,55 @@ class AlbumDetails extends Component {
className={styles.detailsLabel}
size={sizes.LARGE}
>
<Icon
name={monitored ? icons.MONITORED : icons.UNMONITORED}
size={17}
/>
<span className={styles.qualityProfileName}>
{monitored ? translate('Monitored') : translate('Unmonitored')}
</span>
<div>
<Icon
name={monitored ? icons.MONITORED : icons.UNMONITORED}
size={17}
/>
<span className={styles.qualityProfileName}>
{monitored ? translate('Monitored') : translate('Unmonitored')}
</span>
</div>
</Label>
{
!!albumType &&
albumType ?
<Label
className={styles.detailsLabel}
title={translate('Type')}
size={sizes.LARGE}
>
<Icon
name={icons.INFO}
size={17}
/>
<div>
<Icon
name={icons.INFO}
size={17}
/>
<span className={styles.albumType}>
{albumType}
</span>
</div>
</Label> :
null
}
<span className={styles.qualityProfileName}>
{albumType}
</span>
</Label>
{
secondaryTypes.length ?
<Label
className={styles.detailsLabel}
title={translate('SecondaryTypes')}
size={sizes.LARGE}
>
<div>
<Icon
name={icons.INFO}
size={17}
/>
<span className={styles.secondaryTypes}>
{secondaryTypes.join(', ')}
</span>
</div>
</Label> :
null
}
<Tooltip
@ -493,14 +519,15 @@ class AlbumDetails extends Component {
className={styles.detailsLabel}
size={sizes.LARGE}
>
<Icon
name={icons.EXTERNAL_LINK}
size={17}
/>
<span className={styles.links}>
{translate('Links')}
</span>
<div>
<Icon
name={icons.EXTERNAL_LINK}
size={17}
/>
<span className={styles.links}>
{translate('Links')}
</span>
</div>
</Label>
}
tooltip={
@ -526,8 +553,9 @@ class AlbumDetails extends Component {
<div className={styles.contentContainer}>
{
!isPopulated && !albumsError && !trackFilesError &&
<LoadingIndicator />
!isPopulated && !albumsError && !tracksError && !trackFilesError ?
<LoadingIndicator /> :
null
}
{
@ -538,6 +566,14 @@ class AlbumDetails extends Component {
null
}
{
!isFetching && tracksError ?
<Alert kind={kinds.DANGER}>
{translate('TracksLoadError')}
</Alert> :
null
}
{
!isFetching && trackFilesError ?
<Alert kind={kinds.DANGER}>
@ -566,6 +602,14 @@ class AlbumDetails extends Component {
</div>
}
{
isPopulated && !media.length ?
<Alert kind={kinds.WARNING}>
{translate('NoMediumInformation')}
</Alert> :
null
}
</div>
<OrganizePreviewModalConnector
@ -632,6 +676,7 @@ AlbumDetails.propTypes = {
duration: PropTypes.number,
overview: PropTypes.string,
albumType: PropTypes.string.isRequired,
secondaryTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
statistics: PropTypes.object.isRequired,
releaseDate: PropTypes.string.isRequired,
ratings: PropTypes.object.isRequired,
@ -658,6 +703,8 @@ AlbumDetails.propTypes = {
};
AlbumDetails.defaultProps = {
secondaryTypes: [],
statistics: {},
isSaving: false
};

View file

@ -29,7 +29,7 @@ import LogsTableConnector from 'System/Events/LogsTableConnector';
import Logs from 'System/Logs/Logs';
import Status from 'System/Status/Status';
import Tasks from 'System/Tasks/Tasks';
import UpdatesConnector from 'System/Updates/UpdatesConnector';
import Updates from 'System/Updates/Updates';
import UnmappedFilesTableConnector from 'UnmappedFiles/UnmappedFilesTableConnector';
import getPathWithUrlBase from 'Utilities/getPathWithUrlBase';
import CutoffUnmetConnector from 'Wanted/CutoffUnmet/CutoffUnmetConnector';
@ -248,7 +248,7 @@ function AppRoutes(props) {
<Route
path="/system/updates"
component={UpdatesConnector}
component={Updates}
/>
<Route

View file

@ -44,6 +44,7 @@ export interface CustomFilter {
}
export interface AppSectionState {
version: string;
dimensions: {
isSmallScreen: boolean;
width: number;

View file

@ -13,13 +13,16 @@ import MetadataProfile from 'typings/MetadataProfile';
import Notification from 'typings/Notification';
import QualityProfile from 'typings/QualityProfile';
import RootFolder from 'typings/RootFolder';
import { UiSettings } from 'typings/UiSettings';
import General from 'typings/Settings/General';
import UiSettings from 'typings/Settings/UiSettings';
export interface DownloadClientAppState
extends AppSectionState<DownloadClient>,
AppSectionDeleteState,
AppSectionSaveState {}
export type GeneralAppState = AppSectionItemState<General>;
export interface ImportListAppState
extends AppSectionState<ImportList>,
AppSectionDeleteState,
@ -59,6 +62,7 @@ interface SettingsAppState {
advancedSettings: boolean;
customFormats: CustomFormatAppState;
downloadClients: DownloadClientAppState;
general: GeneralAppState;
importLists: ImportListAppState;
indexerFlags: IndexerFlagSettingsAppState;
indexers: IndexerAppState;

View file

@ -1,9 +1,12 @@
import SystemStatus from 'typings/SystemStatus';
import { AppSectionItemState } from './AppSectionState';
import Update from 'typings/Update';
import AppSectionState, { AppSectionItemState } from './AppSectionState';
export type SystemStatusAppState = AppSectionItemState<SystemStatus>;
export type UpdateAppState = AppSectionState<Update>;
interface SystemAppState {
updates: UpdateAppState;
status: SystemStatusAppState;
}

View file

@ -149,9 +149,7 @@ class AlbumRow extends Component {
if (name === 'secondaryTypes') {
return (
<TableRowCell key={name}>
{
secondaryTypes
}
{secondaryTypes.join(', ')}
</TableRowCell>
);
}

View file

@ -15,7 +15,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Popover from 'Components/Tooltip/Popover';
import { icons, inputTypes, kinds, tooltipPositions } from 'Helpers/Props';
import { icons, inputTypes, kinds, sizes, tooltipPositions } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './EditArtistModalContent.css';
@ -93,7 +93,7 @@ class EditArtistModalContent extends Component {
<ModalBody>
<Form {...otherProps}>
<FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
{translate('Monitored')}
</FormLabel>
@ -107,9 +107,10 @@ class EditArtistModalContent extends Component {
/>
</FormGroup>
<FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
{translate('MonitorNewItems')}
<Popover
anchor={
<Icon
@ -132,7 +133,7 @@ class EditArtistModalContent extends Component {
/>
</FormGroup>
<FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
{translate('QualityProfile')}
</FormLabel>
@ -146,10 +147,10 @@ class EditArtistModalContent extends Component {
</FormGroup>
{
showMetadataProfile &&
<FormGroup>
showMetadataProfile ?
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
Metadata Profile
{translate('MetadataProfile')}
<Popover
anchor={
@ -173,10 +174,11 @@ class EditArtistModalContent extends Component {
{...metadataProfileId}
onChange={onInputChange}
/>
</FormGroup>
</FormGroup> :
null
}
<FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
{translate('Path')}
</FormLabel>
@ -189,7 +191,7 @@ class EditArtistModalContent extends Component {
/>
</FormGroup>
<FormGroup>
<FormGroup size={sizes.MEDIUM}>
<FormLabel>
{translate('Tags')}
</FormLabel>
@ -209,7 +211,7 @@ class EditArtistModalContent extends Component {
kind={kinds.DANGER}
onPress={onDeleteArtistPress}
>
Delete
{translate('Delete')}
</Button>
<Button

View file

@ -79,7 +79,7 @@ function ArtistIndexSortMenu(props: SeriesIndexSortMenuProps) {
sortDirection={sortDirection}
onPress={onSortSelect}
>
{translate('Last Album')}
{translate('LastAlbum')}
</SortMenuItem>
<SortMenuItem

View file

@ -6,7 +6,7 @@ import { icons } from 'Helpers/Props';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import dimensions from 'Styles/Variables/dimensions';
import QualityProfile from 'typings/QualityProfile';
import { UiSettings } from 'typings/UiSettings';
import UiSettings from 'typings/Settings/UiSettings';
import formatDateTime from 'Utilities/Date/formatDateTime';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import formatBytes from 'Utilities/Number/formatBytes';

View file

@ -164,7 +164,7 @@ class CalendarLinkModalContent extends Component {
type={inputTypes.TAG}
name="tags"
value={tags}
helpText={translate('TagsHelpText')}
helpText={translate('ICalTagsArtistHelpText')}
onChange={this.onInputChange}
/>
</FormGroup>

View file

@ -3,8 +3,8 @@ import React, { Component } from 'react';
import Alert from 'Components/Alert';
import PathInput from 'Components/Form/PathInput';
import Button from 'Components/Link/Button';
import Link from 'Components/Link/Link';
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';
@ -117,7 +117,7 @@ class FileBrowserModalContent extends Component {
className={styles.mappedDrivesWarning}
kind={kinds.WARNING}
>
Mapped network drives are not available when running as a Windows Service, see the <Link className={styles.faqLink} to="https://wiki.servarr.com/lidarr/faq">FAQ</Link> for more information.
<InlineMarkdown data={translate('MappedNetworkDrivesWindowsService', { url: 'https://wiki.servarr.com/lidarr/faq#why-cant-lidarr-see-my-files-on-a-remote-server' })} />
</Alert>
}

View file

@ -25,7 +25,7 @@ const EVENT_TYPE_OPTIONS = [
{
id: 7,
get name() {
return translate('ImportFailed');
return translate('ImportCompleteFailed');
},
},
{

View file

@ -20,6 +20,8 @@ import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
import TextInput from './TextInput';
import styles from './EnhancedSelectInput.css';
const MINIMUM_DISTANCE_FROM_EDGE = 10;
function isArrowKey(keyCode) {
return keyCode === keyCodes.UP_ARROW || keyCode === keyCodes.DOWN_ARROW;
}
@ -137,18 +139,9 @@ class EnhancedSelectInput extends Component {
// Listeners
onComputeMaxHeight = (data) => {
const {
top,
bottom
} = data.offsets.reference;
const windowHeight = window.innerHeight;
if ((/^botton/).test(data.placement)) {
data.styles.maxHeight = windowHeight - bottom;
} else {
data.styles.maxHeight = top;
}
data.styles.maxHeight = windowHeight - MINIMUM_DISTANCE_FROM_EDGE;
return data;
};
@ -457,6 +450,10 @@ class EnhancedSelectInput extends Component {
order: 851,
enabled: true,
fn: this.onComputeMaxHeight
},
preventOverflow: {
enabled: true,
boundariesElement: 'viewport'
}
}}
>

View file

@ -49,12 +49,12 @@ function getComponent(type) {
case inputTypes.DEVICE:
return DeviceInputConnector;
case inputTypes.PLAYLIST:
return PlaylistInputConnector;
case inputTypes.KEY_VALUE_LIST:
return KeyValueListInput;
case inputTypes.PLAYLIST:
return PlaylistInputConnector;
case inputTypes.MONITOR_ALBUMS_SELECT:
return MonitorAlbumsSelectInput;

View file

@ -1,156 +0,0 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import KeyValueListInputItem from './KeyValueListInputItem';
import styles from './KeyValueListInput.css';
class KeyValueListInput extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isFocused: false
};
}
//
// Listeners
onItemChange = (index, itemValue) => {
const {
name,
value,
onChange
} = this.props;
const newValue = [...value];
if (index == null) {
newValue.push(itemValue);
} else {
newValue.splice(index, 1, itemValue);
}
onChange({
name,
value: newValue
});
};
onRemoveItem = (index) => {
const {
name,
value,
onChange
} = this.props;
const newValue = [...value];
newValue.splice(index, 1);
onChange({
name,
value: newValue
});
};
onFocus = () => {
this.setState({
isFocused: true
});
};
onBlur = () => {
this.setState({
isFocused: false
});
const {
name,
value,
onChange
} = this.props;
const newValue = value.reduce((acc, v) => {
if (v.key || v.value) {
acc.push(v);
}
return acc;
}, []);
if (newValue.length !== value.length) {
onChange({
name,
value: newValue
});
}
};
//
// Render
render() {
const {
className,
value,
keyPlaceholder,
valuePlaceholder,
hasError,
hasWarning
} = this.props;
const { isFocused } = this.state;
return (
<div className={classNames(
className,
isFocused && styles.isFocused,
hasError && styles.hasError,
hasWarning && styles.hasWarning
)}
>
{
[...value, { key: '', value: '' }].map((v, index) => {
return (
<KeyValueListInputItem
key={index}
index={index}
keyValue={v.key}
value={v.value}
keyPlaceholder={keyPlaceholder}
valuePlaceholder={valuePlaceholder}
isNew={index === value.length}
onChange={this.onItemChange}
onRemove={this.onRemoveItem}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
);
})
}
</div>
);
}
}
KeyValueListInput.propTypes = {
className: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.arrayOf(PropTypes.object).isRequired,
hasError: PropTypes.bool,
hasWarning: PropTypes.bool,
keyPlaceholder: PropTypes.string,
valuePlaceholder: PropTypes.string,
onChange: PropTypes.func.isRequired
};
KeyValueListInput.defaultProps = {
className: styles.inputContainer,
value: []
};
export default KeyValueListInput;

View file

@ -0,0 +1,104 @@
import classNames from 'classnames';
import React, { useCallback, useState } from 'react';
import { InputOnChange } from 'typings/inputs';
import KeyValueListInputItem from './KeyValueListInputItem';
import styles from './KeyValueListInput.css';
interface KeyValue {
key: string;
value: string;
}
export interface KeyValueListInputProps {
className?: string;
name: string;
value: KeyValue[];
hasError?: boolean;
hasWarning?: boolean;
keyPlaceholder?: string;
valuePlaceholder?: string;
onChange: InputOnChange<KeyValue[]>;
}
function KeyValueListInput({
className = styles.inputContainer,
name,
value = [],
hasError = false,
hasWarning = false,
keyPlaceholder,
valuePlaceholder,
onChange,
}: KeyValueListInputProps): JSX.Element {
const [isFocused, setIsFocused] = useState(false);
const handleItemChange = useCallback(
(index: number | null, itemValue: KeyValue) => {
const newValue = [...value];
if (index === null) {
newValue.push(itemValue);
} else {
newValue.splice(index, 1, itemValue);
}
onChange({ name, value: newValue });
},
[value, name, onChange]
);
const handleRemoveItem = useCallback(
(index: number) => {
const newValue = [...value];
newValue.splice(index, 1);
onChange({ name, value: newValue });
},
[value, name, onChange]
);
const onFocus = useCallback(() => setIsFocused(true), []);
const onBlur = useCallback(() => {
setIsFocused(false);
const newValue = value.reduce((acc: KeyValue[], v) => {
if (v.key || v.value) {
acc.push(v);
}
return acc;
}, []);
if (newValue.length !== value.length) {
onChange({ name, value: newValue });
}
}, [value, name, onChange]);
return (
<div
className={classNames(
className,
isFocused && styles.isFocused,
hasError && styles.hasError,
hasWarning && styles.hasWarning
)}
>
{[...value, { key: '', value: '' }].map((v, index) => (
<KeyValueListInputItem
key={index}
index={index}
keyValue={v.key}
value={v.value}
keyPlaceholder={keyPlaceholder}
valuePlaceholder={valuePlaceholder}
isNew={index === value.length}
onChange={handleItemChange}
onRemove={handleRemoveItem}
onFocus={onFocus}
onBlur={onBlur}
/>
))}
</div>
);
}
export default KeyValueListInput;

View file

@ -5,13 +5,19 @@
&:last-child {
margin-bottom: 0;
border-bottom: 0;
}
}
.inputWrapper {
.keyInputWrapper {
flex: 1 0 0;
}
.valueInputWrapper {
flex: 1 0 0;
min-width: 40px;
}
.buttonWrapper {
flex: 0 0 22px;
}
@ -20,6 +26,10 @@
.valueInput {
width: 100%;
border: none;
background-color: var(--inputBackgroundColor);
background-color: transparent;
color: var(--textColor);
&::placeholder {
color: var(--helpTextColor);
}
}

View file

@ -2,10 +2,11 @@
// Please do not change this file!
interface CssExports {
'buttonWrapper': string;
'inputWrapper': string;
'itemContainer': string;
'keyInput': string;
'keyInputWrapper': string;
'valueInput': string;
'valueInputWrapper': string;
}
export const cssExports: CssExports;
export default cssExports;

View file

@ -1,124 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import IconButton from 'Components/Link/IconButton';
import { icons } from 'Helpers/Props';
import TextInput from './TextInput';
import styles from './KeyValueListInputItem.css';
class KeyValueListInputItem extends Component {
//
// Listeners
onKeyChange = ({ value: keyValue }) => {
const {
index,
value,
onChange
} = this.props;
onChange(index, { key: keyValue, value });
};
onValueChange = ({ value }) => {
// TODO: Validate here or validate at a lower level component
const {
index,
keyValue,
onChange
} = this.props;
onChange(index, { key: keyValue, value });
};
onRemovePress = () => {
const {
index,
onRemove
} = this.props;
onRemove(index);
};
onFocus = () => {
this.props.onFocus();
};
onBlur = () => {
this.props.onBlur();
};
//
// Render
render() {
const {
keyValue,
value,
keyPlaceholder,
valuePlaceholder,
isNew
} = this.props;
return (
<div className={styles.itemContainer}>
<div className={styles.inputWrapper}>
<TextInput
className={styles.keyInput}
name="key"
value={keyValue}
placeholder={keyPlaceholder}
onChange={this.onKeyChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
</div>
<div className={styles.inputWrapper}>
<TextInput
className={styles.valueInput}
name="value"
value={value}
placeholder={valuePlaceholder}
onChange={this.onValueChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
</div>
<div className={styles.buttonWrapper}>
{
isNew ?
null :
<IconButton
name={icons.REMOVE}
tabIndex={-1}
onPress={this.onRemovePress}
/>
}
</div>
</div>
);
}
}
KeyValueListInputItem.propTypes = {
index: PropTypes.number,
keyValue: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
keyPlaceholder: PropTypes.string.isRequired,
valuePlaceholder: PropTypes.string.isRequired,
isNew: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
onFocus: PropTypes.func.isRequired,
onBlur: PropTypes.func.isRequired
};
KeyValueListInputItem.defaultProps = {
keyPlaceholder: 'Key',
valuePlaceholder: 'Value'
};
export default KeyValueListInputItem;

View file

@ -0,0 +1,89 @@
import React, { useCallback } from 'react';
import IconButton from 'Components/Link/IconButton';
import { icons } from 'Helpers/Props';
import TextInput from './TextInput';
import styles from './KeyValueListInputItem.css';
interface KeyValueListInputItemProps {
index: number;
keyValue: string;
value: string;
keyPlaceholder?: string;
valuePlaceholder?: string;
isNew: boolean;
onChange: (index: number, itemValue: { key: string; value: string }) => void;
onRemove: (index: number) => void;
onFocus: () => void;
onBlur: () => void;
}
function KeyValueListInputItem({
index,
keyValue,
value,
keyPlaceholder = 'Key',
valuePlaceholder = 'Value',
isNew,
onChange,
onRemove,
onFocus,
onBlur,
}: KeyValueListInputItemProps): JSX.Element {
const handleKeyChange = useCallback(
({ value: keyValue }: { value: string }) => {
onChange(index, { key: keyValue, value });
},
[index, value, onChange]
);
const handleValueChange = useCallback(
({ value }: { value: string }) => {
onChange(index, { key: keyValue, value });
},
[index, keyValue, onChange]
);
const handleRemovePress = useCallback(() => {
onRemove(index);
}, [index, onRemove]);
return (
<div className={styles.itemContainer}>
<div className={styles.keyInputWrapper}>
<TextInput
className={styles.keyInput}
name="key"
value={keyValue}
placeholder={keyPlaceholder}
onChange={handleKeyChange}
onFocus={onFocus}
onBlur={onBlur}
/>
</div>
<div className={styles.valueInputWrapper}>
<TextInput
className={styles.valueInput}
name="value"
value={value}
placeholder={valuePlaceholder}
onChange={handleValueChange}
onFocus={onFocus}
onBlur={onBlur}
/>
</div>
<div className={styles.buttonWrapper}>
{isNew ? null : (
<IconButton
name={icons.REMOVE}
tabIndex={-1}
onPress={handleRemovePress}
/>
)}
</div>
</div>
);
}
export default KeyValueListInputItem;

View file

@ -14,6 +14,8 @@ function getType({ type, selectOptionsProviderAction }) {
return inputTypes.CHECK;
case 'device':
return inputTypes.DEVICE;
case 'keyValueList':
return inputTypes.KEY_VALUE_LIST;
case 'playlist':
return inputTypes.PLAYLIST;
case 'password':

View file

@ -83,13 +83,6 @@
}
@media only screen and (max-width: $breakpointMedium) {
.modal.small,
.modal.medium {
width: 90%;
}
}
@media only screen and (max-width: $breakpointSmall) {
.modalContainer {
position: fixed;
}

View file

@ -172,7 +172,7 @@ class SignalRConnector extends Component {
const status = resource.status;
// Both successful and failed commands need to be
// completed, otherwise they spin until they timeout.
// completed, otherwise they spin until they time out.
if (status === 'completed' || status === 'failed') {
this.props.dispatchFinishCommand(resource);
@ -224,10 +224,58 @@ class SignalRConnector extends Component {
repopulatePage('trackFileUpdated');
};
handleDownloadclient = ({ action, resource }) => {
const section = 'settings.downloadClients';
if (action === 'created' || action === 'updated') {
this.props.dispatchUpdateItem({ section, ...resource });
} else if (action === 'deleted') {
this.props.dispatchRemoveItem({ section, id: resource.id });
}
};
handleHealth = () => {
this.props.dispatchFetchHealth();
};
handleImportlist = ({ action, resource }) => {
const section = 'settings.importLists';
if (action === 'created' || action === 'updated') {
this.props.dispatchUpdateItem({ section, ...resource });
} else if (action === 'deleted') {
this.props.dispatchRemoveItem({ section, id: resource.id });
}
};
handleIndexer = ({ action, resource }) => {
const section = 'settings.indexers';
if (action === 'created' || action === 'updated') {
this.props.dispatchUpdateItem({ section, ...resource });
} else if (action === 'deleted') {
this.props.dispatchRemoveItem({ section, id: resource.id });
}
};
handleMetadata = ({ action, resource }) => {
const section = 'settings.metadata';
if (action === 'updated') {
this.props.dispatchUpdateItem({ section, ...resource });
}
};
handleNotification = ({ action, resource }) => {
const section = 'settings.notifications';
if (action === 'created' || action === 'updated') {
this.props.dispatchUpdateItem({ section, ...resource });
} else if (action === 'deleted') {
this.props.dispatchRemoveItem({ section, id: resource.id });
}
};
handleArtist = (body) => {
const action = body.action;
const section = 'artist';

View file

@ -4,7 +4,7 @@
line-height: 1.52857143;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.cell {
white-space: nowrap;
}

View file

@ -7,7 +7,7 @@
white-space: nowrap;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.cell {
white-space: nowrap;
}

View file

@ -10,7 +10,7 @@
border-collapse: collapse;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.tableContainer {
min-width: 100%;
width: fit-content;

View file

@ -9,7 +9,7 @@
margin-left: 10px;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.headerCell {
white-space: nowrap;
}

View file

@ -60,7 +60,7 @@
height: 25px;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.pager {
flex-wrap: wrap;
}

View file

@ -9,7 +9,7 @@
margin-left: 10px;
}
@media only screen and (max-width: $breakpointSmall) {
@media only screen and (max-width: $breakpointMedium) {
.headerCell {
white-space: nowrap;
}

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/Content/Images/Icons/mstile-150x150.png"/>
<TileColor>#00ccff</TileColor>
</tile>
</msapplication>
</browserconfig>

View file

@ -1,19 +0,0 @@
{
"name": "Lidarr",
"icons": [
{
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "../../../../",
"theme_color": "#3a3f51",
"background_color": "#3a3f51",
"display": "standalone"
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="__URL_BASE__/Content/Images/Icons/mstile-150x150.png" />
<TileColor>
#00ccff
</TileColor>
</tile>
</msapplication>
</browserconfig>

View file

@ -0,0 +1,19 @@
{
"name": "__INSTANCE_NAME__",
"icons": [
{
"src": "__URL_BASE__/Content/Images/Icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "__URL_BASE__/Content/Images/Icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "__URL_BASE__/",
"theme_color": "#3a3f51",
"background_color": "#3a3f51",
"display": "standalone"
}

View file

@ -2,8 +2,8 @@ export const AUTO_COMPLETE = 'autoComplete';
export const CAPTCHA = 'captcha';
export const CHECK = 'check';
export const DEVICE = 'device';
export const PLAYLIST = 'playlist';
export const KEY_VALUE_LIST = 'keyValueList';
export const PLAYLIST = 'playlist';
export const MONITOR_ALBUMS_SELECT = 'monitorAlbumsSelect';
export const MONITOR_NEW_ITEMS_SELECT = 'monitorNewItemsSelect';
export const FLOAT = 'float';
@ -34,8 +34,8 @@ export const all = [
CAPTCHA,
CHECK,
DEVICE,
PLAYLIST,
KEY_VALUE_LIST,
PLAYLIST,
MONITOR_ALBUMS_SELECT,
MONITOR_NEW_ITEMS_SELECT,
FLOAT,

View file

@ -11,6 +11,7 @@ import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import { scrollDirections } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import SelectAlbumRow from './SelectAlbumRow';
import styles from './SelectAlbumModalContent.css';
@ -19,6 +20,7 @@ const columns = [
{
name: 'title',
label: () => translate('AlbumTitle'),
isSortable: true,
isVisible: true
},
{
@ -29,6 +31,7 @@ const columns = [
{
name: 'releaseDate',
label: () => translate('ReleaseDate'),
isSortable: true,
isVisible: true
},
{
@ -63,16 +66,22 @@ class SelectAlbumModalContent extends Component {
render() {
const {
items,
onAlbumSelect,
onModalClose,
isFetching,
...otherProps
isPopulated,
error,
items,
sortKey,
sortDirection,
onSortPress,
onAlbumSelect,
onModalClose
} = this.props;
const filter = this.state.filter;
const filterLower = filter.toLowerCase();
const errorMessage = getErrorMessage(error, 'Unable to load albums');
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
@ -83,27 +92,29 @@ class SelectAlbumModalContent extends Component {
className={styles.modalBody}
scrollDirection={scrollDirections.NONE}
>
{
isFetching &&
<LoadingIndicator />
}
<TextInput
className={styles.filterInput}
placeholder={translate('FilterAlbumPlaceholder')}
name="filter"
value={filter}
autoFocus={true}
onChange={this.onFilterChange}
/>
<Scroller
className={styles.scroller}
autoFocus={false}
>
{
{isFetching ? <LoadingIndicator /> : null}
{error ? <div>{errorMessage}</div> : null}
<TextInput
className={styles.filterInput}
placeholder={translate('FilterAlbumPlaceholder')}
name="filter"
value={filter}
autoFocus={true}
onChange={this.onFilterChange}
/>
{isPopulated && !!items.length ? (
<Table
columns={columns}
{...otherProps}
sortKey={sortKey}
sortDirection={sortDirection}
onSortPress={onSortPress}
>
<TableBody>
{
@ -122,7 +133,7 @@ class SelectAlbumModalContent extends Component {
}
</TableBody>
</Table>
}
) : null}
</Scroller>
</ModalBody>
@ -137,8 +148,13 @@ class SelectAlbumModalContent extends Component {
}
SelectAlbumModalContent.propTypes = {
items: PropTypes.arrayOf(PropTypes.object).isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
sortKey: PropTypes.string,
sortDirection: PropTypes.string,
onSortPress: PropTypes.func.isRequired,
onAlbumSelect: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};

View file

@ -3,18 +3,14 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import {
clearInteractiveImportAlbums,
fetchInteractiveImportAlbums,
saveInteractiveImportItem,
setInteractiveImportAlbumsSort,
updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
import { clearAlbums, fetchAlbums, setAlbumsSort } from 'Store/Actions/albumSelectionActions';
import { saveInteractiveImportItem, updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import SelectAlbumModalContent from './SelectAlbumModalContent';
function createMapStateToProps() {
return createSelector(
createClientSideCollectionSelector('interactiveImport.albums'),
createClientSideCollectionSelector('albumSelection'),
(albums) => {
return albums;
}
@ -22,9 +18,9 @@ function createMapStateToProps() {
}
const mapDispatchToProps = {
fetchInteractiveImportAlbums,
setInteractiveImportAlbumsSort,
clearInteractiveImportAlbums,
fetchAlbums,
setAlbumsSort,
clearAlbums,
updateInteractiveImportItem,
saveInteractiveImportItem
};
@ -39,20 +35,20 @@ class SelectAlbumModalContentConnector extends Component {
artistId
} = this.props;
this.props.fetchInteractiveImportAlbums({ artistId });
this.props.fetchAlbums({ artistId });
}
componentWillUnmount() {
// This clears the albums for the queue and hides the queue
// We'll need another place to store albums for manual import
this.props.clearInteractiveImportAlbums();
this.props.clearAlbums();
}
//
// Listeners
onSortPress = (sortKey, sortDirection) => {
this.props.setInteractiveImportAlbumsSort({ sortKey, sortDirection });
this.props.setAlbumsSort({ sortKey, sortDirection });
};
onAlbumSelect = (albumId) => {
@ -82,6 +78,7 @@ class SelectAlbumModalContentConnector extends Component {
return (
<SelectAlbumModalContent
{...this.props}
onSortPress={this.onSortPress}
onAlbumSelect={this.onAlbumSelect}
/>
);
@ -92,9 +89,9 @@ SelectAlbumModalContentConnector.propTypes = {
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
artistId: PropTypes.number.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchInteractiveImportAlbums: PropTypes.func.isRequired,
setInteractiveImportAlbumsSort: PropTypes.func.isRequired,
clearInteractiveImportAlbums: PropTypes.func.isRequired,
fetchAlbums: PropTypes.func.isRequired,
setAlbumsSort: PropTypes.func.isRequired,
clearAlbums: PropTypes.func.isRequired,
saveInteractiveImportItem: PropTypes.func.isRequired,
updateInteractiveImportItem: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired

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,8 @@ class AddNewItem extends Component {
<div className={styles.helpText}>
{translate('FailedLoadingSearchResults')}
</div>
<div>{getErrorMessage(error)}</div>
<Alert kind={kinds.DANGER}>{getErrorMessage(error)}</Alert>
</div> : null
}

View file

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import EditCustomFormatModal from './EditCustomFormatModal';
import EditCustomFormatModalContentConnector from './EditCustomFormatModalContentConnector';
function mapStateToProps() {
return {};
@ -36,6 +37,7 @@ class EditCustomFormatModalConnector extends Component {
}
EditCustomFormatModalConnector.propTypes = {
...EditCustomFormatModalContentConnector.propTypes,
onModalClose: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};

View file

@ -10,11 +10,11 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
import SortDirection from 'Helpers/Props/SortDirection';
import {
bulkDeleteCustomFormats,
bulkEditCustomFormats,
@ -34,7 +34,7 @@ type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageCustomFormatsModalRow
>['onSelectedChange'];
const COLUMNS = [
const COLUMNS: Column[] = [
{
name: 'name',
label: () => translate('Name'),
@ -47,12 +47,15 @@ const COLUMNS = [
isSortable: true,
isVisible: true,
},
{
name: 'actions',
label: '',
isVisible: true,
},
];
interface ManageCustomFormatsModalContentProps {
onModalClose(): void;
sortKey?: string;
sortDirection?: SortDirection;
}
function ManageCustomFormatsModalContent(

View file

@ -4,3 +4,9 @@
word-break: break-all;
}
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 40px;
}

View file

@ -1,6 +1,7 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'actions': string;
'includeCustomFormatWhenRenaming': string;
'name': string;
}

View file

@ -1,10 +1,18 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import IconButton from 'Components/Link/IconButton';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import Column from 'Components/Table/Column';
import TableRow from 'Components/Table/TableRow';
import { icons } from 'Helpers/Props';
import { deleteCustomFormat } from 'Store/Actions/settingsActions';
import { SelectStateInputProps } from 'typings/props';
import translate from 'Utilities/String/translate';
import EditCustomFormatModalConnector from '../EditCustomFormatModalConnector';
import styles from './ManageCustomFormatsModalRow.css';
interface ManageCustomFormatsModalRowProps {
@ -16,6 +24,15 @@ interface ManageCustomFormatsModalRowProps {
onSelectedChange(result: SelectStateInputProps): void;
}
function isDeletingSelector() {
return createSelector(
(state: AppState) => state.settings.customFormats.isDeleting,
(isDeleting) => {
return isDeleting;
}
);
}
function ManageCustomFormatsModalRow(props: ManageCustomFormatsModalRowProps) {
const {
id,
@ -25,7 +42,16 @@ function ManageCustomFormatsModalRow(props: ManageCustomFormatsModalRowProps) {
onSelectedChange,
} = props;
const onSelectedChangeWrapper = useCallback(
const dispatch = useDispatch();
const isDeleting = useSelector(isDeletingSelector());
const [isEditCustomFormatModalOpen, setIsEditCustomFormatModalOpen] =
useState(false);
const [isDeleteCustomFormatModalOpen, setIsDeleteCustomFormatModalOpen] =
useState(false);
const handlelectedChange = useCallback(
(result: SelectStateInputProps) => {
onSelectedChange({
...result,
@ -34,12 +60,33 @@ function ManageCustomFormatsModalRow(props: ManageCustomFormatsModalRowProps) {
[onSelectedChange]
);
const handleEditCustomFormatModalOpen = useCallback(() => {
setIsEditCustomFormatModalOpen(true);
}, [setIsEditCustomFormatModalOpen]);
const handleEditCustomFormatModalClose = useCallback(() => {
setIsEditCustomFormatModalOpen(false);
}, [setIsEditCustomFormatModalOpen]);
const handleDeleteCustomFormatPress = useCallback(() => {
setIsEditCustomFormatModalOpen(false);
setIsDeleteCustomFormatModalOpen(true);
}, [setIsEditCustomFormatModalOpen, setIsDeleteCustomFormatModalOpen]);
const handleDeleteCustomFormatModalClose = useCallback(() => {
setIsDeleteCustomFormatModalOpen(false);
}, [setIsDeleteCustomFormatModalOpen]);
const handleConfirmDeleteCustomFormat = useCallback(() => {
dispatch(deleteCustomFormat({ id }));
}, [id, dispatch]);
return (
<TableRow>
<TableSelectCell
id={id}
isSelected={isSelected}
onSelectedChange={onSelectedChangeWrapper}
onSelectedChange={handlelectedChange}
/>
<TableRowCell className={styles.name}>{name}</TableRowCell>
@ -47,6 +94,31 @@ function ManageCustomFormatsModalRow(props: ManageCustomFormatsModalRowProps) {
<TableRowCell className={styles.includeCustomFormatWhenRenaming}>
{includeCustomFormatWhenRenaming ? translate('Yes') : translate('No')}
</TableRowCell>
<TableRowCell className={styles.actions}>
<IconButton
name={icons.EDIT}
onPress={handleEditCustomFormatModalOpen}
/>
</TableRowCell>
<EditCustomFormatModalConnector
id={id}
isOpen={isEditCustomFormatModalOpen}
onModalClose={handleEditCustomFormatModalClose}
onDeleteCustomFormatPress={handleDeleteCustomFormatPress}
/>
<ConfirmModal
isOpen={isDeleteCustomFormatModalOpen}
kind="danger"
title={translate('DeleteCustomFormat')}
message={translate('DeleteCustomFormatMessageText', { name })}
confirmLabel={translate('Delete')}
isSpinning={isDeleting}
onConfirm={handleConfirmDeleteCustomFormat}
onCancel={handleDeleteCustomFormatModalClose}
/>
</TableRow>
);
}

View file

@ -12,7 +12,7 @@ function ManageCustomFormatsToolbarButton() {
return (
<>
<PageToolbarButton
label={translate('ManageCustomFormats')}
label={translate('ManageFormats')}
iconName={icons.MANAGE}
onPress={openManageModal}
/>

View file

@ -7,8 +7,8 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import ProviderFieldFormGroup from 'Components/Form/ProviderFieldFormGroup';
import Button from 'Components/Link/Button';
import Link from 'Components/Link/Link';
import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
@ -52,12 +52,13 @@ function EditSpecificationModalContent(props) {
fields && fields.some((x) => x.label === translate('CustomFormatsSpecificationRegularExpression')) &&
<Alert kind={kinds.INFO}>
<div>
<div dangerouslySetInnerHTML={{ __html: 'This condition matches using Regular Expressions. Note that the characters <code>\\^$.|?*+()[{</code> have special meanings and need escaping with a <code>\\</code>' }} />
{'More details'} <Link to="https://www.regular-expressions.info/tutorial.html">{'Here'}</Link>
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
</div>
<div>
{'Regular expressions can be tested '}
<Link to="http://regexstorm.net/tester">Here</Link>
<InlineMarkdown data={translate('RegularExpressionsTutorialLink', { url: 'https://www.regular-expressions.info/tutorial.html' })} />
</div>
<div>
<InlineMarkdown data={translate('RegularExpressionsCanBeTested', { url: 'http://regexstorm.net/tester' })} />
</div>
</Alert>
}

View file

@ -10,11 +10,11 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
import SortDirection from 'Helpers/Props/SortDirection';
import {
bulkDeleteDownloadClients,
bulkEditDownloadClients,
@ -35,7 +35,7 @@ type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageDownloadClientsModalRow
>['onSelectedChange'];
const COLUMNS = [
const COLUMNS: Column[] = [
{
name: 'name',
label: () => translate('Name'),
@ -82,8 +82,6 @@ const COLUMNS = [
interface ManageDownloadClientsModalContentProps {
onModalClose(): void;
sortKey?: string;
sortDirection?: SortDirection;
}
function ManageDownloadClientsModalContent(

View file

@ -292,7 +292,7 @@ function EditImportListModalContent(props) {
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText={translate('TagsHelpText')}
helpText={translate('ImportListTagsHelpText')}
{...tags}
onChange={onInputChange}
/>

View file

@ -10,11 +10,11 @@ import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import Column from 'Components/Table/Column';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import useSelectState from 'Helpers/Hooks/useSelectState';
import { kinds } from 'Helpers/Props';
import SortDirection from 'Helpers/Props/SortDirection';
import {
bulkDeleteIndexers,
bulkEditIndexers,
@ -35,7 +35,7 @@ type OnSelectedChangeCallback = React.ComponentProps<
typeof ManageIndexersModalRow
>['onSelectedChange'];
const COLUMNS = [
const COLUMNS: Column[] = [
{
name: 'name',
label: () => translate('Name'),
@ -82,8 +82,6 @@ const COLUMNS = [
interface ManageIndexersModalContentProps {
onModalClose(): void;
sortKey?: string;
sortDirection?: SortDirection;
}
function ManageIndexersModalContent(props: ManageIndexersModalContentProps) {

View file

@ -191,26 +191,21 @@ class MediaManagement extends Component {
<FieldSet
legend={translate('Importing')}
>
{
!isWindows &&
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>
{translate('SkipFreeSpaceCheck')}
</FormLabel>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
size={sizes.MEDIUM}
>
<FormLabel>{translate('SkipFreeSpaceCheck')}</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="skipFreeSpaceCheckWhenImporting"
helpText={translate('SkipFreeSpaceCheckWhenImportingHelpText')}
onChange={onInputChange}
{...settings.skipFreeSpaceCheckWhenImporting}
/>
</FormGroup>
}
<FormInputGroup
type={inputTypes.CHECK}
name="skipFreeSpaceCheckWhenImporting"
helpText={translate('SkipFreeSpaceCheckHelpText')}
onChange={onInputChange}
{...settings.skipFreeSpaceCheckWhenImporting}
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}

View file

@ -1,4 +1,3 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@ -15,11 +14,11 @@ function createMapStateToProps() {
(state) => state.settings.advancedSettings,
(state) => state.settings.namingExamples,
createSettingsSectionSelector(SECTION),
(advancedSettings, examples, sectionSettings) => {
(advancedSettings, namingExamples, sectionSettings) => {
return {
advancedSettings,
examples: examples.item,
examplesPopulated: !_.isEmpty(examples.item),
examples: namingExamples.item,
examplesPopulated: namingExamples.isPopulated,
...sectionSettings
};
}

View file

@ -94,9 +94,9 @@ class RootFolder extends Component {
<ConfirmModal
isOpen={this.state.isDeleteRootFolderModalOpen}
kind={kinds.DANGER}
title={translate('DeleteRootFolder')}
message={translate('DeleteRootFolderMessageText', { name })}
confirmLabel={translate('Delete')}
title={translate('RemoveRootFolder')}
message={translate('RemoveRootFolderArtistsMessageText', { name })}
confirmLabel={translate('Remove')}
onConfirm={this.onConfirmDeleteRootFolder}
onCancel={this.onDeleteRootFolderModalClose}
/>

View file

@ -105,7 +105,7 @@ function EditNotificationModalContent(props) {
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText={translate('TagsHelpText')}
helpText={translate('NotificationsTagsArtistHelpText')}
{...tags}
onChange={onInputChange}
/>

View file

@ -87,9 +87,9 @@ function EditDelayProfileModalContent(props) {
{
!isFetching && !!error ?
<div>
{translate('UnableToAddANewQualityProfilePleaseTryAgain')}
</div> :
<Alert kind={kinds.DANGER}>
{translate('AddDelayProfileError')}
</Alert> :
null
}
@ -186,7 +186,7 @@ function EditDelayProfileModalContent(props) {
{
id === 1 ?
<Alert>
{translate('DefaultDelayProfileHelpText')}
{translate('DefaultDelayProfileArtist')}
</Alert> :
<FormGroup>
@ -196,7 +196,7 @@ function EditDelayProfileModalContent(props) {
type={inputTypes.TAG}
name="tags"
{...tags}
helpText={translate('TagsHelpText')}
helpText={translate('DelayProfileArtistTagsHelpText')}
onChange={onInputChange}
/>
</FormGroup>

View file

@ -119,7 +119,7 @@ function EditReleaseProfileModalContent(props) {
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText={translate('TagsHelpText')}
helpText={translate('ReleaseProfileTagArtistHelpText')}
{...tags}
onChange={onInputChange}
/>

View file

@ -24,19 +24,19 @@
height: 20px;
}
.bar {
.track {
top: 9px;
margin: 0 5px;
height: 3px;
background-color: var(--sliderAccentColor);
box-shadow: 0 0 0 #000;
&:nth-child(3n+1) {
&:nth-child(3n + 1) {
background-color: #ddd;
}
}
.handle {
.thumb {
top: 1px;
z-index: 0 !important;
width: 18px;

View file

@ -1,8 +1,6 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'bar': string;
'handle': string;
'kilobitsPerSecond': string;
'quality': string;
'qualityDefinition': string;
@ -10,7 +8,9 @@ interface CssExports {
'sizeLimit': string;
'sizes': string;
'slider': string;
'thumb': string;
'title': string;
'track': string;
}
export const cssExports: CssExports;
export default cssExports;

View file

@ -55,6 +55,27 @@ class QualityDefinition extends Component {
};
}
//
// Control
trackRenderer(props, state) {
return (
<div
{...props}
className={styles.track}
/>
);
}
thumbRenderer(props, state) {
return (
<div
{...props}
className={styles.thumb}
/>
);
}
//
// Listeners
@ -174,6 +195,7 @@ class QualityDefinition extends Component {
<div className={styles.sizeLimit}>
<ReactSlider
className={styles.slider}
min={slider.min}
max={slider.max}
step={slider.step}
@ -182,9 +204,9 @@ class QualityDefinition extends Component {
withTracks={true}
allowCross={false}
snapDragDisabled={true}
className={styles.slider}
trackClassName={styles.bar}
thumbClassName={styles.handle}
pearling={true}
renderThumb={this.thumbRenderer}
renderTrack={this.trackRenderer}
onChange={this.onSliderChange}
onAfterChange={this.onAfterSliderChange}
/>

View file

@ -86,10 +86,10 @@ function EditSpecificationModalContent(props) {
<InlineMarkdown data={translate('ConditionUsingRegularExpressions')} />
</div>
<div>
<InlineMarkdown data={translate('RegularExpressionsTutorialLink')} />
<InlineMarkdown data={translate('RegularExpressionsTutorialLink', { url: 'https://www.regular-expressions.info/tutorial.html' })} />
</div>
<div>
<InlineMarkdown data={translate('RegularExpressionsCanBeTested')} />
<InlineMarkdown data={translate('RegularExpressionsCanBeTested', { url: 'http://regexstorm.net/tester' })} />
</div>
</Alert>
}

View file

@ -4,11 +4,13 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchDelayProfiles, fetchDownloadClients, fetchImportLists, fetchIndexers, fetchNotifications, fetchReleaseProfiles } from 'Store/Actions/settingsActions';
import { fetchTagDetails, fetchTags } from 'Store/Actions/tagActions';
import createSortedSectionSelector from 'Store/Selectors/createSortedSectionSelector';
import sortByProp from 'Utilities/Array/sortByProp';
import Tags from './Tags';
function createMapStateToProps() {
return createSelector(
(state) => state.tags,
createSortedSectionSelector('tags', sortByProp('label')),
(tags) => {
const isFetching = tags.isFetching || tags.details.isFetching;
const error = tags.error || tags.details.error;

View file

@ -0,0 +1,86 @@
import moment from 'moment';
import { createAction } from 'redux-actions';
import { sortDirections } from 'Helpers/Props';
import { createThunk, handleThunks } from 'Store/thunks';
import updateSectionState from 'Utilities/State/updateSectionState';
import createFetchHandler from './Creators/createFetchHandler';
import createHandleActions from './Creators/createHandleActions';
import createSetClientSideCollectionSortReducer from './Creators/Reducers/createSetClientSideCollectionSortReducer';
//
// Variables
export const section = 'albumSelection';
//
// State
export const defaultState = {
isFetching: false,
isReprocessing: false,
isPopulated: false,
error: null,
sortKey: 'title',
sortDirection: sortDirections.ASCENDING,
items: [],
sortPredicates: {
title: ({ title }) => {
return title.toLocaleLowerCase();
},
releaseDate: function({ releaseDate }, direction) {
if (releaseDate) {
return moment(releaseDate).unix();
}
if (direction === sortDirections.DESCENDING) {
return 0;
}
return Number.MAX_VALUE;
}
}
};
export const persistState = [
'albumSelection.sortKey',
'albumSelection.sortDirection'
];
//
// Actions Types
export const FETCH_ALBUMS = 'albumSelection/fetchAlbums';
export const SET_ALBUMS_SORT = 'albumSelection/setAlbumsSort';
export const CLEAR_ALBUMS = 'albumSelection/clearAlbums';
//
// Action Creators
export const fetchAlbums = createThunk(FETCH_ALBUMS);
export const setAlbumsSort = createAction(SET_ALBUMS_SORT);
export const clearAlbums = createAction(CLEAR_ALBUMS);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_ALBUMS]: createFetchHandler(section, '/album')
});
//
// Reducers
export const reducers = createHandleActions({
[SET_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(section),
[CLEAR_ALBUMS]: (state) => {
return updateSectionState(state, section, {
...defaultState,
sortKey: state.sortKey,
sortDirection: state.sortDirection
});
}
}, defaultState, section);

View file

@ -151,7 +151,7 @@ export const defaultState = {
{
name: 'genres',
label: () => translate('Genres'),
isSortable: false,
isSortable: true,
isVisible: false
},
{

View file

@ -150,7 +150,7 @@ export const defaultState = {
},
{
key: 'importFailed',
label: () => translate('ImportFailed'),
label: () => translate('ImportCompleteFailed'),
filters: [
{
key: 'eventType',

View file

@ -1,5 +1,6 @@
import * as albums from './albumActions';
import * as albumHistory from './albumHistoryActions';
import * as albumSelection from './albumSelectionActions';
import * as app from './appActions';
import * as artist from './artistActions';
import * as artistHistory from './artistHistoryActions';
@ -29,14 +30,18 @@ import * as wanted from './wantedActions';
export default [
app,
albums,
albumHistory,
albumSelection,
artist,
artistHistory,
artistIndex,
blocklist,
captcha,
calendar,
commands,
customFilters,
albums,
trackFiles,
albumHistory,
history,
interactiveImportActions,
oAuth,
@ -47,9 +52,6 @@ export default [
providerOptions,
queue,
releases,
artist,
artistHistory,
artistIndex,
search,
settings,
system,

View file

@ -16,7 +16,6 @@ import createSetClientSideCollectionSortReducer from './Creators/Reducers/create
export const section = 'interactiveImport';
const albumsSection = `${section}.albums`;
const trackFilesSection = `${section}.trackFiles`;
let abortCurrentFetchRequest = null;
let abortCurrentRequest = null;
@ -58,15 +57,6 @@ export const defaultState = {
}
},
albums: {
isFetching: false,
isPopulated: false,
error: null,
sortKey: 'albumTitle',
sortDirection: sortDirections.ASCENDING,
items: []
},
trackFiles: {
isFetching: false,
isPopulated: false,
@ -97,10 +87,6 @@ export const ADD_RECENT_FOLDER = 'interactiveImport/addRecentFolder';
export const REMOVE_RECENT_FOLDER = 'interactiveImport/removeRecentFolder';
export const SET_INTERACTIVE_IMPORT_MODE = 'interactiveImport/setInteractiveImportMode';
export const FETCH_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/fetchInteractiveImportAlbums';
export const SET_INTERACTIVE_IMPORT_ALBUMS_SORT = 'interactiveImport/clearInteractiveImportAlbumsSort';
export const CLEAR_INTERACTIVE_IMPORT_ALBUMS = 'interactiveImport/clearInteractiveImportAlbums';
export const FETCH_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/fetchInteractiveImportTrackFiles';
export const CLEAR_INTERACTIVE_IMPORT_TRACKFILES = 'interactiveImport/clearInteractiveImportTrackFiles';
@ -117,10 +103,6 @@ export const addRecentFolder = createAction(ADD_RECENT_FOLDER);
export const removeRecentFolder = createAction(REMOVE_RECENT_FOLDER);
export const setInteractiveImportMode = createAction(SET_INTERACTIVE_IMPORT_MODE);
export const fetchInteractiveImportAlbums = createThunk(FETCH_INTERACTIVE_IMPORT_ALBUMS);
export const setInteractiveImportAlbumsSort = createAction(SET_INTERACTIVE_IMPORT_ALBUMS_SORT);
export const clearInteractiveImportAlbums = createAction(CLEAR_INTERACTIVE_IMPORT_ALBUMS);
export const fetchInteractiveImportTrackFiles = createThunk(FETCH_INTERACTIVE_IMPORT_TRACKFILES);
export const clearInteractiveImportTrackFiles = createAction(CLEAR_INTERACTIVE_IMPORT_TRACKFILES);
@ -253,8 +235,6 @@ export const actionHandlers = handleThunks({
});
},
[FETCH_INTERACTIVE_IMPORT_ALBUMS]: createFetchHandler(albumsSection, '/album'),
[FETCH_INTERACTIVE_IMPORT_TRACKFILES]: createFetchHandler(trackFilesSection, '/trackFile')
});
@ -336,14 +316,6 @@ export const reducers = createHandleActions({
return Object.assign({}, state, { importMode: payload.importMode });
},
[SET_INTERACTIVE_IMPORT_ALBUMS_SORT]: createSetClientSideCollectionSortReducer(albumsSection),
[CLEAR_INTERACTIVE_IMPORT_ALBUMS]: (state) => {
return updateSectionState(state, albumsSection, {
...defaultState.albums
});
},
[CLEAR_INTERACTIVE_IMPORT_TRACKFILES]: (state) => {
return updateSectionState(state, trackFilesSection, {
...defaultState.trackFiles

View file

@ -52,6 +52,12 @@ export const defaultState = {
isSortable: true,
isVisible: true
},
{
name: 'albums.lastSearchTime',
label: () => translate('LastSearched'),
isSortable: true,
isVisible: false
},
// {
// name: 'status',
// label: 'Status',
@ -131,6 +137,12 @@ export const defaultState = {
// label: 'Status',
// isVisible: true
// },
{
name: 'albums.lastSearchTime',
label: () => translate('LastSearched'),
isSortable: true,
isVisible: false
},
{
name: 'actions',
columnLabel: () => translate('Actions'),

View file

@ -1,4 +1,5 @@
import { createSelector } from 'reselect';
import AlbumAppState from 'App/State/AlbumAppState';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
import { createArtistSelectorForHook } from './createArtistSelector';
@ -7,7 +8,7 @@ function createArtistAlbumsSelector(artistId: number) {
return createSelector(
(state: AppState) => state.albums,
createArtistSelectorForHook(artistId),
(albums, artist = {} as Artist) => {
(albums: AlbumAppState, artist = {} as Artist) => {
const { isFetching, isPopulated, error, items } = albums;
const filteredAlbums = items.filter(

View file

@ -1,13 +1,14 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
import MetadataProfile from 'typings/MetadataProfile';
import { createArtistSelectorForHook } from './createArtistSelector';
function createArtistMetadataProfileSelector(artistId: number) {
return createSelector(
(state: AppState) => state.settings.metadataProfiles.items,
createArtistSelectorForHook(artistId),
(metadataProfiles, artist = {} as Artist) => {
(metadataProfiles: MetadataProfile[], artist = {} as Artist) => {
return metadataProfiles.find((profile) => {
return profile.id === artist.metadataProfileId;
});

View file

@ -1,13 +1,14 @@
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import Artist from 'Artist/Artist';
import QualityProfile from 'typings/QualityProfile';
import { createArtistSelectorForHook } from './createArtistSelector';
function createArtistQualityProfileSelector(artistId: number) {
return createSelector(
(state: AppState) => state.settings.qualityProfiles.items,
createArtistSelectorForHook(artistId),
(qualityProfiles, artist = {} as Artist) => {
(qualityProfiles: QualityProfile[], artist = {} as Artist) => {
return qualityProfiles.find(
(profile) => profile.id === artist.qualityProfileId
);

View file

@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Alert from 'Components/Alert';
import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
@ -77,15 +77,16 @@ class LogFiles extends Component {
<PageContentBody>
<Alert>
<div>
Log files are located in: {location}
{translate('LogFilesLocation', {
location
})}
</div>
{
currentLogView === 'Log Files' &&
<div>
The log level defaults to 'Info' and can be changed in <Link to="/settings/general">General Settings</Link>
</div>
}
{currentLogView === 'Log Files' ? (
<div>
<InlineMarkdown data={translate('TheLogLevelDefault')} />
</div>
) : null}
</Alert>
{

View file

@ -1,48 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import styles from './UpdateChanges.css';
class UpdateChanges extends Component {
//
// Render
render() {
const {
title,
changes
} = this.props;
if (changes.length === 0) {
return null;
}
const uniqueChanges = [...new Set(changes)];
return (
<div>
<div className={styles.title}>{title}</div>
<ul>
{
uniqueChanges.map((change, index) => {
return (
<li key={index}>
<InlineMarkdown data={change} />
</li>
);
})
}
</ul>
</div>
);
}
}
UpdateChanges.propTypes = {
title: PropTypes.string.isRequired,
changes: PropTypes.arrayOf(PropTypes.string)
};
export default UpdateChanges;

View file

@ -0,0 +1,43 @@
import React from 'react';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import styles from './UpdateChanges.css';
interface UpdateChangesProps {
title: string;
changes: string[];
}
function UpdateChanges(props: UpdateChangesProps) {
const { title, changes } = props;
if (changes.length === 0) {
return null;
}
const uniqueChanges = [...new Set(changes)];
return (
<div>
<div className={styles.title}>{title}</div>
<ul>
{uniqueChanges.map((change, index) => {
const checkChange = change.replace(
/#\d{4,5}\b/g,
(match) =>
`[${match}](https://github.com/Lidarr/Lidarr/issues/${match.substring(
1
)})`
);
return (
<li key={index}>
<InlineMarkdown data={checkChange} />
</li>
);
})}
</ul>
</div>
);
}
export default UpdateChanges;

View file

@ -1,249 +0,0 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { icons, kinds } from 'Helpers/Props';
import formatDate from 'Utilities/Date/formatDate';
import formatDateTime from 'Utilities/Date/formatDateTime';
import translate from 'Utilities/String/translate';
import UpdateChanges from './UpdateChanges';
import styles from './Updates.css';
class Updates extends Component {
//
// Render
render() {
const {
currentVersion,
isFetching,
isPopulated,
updatesError,
generalSettingsError,
items,
isInstallingUpdate,
updateMechanism,
updateMechanismMessage,
shortDateFormat,
longDateFormat,
timeFormat,
onInstallLatestPress
} = this.props;
const hasError = !!(updatesError || generalSettingsError);
const hasUpdates = isPopulated && !hasError && items.length > 0;
const noUpdates = isPopulated && !hasError && !items.length;
const hasUpdateToInstall = hasUpdates && _.some(items, { installable: true, latest: true });
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
const externalUpdaterPrefix = 'Unable to update Lidarr directly,';
const externalUpdaterMessages = {
external: 'Lidarr is configured to use an external update mechanism',
apt: 'use apt to install the update',
docker: 'update the docker container to receive the update'
};
return (
<PageContent title={translate('Updates')}>
<PageContentBody>
{
!isPopulated && !hasError &&
<LoadingIndicator />
}
{
noUpdates &&
<Alert kind={kinds.INFO}>
{translate('NoUpdatesAreAvailable')}
</Alert>
}
{
hasUpdateToInstall &&
<div className={styles.messageContainer}>
{
updateMechanism === 'builtIn' || updateMechanism === 'script' ?
<SpinnerButton
className={styles.updateAvailable}
kind={kinds.PRIMARY}
isSpinning={isInstallingUpdate}
onPress={onInstallLatestPress}
>
Install Latest
</SpinnerButton> :
<Fragment>
<Icon
name={icons.WARNING}
kind={kinds.WARNING}
size={30}
/>
<div className={styles.message}>
{externalUpdaterPrefix} <InlineMarkdown data={updateMechanismMessage || externalUpdaterMessages[updateMechanism] || externalUpdaterMessages.external} />
</div>
</Fragment>
}
{
isFetching &&
<LoadingIndicator
className={styles.loading}
size={20}
/>
}
</div>
}
{
noUpdateToInstall &&
<div className={styles.messageContainer}>
<Icon
className={styles.upToDateIcon}
name={icons.CHECK_CIRCLE}
size={30}
/>
<div className={styles.message}>
The latest version of Lidarr is already installed
</div>
{
isFetching &&
<LoadingIndicator
className={styles.loading}
size={20}
/>
}
</div>
}
{
hasUpdates &&
<div>
{
items.map((update) => {
const hasChanges = !!update.changes;
return (
<div
key={update.version}
className={styles.update}
>
<div className={styles.info}>
<div className={styles.version}>{update.version}</div>
<div className={styles.space}>&mdash;</div>
<div
className={styles.date}
title={formatDateTime(update.releaseDate, longDateFormat, timeFormat)}
>
{formatDate(update.releaseDate, shortDateFormat)}
</div>
{
update.branch === 'master' ?
null :
<Label
className={styles.label}
>
{update.branch}
</Label>
}
{
update.version === currentVersion ?
<Label
className={styles.label}
kind={kinds.SUCCESS}
title={formatDateTime(update.installedOn, longDateFormat, timeFormat)}
>
Currently Installed
</Label> :
null
}
{
update.version !== currentVersion && update.installedOn ?
<Label
className={styles.label}
kind={kinds.INVERSE}
title={formatDateTime(update.installedOn, longDateFormat, timeFormat)}
>
Previously Installed
</Label> :
null
}
</div>
{
!hasChanges &&
<div>
{translate('MaintenanceRelease')}
</div>
}
{
hasChanges &&
<div className={styles.changes}>
<UpdateChanges
title={translate('New')}
changes={update.changes.new}
/>
<UpdateChanges
title={translate('Fixed')}
changes={update.changes.fixed}
/>
</div>
}
</div>
);
})
}
</div>
}
{
!!updatesError &&
<div>
Failed to fetch updates
</div>
}
{
!!generalSettingsError &&
<div>
Failed to update settings
</div>
}
</PageContentBody>
</PageContent>
);
}
}
Updates.propTypes = {
currentVersion: PropTypes.string.isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
updatesError: PropTypes.object,
generalSettingsError: PropTypes.object,
items: PropTypes.array.isRequired,
isInstallingUpdate: PropTypes.bool.isRequired,
updateMechanism: PropTypes.string,
updateMechanismMessage: PropTypes.string,
shortDateFormat: PropTypes.string.isRequired,
longDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
onInstallLatestPress: PropTypes.func.isRequired
};
export default Updates;

View file

@ -0,0 +1,303 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import AppState from 'App/State/AppState';
import * as commandNames from 'Commands/commandNames';
import Alert from 'Components/Alert';
import Icon from 'Components/Icon';
import Label from 'Components/Label';
import SpinnerButton from 'Components/Link/SpinnerButton';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
import ConfirmModal from 'Components/Modal/ConfirmModal';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { icons, kinds } from 'Helpers/Props';
import { executeCommand } from 'Store/Actions/commandActions';
import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
import { fetchUpdates } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import { UpdateMechanism } from 'typings/Settings/General';
import formatDate from 'Utilities/Date/formatDate';
import formatDateTime from 'Utilities/Date/formatDateTime';
import translate from 'Utilities/String/translate';
import UpdateChanges from './UpdateChanges';
import styles from './Updates.css';
const VERSION_REGEX = /\d+\.\d+\.\d+\.\d+/i;
function createUpdatesSelector() {
return createSelector(
(state: AppState) => state.system.updates,
(state: AppState) => state.settings.general,
(updates, generalSettings) => {
const { error: updatesError, items } = updates;
const isFetching = updates.isFetching || generalSettings.isFetching;
const isPopulated = updates.isPopulated && generalSettings.isPopulated;
return {
isFetching,
isPopulated,
updatesError,
generalSettingsError: generalSettings.error,
items,
updateMechanism: generalSettings.item.updateMechanism,
};
}
);
}
function Updates() {
const currentVersion = useSelector((state: AppState) => state.app.version);
const { packageUpdateMechanismMessage } = useSelector(
createSystemStatusSelector()
);
const { shortDateFormat, longDateFormat, timeFormat } = useSelector(
createUISettingsSelector()
);
const isInstallingUpdate = useSelector(
createCommandExecutingSelector(commandNames.APPLICATION_UPDATE)
);
const {
isFetching,
isPopulated,
updatesError,
generalSettingsError,
items,
updateMechanism,
} = useSelector(createUpdatesSelector());
const dispatch = useDispatch();
const [isMajorUpdateModalOpen, setIsMajorUpdateModalOpen] = useState(false);
const hasError = !!(updatesError || generalSettingsError);
const hasUpdates = isPopulated && !hasError && items.length > 0;
const noUpdates = isPopulated && !hasError && !items.length;
const externalUpdaterPrefix = translate('UpdateAppDirectlyLoadError');
const externalUpdaterMessages: Partial<Record<UpdateMechanism, string>> = {
external: translate('ExternalUpdater'),
apt: translate('AptUpdater'),
docker: translate('DockerUpdater'),
};
const { isMajorUpdate, hasUpdateToInstall } = useMemo(() => {
const majorVersion = parseInt(
currentVersion.match(VERSION_REGEX)?.[0] ?? '0'
);
const latestVersion = items[0]?.version;
const latestMajorVersion = parseInt(
latestVersion?.match(VERSION_REGEX)?.[0] ?? '0'
);
return {
isMajorUpdate: latestMajorVersion > majorVersion,
hasUpdateToInstall: items.some(
(update) => update.installable && update.latest
),
};
}, [currentVersion, items]);
const noUpdateToInstall = hasUpdates && !hasUpdateToInstall;
const handleInstallLatestPress = useCallback(() => {
if (isMajorUpdate) {
setIsMajorUpdateModalOpen(true);
} else {
dispatch(executeCommand({ name: commandNames.APPLICATION_UPDATE }));
}
}, [isMajorUpdate, setIsMajorUpdateModalOpen, dispatch]);
const handleInstallLatestMajorVersionPress = useCallback(() => {
setIsMajorUpdateModalOpen(false);
dispatch(
executeCommand({
name: commandNames.APPLICATION_UPDATE,
installMajorUpdate: true,
})
);
}, [setIsMajorUpdateModalOpen, dispatch]);
const handleCancelMajorVersionPress = useCallback(() => {
setIsMajorUpdateModalOpen(false);
}, [setIsMajorUpdateModalOpen]);
useEffect(() => {
dispatch(fetchUpdates());
dispatch(fetchGeneralSettings());
}, [dispatch]);
return (
<PageContent title={translate('Updates')}>
<PageContentBody>
{isPopulated || hasError ? null : <LoadingIndicator />}
{noUpdates ? (
<Alert kind={kinds.INFO}>{translate('NoUpdatesAreAvailable')}</Alert>
) : null}
{hasUpdateToInstall ? (
<div className={styles.messageContainer}>
{updateMechanism === 'builtIn' || updateMechanism === 'script' ? (
<SpinnerButton
kind={kinds.PRIMARY}
isSpinning={isInstallingUpdate}
onPress={handleInstallLatestPress}
>
{translate('InstallLatest')}
</SpinnerButton>
) : (
<>
<Icon name={icons.WARNING} kind={kinds.WARNING} size={30} />
<div className={styles.message}>
{externalUpdaterPrefix}{' '}
<InlineMarkdown
data={
packageUpdateMechanismMessage ||
externalUpdaterMessages[updateMechanism] ||
externalUpdaterMessages.external
}
/>
</div>
</>
)}
{isFetching ? (
<LoadingIndicator className={styles.loading} size={20} />
) : null}
</div>
) : null}
{noUpdateToInstall && (
<div className={styles.messageContainer}>
<Icon
className={styles.upToDateIcon}
name={icons.CHECK_CIRCLE}
size={30}
/>
<div className={styles.message}>{translate('OnLatestVersion')}</div>
{isFetching && (
<LoadingIndicator className={styles.loading} size={20} />
)}
</div>
)}
{hasUpdates && (
<div>
{items.map((update) => {
return (
<div key={update.version} className={styles.update}>
<div className={styles.info}>
<div className={styles.version}>{update.version}</div>
<div className={styles.space}>&mdash;</div>
<div
className={styles.date}
title={formatDateTime(
update.releaseDate,
longDateFormat,
timeFormat
)}
>
{formatDate(update.releaseDate, shortDateFormat)}
</div>
{update.branch === 'master' ? null : (
<Label className={styles.label}>{update.branch}</Label>
)}
{update.version === currentVersion ? (
<Label
className={styles.label}
kind={kinds.SUCCESS}
title={formatDateTime(
update.installedOn,
longDateFormat,
timeFormat
)}
>
{translate('CurrentlyInstalled')}
</Label>
) : null}
{update.version !== currentVersion && update.installedOn ? (
<Label
className={styles.label}
kind={kinds.INVERSE}
title={formatDateTime(
update.installedOn,
longDateFormat,
timeFormat
)}
>
{translate('PreviouslyInstalled')}
</Label>
) : null}
</div>
{update.changes ? (
<div>
<UpdateChanges
title={translate('New')}
changes={update.changes.new}
/>
<UpdateChanges
title={translate('Fixed')}
changes={update.changes.fixed}
/>
</div>
) : (
<div>{translate('MaintenanceRelease')}</div>
)}
</div>
);
})}
</div>
)}
{updatesError ? (
<Alert kind={kinds.WARNING}>
{translate('FailedToFetchUpdates')}
</Alert>
) : null}
{generalSettingsError ? (
<Alert kind={kinds.DANGER}>
{translate('FailedToFetchSettings')}
</Alert>
) : null}
<ConfirmModal
isOpen={isMajorUpdateModalOpen}
kind={kinds.WARNING}
title={translate('InstallMajorVersionUpdate')}
message={
<div>
<div>{translate('InstallMajorVersionUpdateMessage')}</div>
<div>
<InlineMarkdown
data={translate('InstallMajorVersionUpdateMessageLink', {
domain: 'lidarr.audio',
url: 'https://lidarr.audio/#downloads',
})}
/>
</div>
</div>
}
confirmLabel={translate('Install')}
onConfirm={handleInstallLatestMajorVersionPress}
onCancel={handleCancelMajorVersionPress}
/>
</PageContentBody>
</PageContent>
);
}
export default Updates;

View file

@ -1,98 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import * as commandNames from 'Commands/commandNames';
import { executeCommand } from 'Store/Actions/commandActions';
import { fetchGeneralSettings } from 'Store/Actions/settingsActions';
import { fetchUpdates } from 'Store/Actions/systemActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import Updates from './Updates';
function createMapStateToProps() {
return createSelector(
(state) => state.app.version,
createSystemStatusSelector(),
(state) => state.system.updates,
(state) => state.settings.general,
createUISettingsSelector(),
createCommandExecutingSelector(commandNames.APPLICATION_UPDATE),
(
currentVersion,
status,
updates,
generalSettings,
uiSettings,
isInstallingUpdate
) => {
const {
error: updatesError,
items
} = updates;
const isFetching = updates.isFetching || generalSettings.isFetching;
const isPopulated = updates.isPopulated && generalSettings.isPopulated;
return {
currentVersion,
isFetching,
isPopulated,
updatesError,
generalSettingsError: generalSettings.error,
items,
isInstallingUpdate,
updateMechanism: generalSettings.item.updateMechanism,
updateMechanismMessage: status.packageUpdateMechanismMessage,
shortDateFormat: uiSettings.shortDateFormat,
longDateFormat: uiSettings.longDateFormat,
timeFormat: uiSettings.timeFormat
};
}
);
}
const mapDispatchToProps = {
dispatchFetchUpdates: fetchUpdates,
dispatchFetchGeneralSettings: fetchGeneralSettings,
dispatchExecuteCommand: executeCommand
};
class UpdatesConnector extends Component {
//
// Lifecycle
componentDidMount() {
this.props.dispatchFetchUpdates();
this.props.dispatchFetchGeneralSettings();
}
//
// Listeners
onInstallLatestPress = () => {
this.props.dispatchExecuteCommand({ name: commandNames.APPLICATION_UPDATE });
};
//
// Render
render() {
return (
<Updates
onInstallLatestPress={this.onInstallLatestPress}
{...this.props}
/>
);
}
}
UpdatesConnector.propTypes = {
dispatchFetchUpdates: PropTypes.func.isRequired,
dispatchFetchGeneralSettings: PropTypes.func.isRequired,
dispatchExecuteCommand: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(UpdatesConnector);

View file

@ -6,15 +6,33 @@ import isTomorrow from 'Utilities/Date/isTomorrow';
import isYesterday from 'Utilities/Date/isYesterday';
import translate from 'Utilities/String/translate';
function getRelativeDate(date, shortDateFormat, showRelativeDates, { timeFormat, includeSeconds = false, timeForToday = false } = {}) {
interface GetRelativeDateOptions {
timeFormat?: string;
includeSeconds?: boolean;
timeForToday?: boolean;
}
function getRelativeDate(
date: string | undefined,
shortDateFormat: string,
showRelativeDates: boolean,
{
timeFormat,
includeSeconds = false,
timeForToday = false,
}: GetRelativeDateOptions = {}
) {
if (!date) {
return null;
return '';
}
const isTodayDate = isToday(date);
if (isTodayDate && timeForToday && timeFormat) {
return formatTime(date, timeFormat, { includeMinuteZero: true, includeSeconds });
return formatTime(date, timeFormat, {
includeMinuteZero: true,
includeSeconds,
});
}
if (!showRelativeDates) {

View file

@ -17,7 +17,7 @@ export async function fetchTranslations(): Promise<boolean> {
translations = data.strings;
resolve(true);
} catch (error) {
} catch {
resolve(false);
}
});
@ -27,6 +27,12 @@ export default function translate(
key: string,
tokens: Record<string, string | number | boolean> = {}
) {
const { isProduction = true } = window.Lidarr;
if (!isProduction && !(key in translations)) {
console.warn(`Missing translation for key: ${key}`);
}
const translation = translations[key] || key;
tokens.appName = 'Lidarr';

View file

@ -131,13 +131,15 @@ class CutoffUnmetConnector extends Component {
onSearchSelectedPress = (selected) => {
this.props.executeCommand({
name: commandNames.ALBUM_SEARCH,
albumIds: selected
albumIds: selected,
commandFinished: this.repopulate
});
};
onSearchAllCutoffUnmetPress = () => {
this.props.executeCommand({
name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH
name: commandNames.CUTOFF_UNMET_ALBUM_SEARCH,
commandFinished: this.repopulate
});
};

View file

@ -20,6 +20,7 @@ function CutoffUnmetRow(props) {
foreignAlbumId,
albumType,
title,
lastSearchTime,
disambiguation,
isSelected,
columns,
@ -89,6 +90,15 @@ function CutoffUnmetRow(props) {
);
}
if (name === 'albums.lastSearchTime') {
return (
<RelativeDateCellConnector
key={name}
date={lastSearchTime}
/>
);
}
if (name === 'status') {
return (
<TableRowCell
@ -132,6 +142,7 @@ CutoffUnmetRow.propTypes = {
foreignAlbumId: PropTypes.string.isRequired,
albumType: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
lastSearchTime: PropTypes.string,
disambiguation: PropTypes.string,
isSelected: PropTypes.bool,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View file

@ -121,13 +121,15 @@ class MissingConnector extends Component {
onSearchSelectedPress = (selected) => {
this.props.executeCommand({
name: commandNames.ALBUM_SEARCH,
albumIds: selected
albumIds: selected,
commandFinished: this.repopulate
});
};
onSearchAllMissingPress = () => {
this.props.executeCommand({
name: commandNames.MISSING_ALBUM_SEARCH
name: commandNames.MISSING_ALBUM_SEARCH,
commandFinished: this.repopulate
});
};

View file

@ -17,6 +17,7 @@ function MissingRow(props) {
albumType,
foreignAlbumId,
title,
lastSearchTime,
disambiguation,
isSelected,
columns,
@ -86,6 +87,15 @@ function MissingRow(props) {
);
}
if (name === 'albums.lastSearchTime') {
return (
<RelativeDateCellConnector
key={name}
date={lastSearchTime}
/>
);
}
if (name === 'actions') {
return (
<AlbumSearchCellConnector
@ -113,6 +123,7 @@ MissingRow.propTypes = {
foreignAlbumId: PropTypes.string.isRequired,
albumType: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
lastSearchTime: PropTypes.string,
disambiguation: PropTypes.string,
isSelected: PropTypes.bool,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,

View file

@ -1,6 +1,6 @@
import { createBrowserHistory } from 'history';
import React from 'react';
import { render } from 'react-dom';
import { createRoot } from 'react-dom/client';
import createAppStore from 'Store/createAppStore';
import App from './App/App';
@ -9,9 +9,8 @@ import 'Diag/ConsoleApi';
export async function bootstrap() {
const history = createBrowserHistory();
const store = createAppStore(history);
const container = document.getElementById('root');
render(
<App store={store} history={history} />,
document.getElementById('root')
);
const root = createRoot(container!); // createRoot(container!) if you use TypeScript
root.render(<App store={store} history={history} />);
}

View file

@ -33,7 +33,7 @@
sizes="16x16"
href="/Content/Images/Icons/favicon-16x16.png"
/>
<link rel="manifest" href="/Content/Images/Icons/manifest.json" crossorigin="use-credentials" />
<link rel="manifest" href="/Content/manifest.json" crossorigin="use-credentials" />
<link
rel="mask-icon"
href="/Content/Images/Icons/safari-pinned-tab.svg"
@ -47,7 +47,7 @@
/>
<meta
name="msapplication-config"
content="/Content/Images/Icons/browserconfig.xml"
content="/Content/browserconfig.xml"
/>
<link rel="stylesheet" type="text/css" href="/Content/Fonts/fonts.css">

View file

@ -14,6 +14,32 @@ window.Lidarr = await response.json();
__webpack_public_path__ = `${window.Lidarr.urlBase}/`;
/* eslint-enable no-undef, @typescript-eslint/ban-ts-comment */
const error = console.error;
// Monkey patch console.error to filter out some warnings from React
// TODO: Remove this after the great TypeScript migration
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function logError(...parameters: any[]) {
const filter = parameters.find((parameter) => {
return (
typeof parameter === 'string' &&
(parameter.includes(
'Support for defaultProps will be removed from function components in a future major release'
) ||
parameter.includes(
'findDOMNode is deprecated and will be removed in the next major release'
))
);
});
if (!filter) {
error(...parameters);
}
}
console.error = logError;
const { bootstrap } = await import('./bootstrap');
await bootstrap();

View file

@ -11,8 +11,11 @@
<!-- Android/Apple Phone -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="format-detection" content="telephone=no">
<meta
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="description" content="Lidarr" />
@ -33,7 +36,11 @@
sizes="16x16"
href="/Content/Images/Icons/favicon-16x16.png"
/>
<link rel="manifest" href="/Content/Images/Icons/manifest.json" crossorigin="use-credentials" />
<link
rel="manifest"
href="/Content/manifest.json"
crossorigin="use-credentials"
/>
<link
rel="mask-icon"
href="/Content/Images/Icons/safari-pinned-tab.svg"
@ -45,10 +52,7 @@
href="/favicon.ico"
data-no-hash
/>
<meta
name="msapplication-config"
content="/Content/Images/Icons/browserconfig.xml"
/>
<meta name="msapplication-config" content="/Content/browserconfig.xml" />
<link rel="stylesheet" type="text/css" href="/Content/styles.css" />
<link rel="stylesheet" type="text/css" href="/Content/Fonts/fonts.css" />
@ -59,7 +63,7 @@
body {
background-color: var(--pageBackground);
color: var(--textColor);
font-family: "Roboto", "open sans", "Helvetica Neue", Helvetica, Arial,
font-family: 'Roboto', 'open sans', 'Helvetica Neue', Helvetica, Arial,
sans-serif;
}
@ -209,9 +213,7 @@
</div>
<div class="panel-body">
<div class="sign-in">
SIGN IN TO CONTINUE
</div>
<div class="sign-in">SIGN IN TO CONTINUE</div>
<form
role="form"
@ -230,8 +232,8 @@
pattern=".{1,}"
required
title="User name is required"
autoFocus="true"
autoCapitalize="false"
autofocus="true"
autocapitalize="false"
/>
</div>
@ -282,16 +284,16 @@
</body>
<script type="text/javascript">
var yearSpan = document.getElementById("year");
yearSpan.innerHTML = "2017-" + new Date().getFullYear();
var yearSpan = document.getElementById('year');
yearSpan.innerHTML = '2017-' + new Date().getFullYear();
var copyDiv = document.getElementById("copy");
copyDiv.classList.remove("hidden");
var copyDiv = document.getElementById('copy');
copyDiv.classList.remove('hidden');
if (window.location.search.indexOf("loginFailed=true") > -1) {
var loginFailedDiv = document.getElementById("login-failed");
if (window.location.search.indexOf('loginFailed=true') > -1) {
var loginFailedDiv = document.getElementById('login-failed');
loginFailedDiv.classList.remove("hidden");
loginFailedDiv.classList.remove('hidden');
}
var light = {
@ -311,7 +313,7 @@
primaryHoverBorderColor: '#1D563D',
failedColor: '#f05050',
forgotPasswordColor: '#909fa7',
forgotPasswordAltColor: '#748690'
forgotPasswordAltColor: '#748690',
};
var dark = {
@ -331,21 +333,16 @@
primaryHoverBorderColor: '#1D563D',
failedColor: '#f05050',
forgotPasswordColor: '#737d83',
forgotPasswordAltColor: '#546067'
forgotPasswordAltColor: '#546067',
};
var theme = "_THEME_";
var theme = '_THEME_';
var defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
var finalTheme = theme === 'dark' || (theme === 'auto' && defaultDark) ?
dark :
light;
var finalTheme =
theme === 'dark' || (theme === 'auto' && defaultDark) ? dark : light;
Object.entries(finalTheme).forEach(([key, value]) => {
document.documentElement.style.setProperty(
`--${key}`,
value
);
document.documentElement.style.setProperty(`--${key}`, value);
});
</script>
</html>

View file

@ -0,0 +1,45 @@
export type UpdateMechanism =
| 'builtIn'
| 'script'
| 'external'
| 'apt'
| 'docker';
export default interface General {
bindAddress: string;
port: number;
sslPort: number;
enableSsl: boolean;
launchBrowser: boolean;
authenticationMethod: string;
authenticationRequired: string;
analyticsEnabled: boolean;
username: string;
password: string;
passwordConfirmation: string;
logLevel: string;
consoleLogLevel: string;
branch: string;
apiKey: string;
sslCertPath: string;
sslCertPassword: string;
urlBase: string;
instanceName: string;
applicationUrl: string;
updateAutomatically: boolean;
updateMechanism: UpdateMechanism;
updateScriptPath: string;
proxyEnabled: boolean;
proxyType: string;
proxyHostname: string;
proxyPort: number;
proxyUsername: string;
proxyPassword: string;
proxyBypassFilter: string;
proxyBypassLocalAddresses: boolean;
certificateValidation: string;
backupFolder: string;
backupInterval: number;
backupRetention: number;
id: number;
}

View file

@ -1,4 +1,4 @@
export interface UiSettings {
export default interface UiSettings {
theme: 'auto' | 'dark' | 'light';
showRelativeDates: boolean;
shortDateFormat: string;

View file

@ -19,6 +19,7 @@ interface SystemStatus {
osName: string;
osVersion: string;
packageUpdateMechanism: string;
packageUpdateMechanismMessage: string;
runtimeName: string;
runtimeVersion: string;
sqliteVersion: string;

View file

@ -0,0 +1,20 @@
export interface Changes {
new: string[];
fixed: string[];
}
interface Update {
version: string;
branch: string;
releaseDate: string;
fileName: string;
url: string;
installed: boolean;
installedOn: string;
installable: boolean;
latest: boolean;
changes: Changes | null;
hash: string;
}
export default Update;

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