1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-19 21:46:50 -04:00

Compare commits

..

845 Commits

Author SHA1 Message Date
Qstick 6df45eb6af Fixed: Don't get all movies if not scanning after refresh 2020-07-31 20:56:05 -04:00
Taloth Saldono 71492d4c6f Fixed: Added glusterfs to known network drive filesystems so it shows up in System 2020-07-31 22:06:27 +01:00
Qstick 0f6fca8340 Fixed: Recognize jpeg and jpg extra images as Kodi metadata 2020-07-30 23:24:35 -04:00
Qstick 8ddaf348f1 Fixed: Pass AdvancedSettings to Metadata Fields 2020-07-30 23:05:58 -04:00
GigiPompieru 55e6605ed8 Translated using Weblate (Romanian) [skip ci]
Currently translated at 23.0% (49 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/ro/
2020-07-30 21:13:36 +00:00
Micky d736635ae4 Translated using Weblate (Turkish) [skip ci]
Currently translated at 7.5% (16 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/tr/
2020-07-30 21:13:36 +00:00
Alessandro Berrone a977c5148e Translated using Weblate (Italian) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/it/
2020-07-30 21:13:36 +00:00
Qstick 06d200f078 Update PushoverProxy.cs 2020-07-30 14:42:49 -04:00
ta264 079b7a7cb7 Added translation using Weblate (Romanian) 2020-07-30 06:26:27 +00:00
nitsua c11928ffb8 New: Add + Search on Discovery Bulk Add (#4657)
#4614
2020-07-29 23:51:24 -04:00
Qstick b7a617bc4e Cleanup yarn.lock file, bump loadash 4.17.19 2020-07-29 23:32:19 -04:00
nitsua d2949b31ab Fixed: Issue with containers not matching posters on cast/crew tabs (#4656)
#4652
2020-07-29 22:01:58 -04:00
nitsua c9324bc357 New: Added more external links to add movie (#4603) 2020-07-29 21:33:49 -04:00
nitsua ca8f4acdaa Fixed: icon stacking on the movie details page (#4658)
#4653
2020-07-29 16:00:44 -04:00
Qstick 7eedb7fbec New: Reject Multi-Part Files from Import - Take 2 (#4644)
* New: Reject Multi-Part Files from Import - Take 2

* fixup! Windows is not the only OS :)
2020-07-28 14:47:56 -04:00
Qstick 71e715f954 Import Sorting Lint for Frontend (#4655) 2020-07-28 14:47:25 -04:00
ta264 b92414d657 Add libMonoPosixHelper for musl-arm64 2020-07-27 21:37:24 +01:00
ta264 85fda91604 Fix migration 178 to cope with leading/trailing slashes 2020-07-28 06:00:47 +01:00
ta264 7722c50603 New: Musl (alpine linux) compatibility 2020-07-27 18:17:47 +01:00
ta264 75eb4e8519 Move runtime copy into a separate target 2020-07-27 18:17:47 +01:00
ta264 84134eb6ce Restore support for mono 5.8 2020-07-27 18:17:47 +01:00
nitsua 765ca89810 New: Added external links to Discovery (#4598)
#2691
2020-07-27 10:30:49 -04:00
nitsua 9ce58be385 New: Added external links to movie index (#4600)
#403
2020-07-27 10:29:19 -04:00
Qstick 33cd699eac Lint issue in Tooltip.css 2020-07-26 23:23:55 -04:00
Qstick c34a5c0c3d Fixed: Tooltip goes off screen for Mobile 2020-07-26 23:02:25 -04:00
angrycuban13 80ae55920b Translated using Weblate (Spanish) [skip ci]
Currently translated at 74.6% (159 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/es/
2020-07-26 09:20:08 +00:00
Austin Best eb0be34924 Remove presets from nyaa since it changes so often 2020-07-26 01:43:03 -04:00
Qstick 483c2ae724 Add MovieId and Language Index to Movie Translations Table 2020-07-26 01:38:57 -04:00
Qstick e2165eb51b New: Use Translations for Movie Mapping 2020-07-26 01:38:57 -04:00
Qstick 6d4be67e36 Rename CleanSeriesTitle to CleanMovieTitle 2020-07-26 01:38:57 -04:00
Qstick b6c75e7e1b Fixed: Skip Flat Extra Files (Plex Naming) on Import
Fixes #4630
2020-07-26 01:38:57 -04:00
Qstick c84a9d6612 Fixed: Include 'Sample' Directory in Ignored 2020-07-26 01:38:57 -04:00
Qstick c04f26b7f1 Fixed: Radarr List not Logging List Status Failures 2020-07-26 01:36:50 -04:00
Qstick 16c912ffea Fixed: Don't Clean if any List Failures
Fixes #4648
2020-07-26 01:32:18 -04:00
Qstick 8871864bc0 Really fix Certification Token 2020-07-25 03:13:22 -04:00
Qstick ea7c08d219 Fixed: Don't die in FileNameBuilder when Certification is Null 2020-07-25 02:32:29 -04:00
Qstick 86a75e2641 Update Swagger defs to OAPI3.0 2020-07-25 00:59:00 -04:00
Qstick b077d1c6cf Push Swagger definitions to main repo [skip ci] 2020-07-25 00:39:10 -04:00
nameproof 3a07720587 Translated using Weblate (Swedish) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-24 04:24:51 +00:00
Qstick fabf758230 Update en.json [skip ci] 2020-07-24 00:24:47 -04:00
Qstick 67ea64fa5e Fixed: Remux2160p Showing in HD720/1080 Default Profile 2020-07-24 00:16:42 -04:00
Qstick 7a19d0a88c Revert "New: Reject Files at Import if they are Multi-Part for now"
This reverts commit bed4604e62.
2020-07-23 22:36:36 -04:00
nameproof e88b794ce0 Translated using Weblate (Swedish) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-24 01:10:41 +00:00
Qstick 0a414f37dc Use Radarr API change feed for changed movies (#4629)
* Use Radarr API change feed for changed movies

* Adjust StartTime for cache overlap
2020-07-23 20:06:42 -04:00
nameproof 5b38edfff9 Translated using Weblate (Swedish) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-24 00:04:46 +00:00
Marketos Damigos 09976c6afe Translated using Weblate (Greek) [skip ci]
Currently translated at 17.3% (37 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/el/
2020-07-24 00:04:46 +00:00
reloxx bca7a38003 Translated using Weblate (German) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/de/
2020-07-24 00:04:44 +00:00
ta264 1934a248f8 Drop mono 6.8 tests, add 6.12 2020-07-22 16:25:03 +01:00
Qstick bed4604e62 New: Reject Files at Import if they are Multi-Part for now 2020-07-22 08:07:08 -04:00
nameproof deabc3aa9a Translated using Weblate (Swedish) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-22 02:01:07 +00:00
sevospl b5f173b018 Translated using Weblate (Polish) [skip ci]
Currently translated at 10.7% (23 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/pl/
2020-07-22 02:01:03 +00:00
jpalenz77 69bd3f7d6d Translated using Weblate (Spanish) [skip ci]
Currently translated at 74.6% (159 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/es/
2020-07-22 02:01:01 +00:00
reloxx 947ec567ba Translated using Weblate (Spanish) [skip ci]
Currently translated at 74.6% (159 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/es/
2020-07-22 02:01:00 +00:00
foXaCe 12414e0bde Translated using Weblate (French) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/fr/
2020-07-22 02:00:59 +00:00
reloxx 012cb66b45 Translated using Weblate (French) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/fr/
2020-07-22 02:00:54 +00:00
reloxx 8ce223d01a Translated using Weblate (German) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/de/
2020-07-22 02:00:52 +00:00
Qstick 158a06adbf Add Test Fixture for Migration 154 2020-07-21 19:25:10 -04:00
Qstick 5765961c3a Fixed: Language Migration Assigning 'Any' to MovieFiles and History 2020-07-21 19:25:10 -04:00
ta264 539f495dbe Fixed: SqliteSchemaDumper with separate Primary Key clause 2020-07-20 20:35:17 -04:00
Qstick 23349c1063 Delete appveyor.yml 2020-07-20 19:24:20 -04:00
Austin Best c2317e3567 Fix: Movie index legend not respecting color blind setting
#4616
2020-07-20 10:49:30 -04:00
ta264 26409c9d36 Try to fix service test 2020-07-19 16:17:33 -04:00
ta264 8799da55d3 New: Use new metadata server for imdb/stevenlu lists
Fixes #4306
2020-07-19 16:17:33 -04:00
hotio fa60c28e9c Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:55:06 +00:00
hotio 175b3de373 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:53:03 +00:00
hotio 957c7a9266 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:46:13 +00:00
hotio a4cd52d931 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:43:33 +00:00
hotio 687a055a61 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:31:06 +00:00
hotio 54299694ef Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 17:27:59 +00:00
TRaSH bd06c756b7 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 16:56:14 +00:00
hotio b69ed9fd11 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (213 of 213 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 16:56:14 +00:00
nameproof 8e1cb149fa Translated using Weblate (Swedish) [skip ci]
Currently translated at 56.1% (91 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-19 16:01:08 +00:00
Qstick ef879c3a91 Test Weblate Hook 2020-07-19 12:00:57 -04:00
Qstick 9603f9fdb5 More Translations, Column Headings, Settings page 2020-07-19 11:42:03 -04:00
hotio fc7091ef0c Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 13:31:19 +00:00
hotio d37fde5f06 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 13:20:25 +00:00
hotio 575dbd18d9 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 12:53:32 +00:00
hotio 2a4f959a87 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 12:44:15 +00:00
hotio b7222c2c55 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 12:35:09 +00:00
hotio 47404783c2 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 12:24:43 +00:00
nameproof 29f2543d57 Translated using Weblate (Swedish) [skip ci]
Currently translated at 46.2% (75 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-19 12:23:59 +00:00
jpalenz77 f8070f0a8e Translated using Weblate (Spanish) [skip ci]
Currently translated at 96.9% (157 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/es/
2020-07-19 12:23:59 +00:00
foXaCe 818b330ece Translated using Weblate (French) [skip ci]
Currently translated at 38.8% (63 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/fr/
2020-07-19 12:23:59 +00:00
reloxx 4fa1fbe23c Translated using Weblate (German) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/de/
2020-07-19 12:23:59 +00:00
hotio 2919b282f6 Translated using Weblate (Dutch) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-19 12:23:59 +00:00
Qstick 2390ab98fa New: Reject File if Sample Check Indeterminate 2020-07-19 01:45:23 -04:00
Qstick 1c85bd96f8 Bump .net Core 3.1.6 2020-07-19 01:16:25 -04:00
Qstick df8023bf02 Add Translation % Badge to README [skip ci] 2020-07-19 01:03:35 -04:00
nameproof 9109280ae2 Translated using Weblate (Swedish) [skip ci]
Currently translated at 3.0% (5 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/sv/
2020-07-18 15:29:26 +00:00
jpalenz77 a59ea1f8d2 Translated using Weblate (Spanish) [skip ci]
Currently translated at 41.9% (68 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/es/
2020-07-18 15:29:26 +00:00
Gabriel Patzleiner 60ebafc940 Translated using Weblate (German) [skip ci]
Currently translated at 100.0% (162 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/de/
2020-07-18 15:29:26 +00:00
hotio ac1956f5ad Translated using Weblate (Dutch) [skip ci]
Currently translated at 80.2% (130 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-18 15:29:26 +00:00
TRaSH 2242f2aba3 Translated using Weblate (Dutch) [skip ci]
Currently translated at 80.2% (130 of 162 strings)

Translation: Servarr/Radarr
Translate-URL: https://translate.servarr.com/projects/radarr/radarr/nl/
2020-07-18 15:29:26 +00:00
Qstick 8010030ca1 Simplify Readme [skip ci] 2020-07-18 01:50:02 -04:00
Qstick 4ac18dd023 Fixed: In some cases MinVotes is int from v0.2, causes migration failure 2020-07-18 00:09:08 -04:00
Qstick c75362ecc8 Fixed: Donate Link references old URL 2020-07-17 23:37:46 -04:00
Weblate Admin bf061f2a96 New: Add All Supported Languages 2020-07-17 23:37:46 -04:00
Qstick c6367112c2 New: German Translation asset
Co-Authored-By: Gabriel Patzleiner <gabriel.patzleiner@gmail.com>
2020-07-17 23:37:46 -04:00
Qstick 074b49fa8c New: Translation support for UI Sidebar, Search Input, Base Menus 2020-07-17 23:37:46 -04:00
Qstick bfc0361784 New: Translations support for Health Checks 2020-07-17 23:37:46 -04:00
Qstick 200c7487b9 New: English Base Translation asset 2020-07-17 23:37:46 -04:00
Qstick ffff528ccb New: Localization framework 2020-07-17 23:37:46 -04:00
austinwbest 5389c86cde New: Added support for AHD User Release indexer flag (#4599)
- Added missing AHD_Internal to the decision maker to increase the score for it

Fixes #3770
2020-07-17 00:01:22 -04:00
Qstick 3a0aa51cbf Fixed: Nordic shuldn't translate to Norwegian Audio Language
Fixes #1352
2020-07-16 22:31:24 -04:00
Qstick 5eb7a83996 Don't fail test if there are no builds with changes shown 2020-07-16 21:48:55 -04:00
Qstick bd5bb2a63c Fixed: QualitySelect defaults to Profile instead of NoChange on Editor 2020-07-15 23:27:35 -04:00
austinwbest d7e0625be7 Fixed: Allow for unchecking empty folder options if both were checked
Fixes #4556
2020-07-14 23:26:29 -04:00
Qstick a22c13bfa3 Remove Development.md [skip ci] 2020-07-13 15:31:50 -04:00
Qstick 3f39c8d1bd Fix Language Aggregation debug Log statement 2020-07-11 00:43:32 -04:00
Qstick 1dbb664ef6 New: Digital Releases on Calendar, Misc Other Calendar Fixes
Fixes #4582
2020-07-11 00:35:02 -04:00
Qstick 0b7067cf9c Fixed: Typo/unclear text in backup retention
Co-Authored-By: Ryan <simplyryan@users.noreply.github.com>
2020-07-10 23:10:25 -04:00
Qstick d8dc35913d Fixed: Console Warning on MovieTitlesRow due to incorrect variable type 2020-07-10 23:08:11 -04:00
Qstick 027a388157 Fixed: Block deleteEmpty and createEmpty both on in UI 2020-07-10 23:01:08 -04:00
Qstick a552389ee8 Fixed: Don't duplicate languages from MediaInfo 2020-07-10 22:02:55 -04:00
Qstick f8f58c2eda Fixed: Refresh from IndexItem and MovieDetails doesn’t refresh single (#4584)
* Fixed: Refresh from MovieDetails doesn’t refresh single

* Fixed: Refresh from index Item should be sing

* Fixed: Detection on running refresh on MovieDetails
2020-07-10 07:39:52 -04:00
Qstick 113f5a9bfe Fixed: MovieFile Language Select Modal can show Unknown 2020-07-10 07:29:25 -04:00
Qstick 9d913899ca Fixed: Bad RootPath Added on List Add from Collection 2020-07-10 00:14:16 -04:00
Qstick c9a9babdf3 Fixed: Styling of Collection name on MovieDetail view 2020-07-09 22:13:11 -04:00
Qstick 6be1ae0120 Fixed: Alignment of dates in Release Date Tooltip 2020-07-09 22:06:07 -04:00
Qstick ac79c51196 New: Monitor Collections from Movie Details Page 2020-07-09 21:43:21 -04:00
Qstick 9bf50d4493 Fixed: Better NetImport Validation (Don't Allow Bad ProfileIds) 2020-07-09 21:26:57 -04:00
Qstick aa6c8f493e New: Refresh Selected in Editor Mode 2020-07-09 13:59:59 -04:00
Qstick 135251ec31 More descriptive validation error for Movie Path 2020-07-09 10:41:17 -04:00
Qstick c51fe81f41 Fixed: Don't show percentage text on Fat progress bar 2020-07-09 10:40:58 -04:00
Qstick 7d22696b1f New: Show Digital Release on Poster view when Sorted By 2020-07-08 09:30:24 -04:00
Qstick d791f3f67f Fixed: Digital before Physical for normal release order 2020-07-08 09:28:42 -04:00
Qstick 74539d2025 Fixed: Search Failing due to Null Titles
Fixes #4578
2020-07-08 09:24:40 -04:00
Qstick 075f024cec Cleanup Solution Unused Usings 2020-07-08 01:24:06 -04:00
Qstick 3a50152b21 New: Digital Release Dates in UI 2020-07-08 01:24:06 -04:00
Qstick 88bda6bcb6 New: Use MediaInfo to Augment Languages 2020-07-08 01:24:06 -04:00
Qstick 10322a1867 New: Aphrodite Language Improvements 2020-07-08 01:24:06 -04:00
Qstick 965ed041ae Fixed: All Imports show as Multi due to duplicated langs 2020-07-06 09:15:46 -04:00
Qstick 2b5dc59a36 Fixed: All AHD x265 Encodes should be HDR
Fixes: #4386
2020-07-06 00:37:26 -04:00
Qstick b82d636a8c Fixed: Don't make Editor call on initial page load 2020-07-04 22:31:13 -04:00
Qstick 5982731ef7 New: Search Selected from Movie Editor
Fixes #4130
2020-07-04 22:30:23 -04:00
Qstick b97e6977fb New: Year column on Movie Index view 2020-07-04 21:59:54 -04:00
Qstick 98c4e63309 Fixed: Don’t Prioritize Flags over Protocol when Prefer Flags Enabled
Fixes #1814
2020-07-04 11:21:44 -04:00
Qstick f37c7a9748 New: Swipe on Movie Details Page 2020-07-04 03:13:24 -04:00
Qstick a08648272c Bump Yarn timeout to 2min 2020-07-03 23:54:02 -04:00
Qstick 0142c45210 Fixed: Multi Language lost on Import
Fixes #4313
2020-07-03 23:41:07 -04:00
Qstick f073f0c35c Fixed Movie Details Tweaks
Fixes #4124
2020-07-03 23:09:48 -04:00
Qstick b7cf6f49d0 Fixed: Move Links, Add Tags to Movie Detail
Fixes #4545
2020-07-03 22:04:58 -04:00
Qstick 347cd5982a New: Title and Year MovieIndex Filters 2020-07-03 21:24:38 -04:00
ta264 0a589c529f Fixed: Map dsm shared folder to full path in status (#797)
* Fixed: Map dsm shared folder to full path in status

* Add tests

* fix tests
2020-07-03 20:20:29 -04:00
Qstick f29e7557dd Fixed: dispatchClearPendingChanges on some Settings Sections 2020-07-02 21:42:04 -04:00
Qstick 7f201c6677 Fixed: Mark "BAD" Nzbget Downloads as Failed
Fixes #3076
2020-07-02 21:10:47 -04:00
ta264 a77d43bd43 Fixed: size CF conditions are now exclusive on min, inclusive on max 2020-07-01 21:05:49 +01:00
Qstick e18e074ee9 fixup FilterFiles mocks in various unit tests 2020-07-01 23:19:58 -04:00
Qstick 4f22d135d6 Fixed: Skip various Extras directories during scan
Fixes: #4487
2020-07-01 21:48:13 -04:00
Qstick 8f791853ad Fixed: Ignore location on UI header fuzzy search
Fixes #4558
2020-07-01 21:29:47 -04:00
ta264 e870fd5215 Fixed: Use originalFilePath in CF if scene name missing 2020-06-30 22:34:41 -04:00
Qstick 6706138fa0 New: Add Language Tests 2020-06-30 22:32:11 -04:00
Agneev Mukherjee 5ee2b10c2c Fixed: Updated icon for Apple Touch devices (#4518)
* Delete apple-touch-icon.png

* Updated icon
2020-06-29 23:05:58 -04:00
Qstick ccb206aed1 New: Show Year in Header Search Results
Fixes: #4547
2020-06-29 22:57:22 -04:00
Qstick cdd653bea9 Fixed: Show AltTitle or Tag if found by in search 2020-06-29 22:56:35 -04:00
Qstick afada848c8 Change Missed http instances to https in HttpClientFixture 2020-06-29 22:01:32 -04:00
ta264 e09ca145d1 Fixed: False positive in remote path check with transmission
Correctly use the download directory when it's set
2020-06-29 10:24:52 +01:00
Qstick a8deaf85c0 Fixed: Bootloop due to bad format args in AppFolderFactory
Fixes #4553
2020-06-28 23:08:59 -04:00
Qstick f069801eba New: Ensure all unmapped folders are fetched when importing from a root folder
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>

#4548
2020-06-28 22:57:46 -04:00
Qstick 5edbe4200b fix lint issue in MetadataSettings.js 2020-06-28 11:55:49 -04:00
Qstick 5b6bef36f2 Refresh Yarn.lock, net core 3.1.5 2020-06-28 11:49:52 -04:00
Qstick 79c35fabfa New: RemotePathMapping HealthCheck
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2020-06-27 18:47:18 -04:00
Qstick ee9d35e55f UI fixups from Lidarr 2020-06-27 18:13:31 -04:00
Qstick c0bb1392e2 Fixed: Don't Show NoMovie on Cal if Movies are Loading
Fixes #4499
2020-06-25 21:53:34 -04:00
Qstick 8c84047a56 Update NuGet.config 2020-06-24 21:03:02 -04:00
Qstick 0e563db10b Fixed: Typo in exclude modal 2020-06-24 10:55:50 -04:00
Doug Krahmer 3a7b1741d9 Fixed: Skip sample check for DVD image files (iso, img, m2ts) (#4531)
* Add support for video files with non-lowercase extensions.

* Fix file scan ignoring DVD image files (iso, img, vob, m2ts)
Always allow DVD and Bluray file types without analysis, instead of detecting as 0 runtime.

* Use extensions to detect DVD image files instead of Quality enum
Add unit tests

Co-authored-by: Doug Krahmer <doug.git@remhark.com>
2020-06-23 21:41:16 -04:00
Qstick 8687dbda1d Fixed: Don't fail lookup if no Recommendations for a result 2020-06-21 22:14:31 -04:00
Qstick c896833607 Fixed: Don't fail lookup on some IMDB Searches
Fixes #4533
2020-06-21 22:11:45 -04:00
Qstick 3a7c4b2cfe Cleanup Episode References 2020-06-21 21:44:05 -04:00
Qstick a946d5886c Use HTTPS for httpbin tests 2020-06-21 15:13:38 -04:00
Qstick dfba9e9b4d Fixed: Update Credits during refresh instead inserting old record 2020-06-21 14:54:38 -04:00
Qstick 28e6e4ed7b Update HttpClientFixture.cs 2020-06-20 16:59:05 -04:00
Qstick 22d0f9ffef New: Remove Anime Category for Newznab/Torznab 2020-06-20 15:55:31 -04:00
Qstick 7120a20984 Fixed: Don't hang on parsable, non-matched releases 2020-06-20 15:39:11 -04:00
Qstick 40b5702ca6 Fixed: Downloads with un-parsable releases stuck on import 2020-06-16 23:18:46 -04:00
Qstick 3c7f80612d Fixed: Sorting of queue by movie title when unknown items are included
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-06-16 22:14:40 -04:00
Qstick 2328b384e2 Fixed: Exception thrown when marking download as complete
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-06-16 22:13:03 -04:00
Qstick 566fa8b132 Fixed: Displayed root folder path truncated when adding a movie with a long title
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-06-16 22:00:49 -04:00
Qstick 8d57c5497a Update wiki links that reference Sonarr/Lidarr
Fixes #4516
2020-06-16 21:58:39 -04:00
Qstick 62b3ed5d48 Fixed: Speed Up List Fetch 2020-06-14 17:42:08 -04:00
Qstick 6802bfc736 Fixed: List UI Revamp 2020-06-14 17:42:08 -04:00
Qstick 2d59192a9e New: Movie Discovery/Recommendations Reworked 2020-06-14 17:42:08 -04:00
Taloth Saldono 3e3b2a7784 Revised webpack bundling and updated worker loading, turned inline worker on by default. 2020-06-10 21:50:14 -04:00
Qstick 9c3b4e3025 Fixed: Update Fuse to allow search over 32 without crash 2020-06-10 21:49:52 -04:00
Qstick b9c59e5482 Fixed: Collection not stored for Movies 2020-06-10 14:06:24 -04:00
Qstick 1849ce4190 Additional logging when trying to complete tracked downloads
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-06-09 00:06:31 -04:00
Taloth Saldono 021e7b8163 Log contents on api errors during tests. 2020-06-08 23:53:17 -04:00
Arthur Bols 4b25ef6deb New: Removed chown and simplified chmod options for linux/osx
Closes #3760
Closes #3752
2020-06-08 23:44:57 -04:00
Qstick 30def1f53a New: Multiple Recipients on Email Notifications (Also CC, BCC) 2020-06-07 18:21:11 -04:00
Gabriel Patzleiner 57961df1df Fixed: #4394 parsing multi language from ReleaseGroup name 2020-06-07 16:48:25 -04:00
Gabriel Patzleiner 40b630ef10 Fixed: Updated GetSceneName. It will clean the ReleaseTitle now.
Added some SceneChecker tests
2020-06-07 16:48:25 -04:00
Gabriel Patzleiner e59257f457 Fixed: correctly replacing SimpleReleaseTitle by A Movie. This resolves a lot of cases where a wrong language or edition has been parsed because it has parsed a part of the movie title (also applies to custom formats parsing) 2020-06-07 16:48:25 -04:00
Gabriel Patzleiner a6c2b2e039 Fixed: some parsing issues (Add dot after "Dr" and fixed movies starting with an edition tag to match the edition (e.g. Uncut Gems (2019)) and added some tests for them 2020-06-07 16:48:25 -04:00
Gabriel Patzleiner 51de8f16fb New: Removed the whole lenient parsing and lenient mapping functionality 2020-06-07 16:48:25 -04:00
Gabriel Patzleiner 5f5391db47 Fixed: some releases not getting parsed correctly if lenient parsing is enabled.
It fixes releases that had some strings (especially editions) between the year and the language (The.Movie.2016.EXTENDED.German.720p...)
2020-06-07 16:48:25 -04:00
Gabriel Patzleiner 73eba0f95d Fixed: some Parser problems (Improved editions and German releases)
- Moved the ReportEditionRegex up because we use it in another 2 regexes (so it's not 3 times the same regex). Also added an optional bracket at the beginning.
- Added Recut to the edition regex
- The Regex for german and french tracker formats (ReportMovieTitleLenientRegexBefore) has been updated to support the same editions as the english versions, but the regex is only used if ParsingLeniency is set to Lenient. Should resolve a lot of cases for german releases where the movietitle wasn't parsed correctly before.
- Updated acronym method. Fixed wrong dots for "World War Z.", "World War Z. 2", but still supports "R.I.P.D.", "V.H.S. 2", "G.I. Joe" and "2 Tage in L.A."
- Added a lot of tests for this changes (there is a new test for german releases that works without lenient parsing, and another one that only works if lenient parsing is activated)
2020-06-07 16:48:25 -04:00
Qstick 1ef31e8094 Fixed: Visibility of NetImport Base Abstract Classes 2020-06-07 01:10:22 -04:00
Qstick b1fd924188 New: List Status Checks/Backoffs 2020-06-07 01:10:22 -04:00
Qstick d10e60587b fixup Sonarr stylecop issues 2020-06-07 01:01:01 -04:00
Taloth Saldono 88879e0db6 Fixed flaky test. 2020-06-07 00:46:18 -04:00
Taloth Saldono 2a9160f870 Added Plex url to cleanser 2020-06-07 00:45:26 -04:00
Qstick 64382e13a4 New: Allow Nested Movie Folders 2020-06-07 00:06:56 -04:00
dependabot[bot] 232682f109 Bump websocket-extensions from 0.1.3 to 0.1.4
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-06 16:56:17 -04:00
Qstick d07dd33a9e New: Upgrade sqlite to 3.32.1 and system.data.sqlite to 1.0.113
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2020-06-06 16:43:58 -04:00
Qstick 7adb358d1c Fixed: Speed up mass deletes from Movie Editor (#4463) 2020-06-04 23:35:02 -04:00
Qstick 913037c45e Fixed: Unknown Movie Releases stuck in ImportPending 2020-06-04 20:03:28 -04:00
Qstick b00326424f Fixed: Add TMDB Collection Id to Kodi NFO
#4442
2020-06-03 22:35:19 -04:00
Qstick 67ff8d39da Fixed: DVDR Parsing as DVDRip
Fixes #4486
2020-06-03 22:18:07 -04:00
Qstick 13f3d0292c Fixed: Trim whitespace on Remote Mapping Add
Fixes #4481
2020-06-03 21:38:46 -04:00
Taloth Saldono 9ca291ecaf Cleanse remote IP Address from trace log file 2020-06-03 20:43:47 -04:00
Qstick 1fcb927b3b Revert conditional map to prevent validation failures in add service 2020-06-01 22:10:34 -04:00
Qstick be9b2d9ebb New: Speed up list sync processing 2020-05-31 22:54:04 -04:00
Qstick 97ff509025 Fixed: Recognize 8 Digit IMDB in parsing/rss lists
Fixes #3637
2020-05-31 22:19:18 -04:00
Qstick c6c4eb0129 Add Test Fixture for Release Group Aggregation 2020-05-31 15:37:29 -04:00
Qstick aefa4aa20b Fixed: Not removing seeded download if it was manual imported in some cases
Fixes #4474

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-30 15:04:26 -04:00
Mark McDowall 1209e3cefb Fix some styling issues in Quality Profile and Release Profiles 2020-05-30 15:01:55 -04:00
Mark McDowall 70a2da0f74 Fixed: Deleting row from middle of filter builder leading to error 2020-05-30 14:57:56 -04:00
Qstick 5de1f4563a Fixed: Manual Import Movie filter Input losing focus
Fixes #4297

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-30 14:46:51 -04:00
Qstick 8112ca5a43 Fixed: Use User Slug for User List Trakt Requests (Requires Re-Auth for affected users)
Fixes #4168
Fixes #4440
2020-05-29 23:35:08 -04:00
ta264 f2a34e5eda Swap to servarr trakt auth 2020-05-29 07:24:37 +01:00
Qstick 6ecffc9bed Fixed: Don't fail on duplicate Alt Titles (Temp) 2020-05-26 21:57:16 -04:00
Qstick f0be9994b1 Fix parameter in logging statement from AddMovieService 2020-05-25 16:54:58 -04:00
geogolem 9ede227178 this is a follow-up to commit:
https://github.com/Radarr/Radarr/commit/64e9e48217002604c36a778d485c23b32352f72e
2020-05-25 01:14:03 -04:00
Qstick f59b391b28 Fixed: Don't name with "0" year if no year in db 2020-05-25 01:05:42 -04:00
Qstick de2ebba363 Fixed: TitleThe Naming Token Handling (and tests) 2020-05-25 01:01:23 -04:00
Qstick 2a3d22038f Rename MovieFolderCreatedEvent Props 2020-05-25 00:53:41 -04:00
Mark McDowall baa6972a62 Fixed: Rejections custom filter for Interactive Search (now Rejections Count) 2020-05-25 00:46:45 -04:00
Mark McDowall 7f1497974f Queue status/timeleft improvements
New: Queue status icon is purple when download is waiting to import or importing
Fixed: Timeleft on Queue won't show when completed
Closes #3743
2020-05-25 00:45:37 -04:00
Qstick d01c0afa56 Fixed: Parse WEB at end of Release Title, Improve truncated names
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-25 00:42:03 -04:00
Qstick 874b1bd17c Fixed: Rotating mobile device when modal is open won't reset modal
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-25 00:22:33 -04:00
Qstick f00f4d0c2c Fixed: Consider Released if we have a digital release
Fixes #4460
2020-05-25 00:14:41 -04:00
Qstick b997bf21a5 New: Don't close manual import when clicking outside the modal
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-24 23:29:01 -04:00
Qstick 62de7b63bd Add Test Fixture for Alt Title Housekeeper 2020-05-24 22:37:05 -04:00
Qstick 3e5626f894 Fixed: Housekeeper for Credits (Left behind from deleted caused later add issues) 2020-05-24 22:36:40 -04:00
Qstick ba6ba06d9b Fixed: Manual Imports for Unknown Movies
Fixes RADARR-Z
Fixes RADARR-1E
Fixes #4379
2020-05-24 22:35:22 -04:00
Qstick 1d6a7a1843 Revert "Async HttpClient and list lookup"
This reverts commit c8a2af867e.
2020-05-24 17:06:42 -04:00
Qstick 0479bb9cc3 Revert "Revert "Revert "Use async api calls for Refresh and NetImportSync"""
This reverts commit 0080e6b685.
2020-05-24 17:06:42 -04:00
Qstick 4951a97984 Revert "Force min 4 threads in pool"
This reverts commit 789e47dfff.
2020-05-24 17:06:42 -04:00
Qstick 70601faa4f Bump Core Packages for 3.1.4 2020-05-23 23:34:58 -04:00
Qstick 846b3324e5 Bump UI Packages 2020-05-23 23:22:35 -04:00
Qstick 5761ce640b Fixed: Actually make SimpleReleaseTitle work 2020-05-23 23:01:03 -04:00
ta264 789e47dfff Force min 4 threads in pool 2020-05-23 21:40:34 -04:00
Qstick 0080e6b685 Revert "Revert "Use async api calls for Refresh and NetImportSync""
This reverts commit 250cc09239.
2020-05-23 21:39:59 -04:00
Qstick 250cc09239 Revert "Use async api calls for Refresh and NetImportSync"
This reverts commit 2bdd806565.
2020-05-23 16:16:45 -04:00
Qstick 00b9118dbd Fixed: Pass no parameter instead of null parameter on Kodi Update
Fixes #4452
Fixes #4230
2020-05-22 18:02:21 -04:00
Qstick d3d3117bf3 Cleanup TMDB Resources 2020-05-20 11:00:20 -04:00
Qstick 50f84101e0 Map properties returned from TMDB lists to avoid needing server re-mapping 2020-05-20 11:00:20 -04:00
ta264 2bdd806565 Use async api calls for Refresh and NetImportSync 2020-05-20 11:00:20 -04:00
ta264 4a5012f98e Fix net import sync - title won't exist until movie mapped 2020-05-20 11:00:20 -04:00
ta264 c8a2af867e Async HttpClient and list lookup 2020-05-20 11:00:20 -04:00
Qstick c64c2d9f27 New: Use RadarrApi For MovieInfo 2020-05-20 11:00:20 -04:00
Qstick 9bdaea4a1b Fixed: Tag details list movies in alphabetical order
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-19 22:08:29 -04:00
Taloth Saldono e8e852100c Fixed recursion issue when emptying recycle bin 2020-05-19 22:02:55 -04:00
Taloth Saldono 83679214b3 Fixed: Added .org to website url filtering in parser 2020-05-19 21:57:19 -04:00
Qstick 10074ae994 Fixed: Use v3 API for Lists to prevent needing pagination
Fixes #4441
2020-05-18 21:14:52 -04:00
Qstick 78e83488b9 Update bug template [skip ci] 2020-05-18 20:25:34 -04:00
ta264 e52005fa35 New: Warn if UI won't update due to SignalR errors 2020-05-18 18:06:18 +01:00
ta264 6463befc79 New: HealthCheck to warn if running legacy mono version 2020-05-15 16:48:23 +01:00
ta264 d03a6486d3 Fixed: NET Core not deleting source when moving across drives
This reverts commit 10fc0b071f.  Use the
mono fix from 43a35c8447 in NET Core also
2020-05-15 10:43:28 -04:00
ta264 5c9b85972d Fixed: SQL error searching for movie by name 2020-05-15 10:41:47 -04:00
ta264 32a6c9fe2a Revert "Revert "Fixed: Rename more than 999 movies in one go""
This reverts commit c0b80696bc.
2020-05-15 10:41:47 -04:00
Qstick ebc72bfba9 New: AV1 Video Codec Formatting
fixes RADARR-K
fixes RADARR-R
fixes RADARR-6C
2020-05-14 23:03:03 -04:00
Qstick c0b80696bc Revert "Fixed: Rename more than 999 movies in one go"
This reverts commit 2c6b13dd11.
2020-05-14 21:14:18 -04:00
ta264 78c7372a0d Fixed: Ensure SSL cert exists before saving config
Trap missing certificate exception to avoid bootloop

Fixes #4403
2020-05-14 23:07:58 +01:00
ta264 6ad3653c04 Trap Kestrel Address bind exception to avoid bootloop 2020-05-14 23:07:58 +01:00
ta264 2c6b13dd11 Fixed: Rename more than 999 movies in one go
Also pulls across Dapper improvements from Readarr.

Fixes Sentry RADARR-1V
Fixes #4359
2020-05-14 22:41:59 +01:00
Qstick 93d27c70c4 New: AddMovieService to validate and populate incomplete adds 2020-05-14 09:57:08 -04:00
ta264 8b06df1b1a Disable discord notification for fork PRs 2020-05-14 07:17:11 +01:00
Qstick daee51a8ab Remove HUGE logo from Readme [skip ci] 2020-05-13 23:37:58 -04:00
Qstick 5e0ba314d2 Fixed: Kodi GetMovies fails due to Parameter Type
Fixes #4405
2020-05-13 21:52:08 -04:00
Taloth Saldono 4606503818 Fixed: Performance issue when scanning large root folder 2020-05-13 20:29:16 -04:00
Qstick 942d239092 Bump .net core to 3.1.4 2020-05-13 10:36:17 -04:00
ta264 a19bcf9683 Fixed: CustomFormat size specs in already grabbed check
Sizes need to be parsed as a long not an int else anything with a size
> 2GiB will fail to be parsed and be set with size 0

Fixes #4262
2020-05-12 21:43:08 +01:00
Qstick 95f66117e4 Fixed: DownloadedMovieScan API should delete source folder if ImportMode is Move 2020-05-10 12:34:16 -04:00
Qstick f332f8d7cd New: Dont Use Profile Language for Metadata Pull 2020-05-09 18:31:37 -04:00
Qstick d6f15af6b6 Fixed: Command Inherited Properties not Saved to DB 2020-05-08 17:05:57 -04:00
Qstick ce9fc48910 Fixed: Warn Series reference in CompletedDownloadService.cs 2020-05-07 20:34:44 -04:00
Taloth Saldono c988151b5d Replaced matchAll usage since it's not available on all browsers 2020-05-06 12:06:11 -04:00
Taloth Saldono cd50767273 Added UserAgent to api request trace log 2020-05-05 20:44:46 -04:00
Qstick e46c171d54 Fixed: Windows installer won't create shortcut if unchecked
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-05-03 20:59:54 -04:00
Taloth Saldono dbc784e94b Added UpdateMechanismMessage to allow package maintainers provide custom message 2020-05-03 20:53:00 -04:00
Taloth Saldono ac37285be6 Inline markdown-style link for PackageAuthor 2020-05-03 20:49:30 -04:00
Qstick 13bd9535cc Log exception message with audio channel warn 2020-05-03 16:31:17 -04:00
Qstick cb97f90b9e Allow DownloadMonitoring to report client status 2020-05-03 16:31:17 -04:00
Qstick 91ed949a47 Fixed: Gotify Log Statement 2020-05-03 16:31:17 -04:00
ta264 f53a13ab41 Make windows automation failure optional 2020-05-03 19:16:56 +01:00
Taloth Saldono 4d1dbfe559 Lock CommandQueueManager.PushMany too 2020-05-03 13:08:10 -04:00
Taloth Saldono 1fc49f2aa1 Skip unknown/removed commands still queued in the database 2020-05-03 12:17:10 -04:00
Taloth Saldono 8d2d19d17b Fixed timing issue allowing multiple instances of the same command to be queued 2020-05-03 11:52:31 -04:00
Mark McDowall cc283f64a3 New: Add DownloadClient and DownloadId to Webhook notifications 2020-05-03 11:50:45 -04:00
Qstick 5ddc63c673 Fixed: Revert GetAllMovies call to AllMoviePaths 2020-05-01 21:16:17 -04:00
Qstick 41d7bf87c0 Fixed: Resource missing from Gotify call 2020-05-01 21:01:17 -04:00
Mark McDowall 771f7e9261 Add class to allow for overriding scrollbar width 2020-04-30 20:03:59 -04:00
Taloth Saldono eb98a7e8be Improved error message when nzb download contains an newznab error instead 2020-04-30 20:03:20 -04:00
Mark McDowall d4817e9ff2 Improve root folder health check
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-30 19:57:02 -04:00
Qstick e71973b16c Cleanup Sonarr References 2020-04-30 19:46:53 -04:00
Qstick d36608b4fd Fixed: Imports triggered through API not being marked as imported/removed from client
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-30 19:40:50 -04:00
Qstick 73eb83c9dc Fix Indent Issue from cherry pick 2020-04-29 20:55:57 -04:00
Mark McDowall 4928261366 Fixed: Imported downloads not being removed when seeding goals are met
Fixes #3693
2020-04-29 20:42:53 -04:00
Qstick c44fa60721 Clarify that Post-Import Category torrents are not monitored by Radarr
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-04-29 20:41:57 -04:00
Taloth Saldono 7afea3df90 Log Real IP on Authentication failure in case of a reverse proxy
closes #3711
2020-04-29 20:37:40 -04:00
ta264 e8018b7959 Fix sentry project slugs 2020-04-28 07:13:49 +01:00
ta264 0b36a83cc4 More sentry updates 2020-04-28 06:26:05 +01:00
ta264 dc66e92c48 Fixed: Failing to start when RenameMovieFolders queued 2020-04-28 06:07:04 +01:00
Qstick 17c60f897b Sentry DSN Update 2020-04-27 18:56:35 -04:00
Qstick c8de38ef23 Fixed: List Connection Error Mentions Indexer 2020-04-26 20:33:34 -04:00
Qstick c2761ef16c Fixed: Don't lock command queue if updating is disabled 2020-04-26 15:30:35 -04:00
Qstick 3b6e20908e Fixed: Don't Throw Error if No Movies on Discover
Fixes #4356
2020-04-26 15:16:56 -04:00
Mark McDowall e6479e9a53 Fixed: Movies removed from queue re-appearing on refresh 2020-04-26 14:54:29 -04:00
Qstick 01e6fbddf4 Fixed: Rejection message for quality mismatch
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-26 14:54:29 -04:00
Mark McDowall 0f0afd62bb Fixed: Remove seeded downloads if they've finished seeding after import 2020-04-26 14:54:20 -04:00
gwenvador eee6fd9f54 Update CompletedDownloadService.cs
Replaced Sonarr by Radarr
2020-04-26 09:52:44 -04:00
Qstick a0fe43cd6e Fixed: Task Error due to Not Found in Scheduled Tasks 2020-04-25 16:50:17 -04:00
Qstick 91b5e359bd Fixed: Default new install to Aphrodite
Fixes #4354
2020-04-25 16:23:19 -04:00
rg9400 13fc878168 New: Clarify Replace Illegal Characters setting (#4353)
* Clarify Replace Illegal Characters setting

* Remove simply
2020-04-25 13:55:46 -04:00
Qstick 641d43c6f3 Simplify YML for Discord Notification (#4349)
* Simplify YML for Discord Notification

* fixup! Simplify YML for Discord Notification
2020-04-24 16:04:28 -04:00
Qstick bb471d2d6d Fixed: Date parameter for TMDB
Fixes #4343
2020-04-24 15:00:40 -04:00
Qstick cfb8678c59 Mobile Edit Oops 2020-04-24 11:38:23 -04:00
Qstick 437c4a78c1 Fixed: Notification still shows disabled with onHealthIssue
Fixes #4347
2020-04-24 11:37:30 -04:00
Qstick df76c679ff Discord Azure Notification 2020-04-24 08:45:42 -04:00
Qstick d30c1b1385 Fixed: Queue Not Rendering
Fixes #4341
2020-04-23 15:04:28 -04:00
Qstick 0b113b9bab Fixed: Queue Items don't show up 2020-04-22 19:12:33 -04:00
Qstick f3d7852ec4 Fixed: Queue not always clearing checked items when updated
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-22 17:33:08 -04:00
Qstick 824d315a3b New: Download History
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-22 17:33:08 -04:00
Qstick 72caab1b2b New: Monitor and Process downloads separately
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-22 17:33:08 -04:00
Qstick f430d1aab2 Update azure-pipelines.yml 2020-04-22 14:18:38 -04:00
Qstick d21e7b8f1a Warn and continue on source map upload failures 2020-04-22 14:02:16 -04:00
Qstick 750b819086 Update azure-pipelines.yml 2020-04-22 13:37:23 -04:00
Taloth Saldono 43a35c8447 Another mono 6.x workaround to use rename rather than expensive copy 2020-04-20 13:42:24 -04:00
Qstick 022af2053a fixed props validation 2020-04-19 14:12:44 -04:00
Qstick 90213554bb Fixed: Cannot Edit Branch in Update Settings 2020-04-19 14:08:31 -04:00
Qstick 6d87ce73b6 Fixed: Gotify token as query parameter
Fixes #4321
2020-04-19 13:47:45 -04:00
Qstick 8c4ac632ca Update stale.yml 2020-04-19 03:29:03 -04:00
Qstick e819eec41f Fix SendGrid Exception copy/paste 2020-04-13 18:39:05 -04:00
Qstick ac34684850 New: SendGrid Support 2020-04-13 00:31:13 -04:00
Qstick cf1cea751b Fixed: Radarr Sync doesn't set path, tags, profile, avail 2020-04-12 15:27:42 -04:00
Qstick df961860f3 Fixed: ReEnable Multi Language Settings for Indexers 2020-04-11 00:26:09 -04:00
Qstick 55134d76c1 New: Allow Filter by Profile from Radarr Instance List 2020-04-11 00:17:42 -04:00
Qstick 6739bf72c4 New: Allow query by TMDbId on Movies endpoint 2020-04-10 23:45:26 -04:00
Qstick 9f35dcd900 Fixed: Toolbar button collapsing includes separator
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-10 21:28:44 -04:00
Mark McDowall 93ad148b03 Fixed: Ignore .@__thumb folders 2020-04-10 21:26:45 -04:00
Qstick ecaff28231 Fixed: Tooltips for Existing and Exclusion Icons in Search
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-10 21:26:13 -04:00
Qstick 4abf44617c Convert Notifications from RestSharp to HttpClient 2020-04-10 21:22:45 -04:00
Qstick 39250abf7d Fixed: Add AltTitles from TMDB if Mapping Fails
Fixes #4300
2020-04-09 21:22:33 -04:00
Qstick a711cbd475 Fixed: Movie History showing ascending by date 2020-04-09 19:22:06 -04:00
Qstick 975d31178b Fixed: History Repo GetById not always ordered by Date 2020-04-08 22:13:32 -04:00
Qstick 415c2821c8 Fix Qbit Test 2020-04-08 21:59:47 -04:00
Qstick f891f25f9d Fixed: Strip AlteZachen from release group name
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-08 21:33:59 -04:00
Qstick 0042fadcb0 Fixed: Don't try to render quality when it's null
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-04-08 21:31:47 -04:00
Mark McDowall 34fff4ef26 Fixed: Treated checkingUP status from Qbit as queued in case it fails to validate 2020-04-08 21:27:49 -04:00
Qstick eea99b034e Fixed: Scroll issue in Root Path selector on mobile
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-04-08 21:24:10 -04:00
Qstick 30664ea806 New: Added Norwegian Bokmal alias
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-04-08 21:18:19 -04:00
Qstick 37d004d78d Fixed: Copy linux permission mask when moving to recycle bin
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-04-08 21:15:58 -04:00
Qstick 22062931fe Fixed: Set SimpleReleaseTitle to get CFs on Unparsable Release
#4291
2020-04-07 21:21:22 -04:00
Qstick d44f0d23ea Fixed: ExtraFiles and PendingReleases not deleted correctly 2020-04-05 12:27:34 -04:00
Qstick 2d0fdf144b Fixed: Update Mechanism doesnt show in Settings 2020-04-05 10:17:59 -04:00
Qstick 727347331f Fixed: More robust Certification Logic 2020-04-05 03:30:03 -04:00
Qstick 8c25ccf1ea Update Frontend Packages 2020-04-05 02:06:13 -04:00
Qstick d59c563023 Update Backend Packages 2020-04-05 02:06:13 -04:00
ta264 d5e35335bb Update to net core 3.1.201 2020-04-05 02:06:13 -04:00
Qstick 43c48bf833 New: Quality Preferred Setting 2020-04-04 18:11:46 -04:00
Qstick ea18a366c4 fixup! Code Smells 2020-04-04 11:16:01 -04:00
Qstick 0a1eb877e0 New: Set Branch, Update Mech from PackageInfo 2020-04-04 11:16:01 -04:00
Qstick 16d6fd90a9 Only Close Stale Issues, Not PRs 2020-04-04 10:38:34 -04:00
Qstick fac49c1da3 New: Seach All/Filtered on Index 2020-04-03 15:51:03 -04:00
Qstick fb7bd4f14f Fixed: Don't cutoff Filter menu on mobile 2020-04-03 15:37:31 -04:00
Qstick 74c0e409e7 Fixed: Skip MovieDetails Ratings Render if Null 2020-04-03 10:06:24 -04:00
Qstick 1137c8d770 Fixed: Show Separator on all index views 2020-04-02 23:11:22 -04:00
Qstick 68dec70b34 Fixed: Missing Sort Items on Poster View 2020-04-02 23:08:19 -04:00
Qstick dd52760095 New: Movie Certifications 2020-04-02 21:47:52 -04:00
Qstick 770e3379fb New: Trakt List Organization, User Collection List Support 2020-04-02 12:42:06 -04:00
Qstick 7adfe65f65 New: Import from Another Radarr Instance
Co-Authored-By: Leonardo Galli <galli-leo@users.noreply.github.com>
2020-04-02 12:42:06 -04:00
Anthony Borushko 57057911ab Fixed: Tag inputs respect non-QWERTY layouts 2020-03-31 23:34:32 -04:00
Qstick 7a1fa71b76 New: Add back Prowl Notifications 2020-03-29 22:48:21 -04:00
Qstick d3af103812 Fixed: Don't wrap Health Actions Cell 2020-03-29 09:28:31 -04:00
Qstick 0431b9561e Fixed: Cleanup HealthCheck wiki links 2020-03-29 09:03:42 -04:00
Qstick f4e334c14e New: Add Filter Value for Deleted MovieStatus 2020-03-29 08:37:45 -04:00
Qstick ef2f954b81 New: HealthCheck for valid Branch value 2020-03-28 16:01:38 -04:00
Taloth Saldono 12041ac290 Fixed: Audio Channel Information missing in MediaInfo for certain mkv files with DTS audio 2020-03-22 22:01:27 -04:00
Mark McDowall e6f5d535e9 New: Clone indexer button
Closes #3546
2020-03-21 00:05:33 -04:00
Qstick f338941cfc New: Show ExtraFiles in UI 2020-03-20 23:50:15 -04:00
Qstick 3576f529ec Fix Indent Issues (Update Eslint/Stylelint) 2020-03-20 23:50:15 -04:00
Qstick 5a5e896eb4 Fixed: Misc Calendar Improvements 2020-03-20 23:50:15 -04:00
Qstick b2c1dbf3ab Fixed: MovieDetails warnings if InCinemas is null 2020-03-20 23:50:15 -04:00
Qstick 990a65a681 Fixed: Change MovieHistoryDetails to Modal 2020-03-20 23:50:15 -04:00
Qstick 086640519a Fixed: Error Rendering Queue Row on Null Quality
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-03-20 22:50:03 -04:00
Qstick d99519cb23 New: RSS Sync button on Calendar
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-03-20 22:50:03 -04:00
Qstick aae0ef200d New: Tmdb and Imdb Webhook Properties 2020-03-20 22:50:03 -04:00
Qstick dfdffb0626 Fixed: Remove website post fix before parsing
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-03-20 22:50:03 -04:00
Qstick ae1e12f905 Fixed: Update help text in Connections from Download to Import
Co-Authored-By: hugepants <hugepants@users.noreply.github.com>
2020-03-20 22:50:03 -04:00
Qstick c49867a08d Fixed comment typo in webpack config
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-03-20 22:50:03 -04:00
Qstick 669e46266a Fixed: Typo in ReleasePushModule.cs 2020-03-20 22:50:03 -04:00
Taloth Saldono 755fa9e865 Fixed: Workaround for mono 6.x file copy/move issues 2020-03-20 22:48:30 -04:00
kiityman1 ed72713ba7 Fixed: Enter on Delete profile confirmation deleting all unused profiles (#4241) 2020-03-20 21:49:51 -04:00
Qstick 2fbcf17c8d Fix Linting Error (Double Blank Line) 2020-03-11 22:23:35 -04:00
Qstick f37fb47058 Fixed: Scrolling and Hotkey improvements
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-03-11 22:23:07 -04:00
Mark McDowall 55758a2772 New: Ignore #recycle folders (Synology Recycle bin folder) 2020-03-11 22:10:16 -04:00
Qstick 363be4ca34 Fixed: Metadata files not being created after rescan
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-03-11 22:09:08 -04:00
Qstick 5128481b23 New: Additional Path Validation on Movie Add 2020-03-08 22:49:00 -04:00
Mark McDowall fd3acd85e6 Fixed: Broken tasks getting stuck in queue 2020-03-08 22:37:10 -04:00
Qstick 5b6a6cc9dc Fixed: Delete a movie file from the UI that was already deleted from disk 2020-03-07 14:52:48 -05:00
Qstick b50fdd637b Change to Servarr TestImages 2020-03-07 00:44:34 -05:00
Qstick fba1a5acb9 Fixup test failures 2020-02-29 23:42:01 -05:00
Taloth Saldono 8babe7205b Fixed: Removed .Net update notice on Windows LTSB 2015 2020-02-29 22:30:26 -05:00
Mark McDowall e02151b273 New: Add Tabula Rasa Newznab Preset 2020-02-29 22:25:03 -05:00
Qstick 0c44ee5f88 New: HealthCheck and Status for Movies Deleted from TMDb 2020-02-29 21:21:36 -05:00
ta264 ea9494019e Fixed: Allow saving profiles with large negative CF scores 2020-02-29 16:23:19 +00:00
Qstick e986869e96 New: CustomFormat Naming Token 2020-02-29 13:03:58 +00:00
ta264 50d6c5e61e New: User defined scores for each Custom Format
Brings it more into line with Sonarr preferred words
2020-02-29 13:03:58 +00:00
Qstick da80793204 Fixed: Check for EnableCompletedDownloadHandling Enabled, Not Defined 2020-02-27 00:02:09 -05:00
ta264 2d78e0ebb6 Fix azure executable bit warnings 2020-02-26 20:04:32 -05:00
ta264 ac662ab3c1 Pull build.sh speedups from Lidarr 2020-02-26 20:04:32 -05:00
ta264 456169b8d2 Cache integration test DB to avoid repeated migrations 2020-02-26 20:04:32 -05:00
ta264 f3308827d0 Cache database for Unit tests to avoid repeated migrations 2020-02-26 20:04:32 -05:00
Mark McDowall d6cac3add8 Fixed: Set permissions on extra and subtitle files 2020-02-25 22:44:02 -05:00
Taloth Saldono 42b4a03eb4 Fixed redirect test 2020-02-25 22:34:05 -05:00
Петр Шургалин 1e4bdcc324 Fixed: RestClient does not use global proxy settings
Co-Authored-By: Petr Shurgalin <pshurgal@users.noreply.github.com>
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2020-02-25 22:32:16 -05:00
Qstick c45cff87fc Fixed: Test All not clearing health error for clients
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-02-25 22:06:28 -05:00
Qstick 7de9ef3762 MovieIndexActions no longer uses JQuery 2020-02-25 21:34:14 -05:00
Qstick 9ccdb4871d Fixed: Don't append every MovieId to Delete as a URL Parameter
Fixes #4186
Fixes #4188
2020-02-25 21:28:33 -05:00
Qstick 7173c5c737 Fixed: Sort qualities by name within group when sorting by status
Relates to #4189, Optimal end state would be to sort by quality weight within group, however that is not currently passed from API with the quality object.
2020-02-25 21:02:10 -05:00
Qstick 64e9e48217 Fixed: Show "Not Available" instead of "?" when no InCinemas date 2020-02-25 20:33:43 -05:00
Qstick 3de65daf85 New: Runtime Column/Filter on Movie Index
Fixes #4131
2020-02-25 20:30:42 -05:00
ta264 7584d95149 Fixed: Not showing past first few pages of history 2020-02-25 20:10:14 -05:00
Qstick d7fd48ef25 Fixed: Use TMDB ReleaseDate Year for Year (Match Cinema Date) 2020-02-25 20:07:24 -05:00
Qstick 3969a396d3 Fixed: Change Language on FileNameSampleService so AudioLanguages token works
Fixes #4185
2020-02-24 19:06:33 -05:00
Qstick c7dfd9dcf1 Fix Log Statement Grouping in FreeSpaceSpec
Fixes #4184
2020-02-23 13:07:56 -05:00
Qstick b0afbce97c Fixed: Don’t fail build on Sentry fail 2020-02-22 20:03:43 -05:00
PHOENiX 5d478a5259 Fixed: Page Header Logo Vertical Centering (#4179) 2020-02-22 17:21:05 -05:00
Qstick 63780884c2 Fixed: Show Colon Replacement setting in UI
Fixes #4122
2020-02-22 17:13:42 -05:00
Qstick f05207580c Fixed: Show Edition naming token in UI 2020-02-22 16:57:34 -05:00
Qstick 27551e2975 Fixed: Typo in unmonitored movie tooltip
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-02-22 16:06:09 -05:00
Qstick 76752a5dbd Fixed: Empty list message for System: Events
Co-Authored-By: beyondmeat <beyondmeat@users.noreply.github.com>
2020-02-22 16:02:20 -05:00
Qstick deeb2979f1 Fixed: Parse 360p releases as base quality instead of 720p
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-02-22 16:00:07 -05:00
Qstick d19321733b Fixed: Prompt to restart after resetting API key
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-02-22 15:41:39 -05:00
Qstick f4d069c0cf Fixed: QuickImport uses Invalid Command Name 2020-02-22 15:39:48 -05:00
ta264 241bf85f15 New: Better interface for creating custom formats 2020-02-21 21:29:15 -06:00
ta264 a5bac30ef3 New: Bump .net core to 3.1.102 2020-02-19 21:35:07 -05:00
Qstick b670bffa1b Fixed: Populate Collection on every Resource Map
Fixes #4162
2020-02-17 20:42:36 -05:00
ta264 4c5490df94 Fixed: Improve performance of Manual Import movie selector 2020-02-17 20:34:11 -05:00
ta264 d5ddebb7ac Fixed: Editing provider/profile settings appearing to affect wrong item 2020-02-17 20:34:11 -05:00
Qstick dc22c8df1f Fixed: Focus on Search Bar on Add new page
Fixes #4104
2020-02-15 20:32:03 -05:00
ta264 66ad75b759 Fixed: Don't check update folder writable in docker 2020-02-15 12:17:36 +00:00
desimaniac b0f4a02dc6 New: Add Year to Custom Script (#4141) 2020-02-14 19:59:05 -05:00
Qstick 4a7f497023 Fixed: Cleanup Double Path on Details Page 2020-02-11 21:11:25 -05:00
Qstick ecc68af439 Fixed: Pass Array to Bulk Language Select for Manual Import
Fixes #4143
2020-02-11 21:08:17 -05:00
Qstick 79a05876bf Fixed: Filter by Studio if null studio exists in library
Fixes #4142
2020-02-11 21:01:55 -05:00
Qstick 3ea6d78d39 New: Add MediaCovers endpoint to V3 API 2020-02-11 20:40:31 -05:00
Qstick b766f0693a Fixed: Use movieId instead of seriesId from MediaCover route 2020-02-11 20:34:30 -05:00
Qstick a19f0202b0 Fixed: NullRef on FIleList Parse if IMDB Empty 2020-01-31 19:46:38 -05:00
Qstick c6baff9bce Fixed: Populate Info in Lookup Results if Existing Movie 2020-01-31 06:09:34 -05:00
Qstick 63197d38ce New: Store OriginalFilePath for New Downloads 2020-01-30 19:06:00 -05:00
Qstick e954b01921 Fixed: Remove Static/Dynamic Settings, Allow Folder Move from Editor 2020-01-30 19:06:00 -05:00
Qstick 1c8f94f1d8 New: Filelist.ro Indexer
Fixes #4061
2020-01-30 19:04:12 -05:00
Qstick 3d17498945 Fixed: Queue not loading for unknown releases 2020-01-30 12:42:43 -05:00
Qstick 869ce2b366 Fixed: NullRef on Force Download Unparsable Releases 2020-01-26 01:24:10 -05:00
Qstick 68bfd8bc25 Fixed: Edition not Saving to DB due to missing Aggregator 2020-01-26 01:13:45 -05:00
Qstick fdd1167f33 New: Clean 'Obfuscation' when parsing ReleaseGroup 2020-01-26 00:15:16 -05:00
ta264 4ce4790ed2 Upgrade build agents 2020-01-25 00:25:01 -05:00
ta264 01a03e9baf Fixed: WhereBuilder exception when string variable null 2020-01-24 19:58:19 +00:00
ta264 df101258c5 New: Calculate custom formats on demand 2020-01-23 20:28:55 +00:00
Qstick 13701498ce Fixed: Fix typo in remove queue item modal
Co-Authored-By: Jayden <lukyjay@users.noreply.github.com>
2020-01-22 22:47:30 -05:00
Qstick 09f8dace9d Fixed: Remove website prefixes with dashes in URL
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-01-22 22:45:32 -05:00
Qstick 88cbeb930d New: Allow Sort by Size on Manual Import
Fixes #4097
2020-01-22 22:43:16 -05:00
Qstick 4d3a1efca3 New: Size Column in Queue
Fixes #4063
2020-01-22 22:40:12 -05:00
beyondmeat 27f7dd26ab Fixed: Episode reference in search results string (#4094) 2020-01-22 20:54:18 -05:00
ta264 ba41939c27 Test mono 6.10, drop mono 6.6 2020-01-21 16:37:15 +00:00
ta264 d5869aebc6 Fix tmdb tests
Need to resolve the mapper now that we don't blindly add images
2020-01-21 16:37:15 +00:00
Qstick dfa3df8ea5 Fixed: Only Add Images if not Null on TMDB 2020-01-20 20:59:50 -05:00
ta264 92f7b25117 Fix build for bad azure update 2020-01-21 00:12:21 +00:00
ta264 6cb504cb08 Fixed: Use portable PDBs on net core
Enables stack trace line numbers on linux
2020-01-21 00:12:21 +00:00
Qstick e9de53ef98 Fix MovieFileMovingService create directory error logger 2020-01-19 18:43:26 -05:00
Qstick d89c267e62 Update Popper to 1.3.7
1.3.6 has an infinite loop issue with componentDidUpdate
2020-01-18 12:57:23 -05:00
Qstick b59546b1ab Bump UI Linting Packages 2020-01-18 12:46:00 -05:00
Qstick b73950c1b4 Fixed: Don't NullRef if filter parameters null 2020-01-18 11:29:55 -05:00
Qstick 3287ce6f90 Fixed: FilterSettings not mapping due to no setter (System.Json) 2020-01-16 22:40:29 -05:00
ta264 9cf353b423 Fix typo and rename duplicate ListType property 2020-01-16 22:40:29 -05:00
ta264 377d788223 Fixed: Migrate the old TMDb list definitions to new type 2020-01-16 22:40:29 -05:00
ta264 7255cb49cb Fix .editorconfig for omnisharp 2020-01-16 22:40:29 -05:00
Qstick 86b8dd4856 Fixed: Not deleting movie files during upgrade when root folder is missing
Fixes #4066

Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-01-16 22:38:07 -05:00
Qstick 16c03444ab Fixed: Removed NotifyMyAndroid and Pushalot
Fixes #4033
Tack onto migration 164
2020-01-15 23:12:39 -05:00
Qstick ccc77bfcfe New: Add Collection and Cast data to Kodi NFO
Fixes #3421
2020-01-15 23:12:39 -05:00
Qstick b3caa87b78 New: Add and Edit People Lists from Movie Details Page 2020-01-15 23:12:39 -05:00
ta264 8021381de2 Fix mapper exception 2020-01-15 23:12:39 -05:00
Qstick 65287ec4f3 New: TMDb List Rework 2020-01-15 23:12:39 -05:00
Qstick bdc1adb2ed New: Cast/Crew Tabs on Movie Details Page 2020-01-15 23:12:39 -05:00
Qstick f2fffe5304 New: Collection Column/Filter Movie Index 2020-01-15 23:12:39 -05:00
Qstick 1c91c9939f Fixed: Cleanup NetImport Implementation 2020-01-15 23:12:39 -05:00
Qstick ba83c01b6c New: NetImport Lists Grouped by Type
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2020-01-15 23:12:39 -05:00
Qstick d76423a305 New: Scrape Cast/Crew/Collection Data on Movie Refresh 2020-01-15 23:12:39 -05:00
Qstick dec0e3eec3 Dedupe JS events to prevent 1000s repeated hitting Sentry 2020-01-12 14:59:14 -05:00
Qstick 1fab973c67 Update Sentry-JS from 5.10.1 to 5.11.0 2020-01-12 14:58:15 -05:00
Qstick 70d7b3b70a Fixed: 4 digit year for log lines
#4053
2020-01-12 10:40:14 -05:00
Qstick c8095faa1f New: Size on Disk in Index and Editor
Fixes #977
2020-01-11 17:06:02 -05:00
Qstick d5e6cc542f Fixed: Sort by Status on MovieIndex
Fixes #4018
2020-01-11 16:44:30 -05:00
Fossil 3c1113260d Fixed: NZB Finder categories (#4054)
As discussed here https://github.com/Radarr/Radarr/pull/3568
2020-01-10 13:37:29 -05:00
Qstick 06b56db67c Squash Pre-Fork Sonarr Migrations (#4029) 2020-01-09 20:14:17 -05:00
Qstick b3553e93ab Fixed: Broken Title Sort on Activity Pages 2020-01-09 20:03:08 -05:00
Qstick 7f7226e466 Fixed: Search results shown for different movie if navigate during request 2020-01-08 22:36:25 -05:00
Qstick c060f9fd4d Fixed: Prevent error on MovieFile Delete if no movie references movieFile
Fixes #4042
2020-01-08 22:08:26 -05:00
Qstick e70be7cf9e New: Added help text for qualities in groups
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2020-01-08 21:42:19 -05:00
Mark McDowall 822035056a Fix proptype warning for id of EnhancedSelectInputOption 2020-01-08 21:40:40 -05:00
Mark McDowall cf6e993a4d New: Limit recent folders in Manual import to 10 and descending order
Closes #3491
2020-01-08 21:39:51 -05:00
ta264 6f115d2db3 New: Update MonoTorrent from nuget 2020-01-08 20:53:36 -05:00
Qstick 0845a4bf4c Fixed: Handle 3 digit audio channels 2020-01-07 09:48:03 -05:00
Qstick e0f591ebe3 Fixed: Re-Enable Indexer Flags for Torrent Indexers 2020-01-07 09:47:29 -05:00
Qstick 68ea6fbb4f Fixed: Broken SeedConfigOptions due to System.Text.Json req. for public setters 2020-01-07 09:47:29 -05:00
Qstick 7ad2e7fbdb Update config.yml 2020-01-06 21:21:42 -05:00
Qstick fd3c703e2e Update config.yml 2020-01-06 21:21:28 -05:00
Qstick 8d021ba7e1 Update issue templates 2020-01-06 21:03:33 -05:00
EnorMOZ c77fd1a6fc New: Add Radarr_Download_Client to OnDownload (#4034)
* Add Radarr_Download_Client to OnDownload

* Add Radarr_Download_Client to OnDownload
2020-01-06 07:00:40 -05:00
ta264 4025005251 Fixed: Regression setting file date 2020-01-05 18:05:27 -05:00
Qstick 3a8920a1e9 Fixed: Always full build on branch builds 2020-01-05 00:56:28 -05:00
Qstick c94606dccd Fixed: MediaCover Test Broken in #3982 2020-01-05 00:47:38 -05:00
Qstick 0957bdda20 New: Correct default caps for nzbfinder and omg
Co-Authored-By: Fossil <fossil01@users.noreply.github.com>
2020-01-04 21:16:59 -05:00
Qstick f75b404387 Fixed: Remove PFMonkey.com from Presets
Co-Authored-By: Fossil <fossil01@users.noreply.github.com>
2020-01-04 20:53:44 -05:00
ta264 959b8ed83e Fixed: Speed up UI during refresh
Don't update state if we know items are equal to avoid reselections.
Don't pass LastInfoUpdate to frontend to prevent useless updates (the
field isn't used)
2020-01-04 11:25:26 -05:00
ta264 5b07046396 Fixed: Trigger fewer signalr broadcasts 2020-01-04 11:25:26 -05:00
ta264 8e256462bf Use MediaCoverService from Sonarr 2020-01-04 11:25:26 -05:00
Qstick 80673b572a Fixed: Full Build on Backend, Not Don’t Build If any Non Backend (#4026)
* Fixed: Build on Backend, Not Don’t Build If any Non Backend

* fixup! Quit pushing from 5 inch screen!!
2020-01-03 15:34:28 -05:00
Qstick f3677a49bf Fixed: Use User Selected Languages during Manual Import 2020-01-02 22:55:20 -05:00
Qstick c22c7eff60 Fixed: Don't crash due to null Formats on Quality edit 2020-01-02 22:55:20 -05:00
Qstick 51af4011a3 Fixed: Remove unused Manage Files button 2020-01-02 22:55:20 -05:00
Qstick 02c35f963e New: Select Multiple Languages on Manual Import 2020-01-02 22:55:20 -05:00
geogolem 4385acef99 Fixed: Reverted a change that caused SQL error on NetImport
https://github.com/Radarr/Radarr/commit/c300af82415c6f71af67a74d1f59ae9b2b30c674#diff-4199a537105793b9a05a6232cb0531bb

that caused SQL error when doing netimport search

update tests

update tests

revert tests then remove

revert tests changes --> comment out the tests
2020-01-02 22:28:11 -05:00
Qstick bf0c716aa5 Disable blank GitHub issues, redirect support to Discord 2020-01-02 20:20:43 -05:00
Qstick 503f7286b9 Fixed: Treat any CF over Cutoff as Cutoff Met 2020-01-02 06:36:11 -05:00
Qstick 0f9c6038ca Fixed: Namespace for CustomFormats Tests 2020-01-02 06:36:11 -05:00
Qstick c6cae162be Fixed: Format Cutoff Selection searches Qualities not Formats 2020-01-02 00:29:57 -05:00
Qstick 5c7d20df54 Fixed: Cutoff Unmet Search fails due to SQL query 2020-01-01 16:46:33 -05:00
Qstick 2e5c7e0073 Fixed: Fallback to Title and Year for PTP if IMDB doesn't exist
Fixes #3509
2019-12-30 22:30:17 -05:00
Qstick 151bf5b49f Fixed: React error displaying a search result for an existing movie
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-12-30 21:48:43 -05:00
Qstick 1b7f52e013 Fixed: Trying to add a movie when root folders hadn't populated
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-12-30 21:43:07 -05:00
Qstick 00921ed839 Add Test Fixture for List Sync 2019-12-30 12:07:37 -05:00
Qstick c300af8241 New: Don't clean moves if IMDB Match 2019-12-30 12:07:37 -05:00
ta264 b1b6a91db8 Fixed: Correctly add parameters to migrations 36 and 154 2019-12-30 16:12:05 +00:00
Qstick dbb24108f1 Don't hardcode branch in backend only check 2019-12-29 02:25:52 -05:00
Qstick 60b9496162 New: Fallback to tmdb search on Rarbg if IMDb is null 2019-12-28 20:26:56 -05:00
Qstick 2eff7b95dd Fixed: Catch NullRef On HDBits request if IMDB empty 2019-12-28 20:26:07 -05:00
ta264 89fec7841c Fixed: Cutoff Status and Filter on MovieIndex 2019-12-28 11:38:34 -05:00
ta264 f83ccb6ca4 Swap to dapper Mk. 2 2019-12-28 11:38:34 -05:00
ta264 47f45a6275 Revert "Revert "Remove Marr.Data""
This reverts commit d2d2020573.
2019-12-28 11:38:34 -05:00
Qstick a39991a9ca New: Don't Run Unit/Integration/Analyze Back if no backend changes 2019-12-28 10:45:24 -05:00
Qstick daee348795 Fixed: Video format exception using split string object 2019-12-28 02:42:56 -05:00
Qstick fa2c4725be New: Show IndexerFlags on Interactive Search 2019-12-28 00:45:59 -05:00
ta264 f02fa629cc Reformat and apply Stylecop rules 2019-12-27 20:40:13 -05:00
ta264 d4fa9b7345 Remove trailing whitespace 2019-12-27 20:40:13 -05:00
ta264 8d27111f7b Remove all unnecessary usings 2019-12-27 20:40:13 -05:00
ta264 c6ae0bb509 Add missing new lines at end of files 2019-12-27 20:40:13 -05:00
ta264 997ff74fb9 Replace tabs with 4 spaces 2019-12-27 20:40:13 -05:00
Qstick 8476e36122 New: Turn MediaInfo Popover into Modal, Add AudioFeatures 2019-12-26 21:52:05 -05:00
Qstick 5e7f0f9d78 Fixed: Tmdb Rename Tokens
Fixes #4004
2019-12-26 21:11:44 -05:00
ta264 10fc0b071f Fixed: NET Core doing copy/delete instead of rename 2019-12-25 16:36:07 +00:00
Qstick 38b6bb3952 Fixed: Legacy API not excluded from coverage 2019-12-24 16:43:47 -05:00
ta264 921771d41b Fix sentry project names 2019-12-23 16:57:20 -05:00
ta264 c53aed4cbf Update sentry DSN to self hosted 2019-12-23 16:03:36 -05:00
ta264 415006badc Slim down mono test suite
Run oldest supported (5.10), current recommended (5.20), latest
stable (6.6) and preview (6.8)
2019-12-22 22:13:02 +00:00
Qstick 089c9657f9 Fixed: Don't throw error IMDB mapped movie not found 2019-12-21 14:18:38 -05:00
ta264 0ca72bf444 Fixed: Null exception on EnhanceMovieInfo
Fixes SENTRY Radarr-3KM
2019-12-19 18:01:12 -05:00
ta264 6057b27dc8 Fixed: Error in epic fail handler if console input redirected
Fixes Sentry Radarr-3AK
2019-12-19 17:58:00 -05:00
EnorMOZ a5d695ec92 Fixed: Language debug log in parser (#3971) 2019-12-18 22:26:39 -05:00
Qstick f7163451e1 New: Keep DB Migration to fix 147
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2019-12-17 21:59:03 -05:00
Qstick d2d2020573 Revert "Remove Marr.Data"
This reverts commit 7b17c3e36c.
2019-12-17 21:59:03 -05:00
Qstick e937d74b11 Revert "Swap to dapper and system.text.json for database backend"
This reverts commit d2065bfa1b.
2019-12-17 21:59:03 -05:00
Qstick d778085ba5 Revert "Fixed: Serialize flag enum as string also"
This reverts commit b0ae4e9a60.
2019-12-17 21:59:03 -05:00
ta264 b0ae4e9a60 Fixed: Serialize flag enum as string also 2019-12-17 18:38:33 +00:00
Qstick fcb31ac51b Fixed: Don't crap on bad searches 2019-12-16 21:44:59 -05:00
Qstick 5d2ac0b86b Fixed: Custom Format Compare not using Id value
Fixes #3954
2019-12-16 20:58:49 -05:00
Qstick d4422e8901 Fixed: Change Refresh to Use StartTime 2019-12-16 20:20:34 -05:00
Qstick da50b49e01 New: Store Task StartTime, Show Duration in UI 2019-12-16 20:20:34 -05:00
Qstick edcc0ba7ce New: Only Refresh on TMDB Refersh 2019-12-16 20:20:34 -05:00
ta264 d2065bfa1b Swap to dapper and system.text.json for database backend 2019-12-16 20:22:58 +00:00
ta264 7b17c3e36c Remove Marr.Data 2019-12-16 20:22:58 +00:00
Qstick acac33c217 New: Change Automation timeout to 3min (#3951)
* New: Change Automation timeout to 3min

* Fixed: Workaround netcore/selenium HTTP bug

* try without timespan override

* Update AutomationTest.cs
2019-12-15 18:55:14 -05:00
Qstick 5d7804478b New: Trakt Auth per List 2019-12-15 00:39:16 -05:00
Qstick 5e52a12287 Fixed: Add back Indexer Options 2019-12-14 21:40:28 -05:00
Qstick a23e57d9f7 Fixed: Typo in RescanMovie log statement 2019-12-14 21:15:04 -05:00
EnorMOZ 2f902d412c Fixed: History fails due to languages property (#3944)
* Fix languages property

* fix language
2019-12-14 20:33:09 -05:00
Qstick e9ed08c8be New: Shrink Test Images (Remove Mono 5.12, 5.14, 5.16) 2019-12-10 21:04:40 -05:00
ta264 a931ac1022 Fixed: Webpack worker loading with UrlBase 2019-12-10 21:00:40 -05:00
Qstick 24f93ec3ef Fixed: Regenerate Yarn.lock for update (serialize-javascript@^2.1.1) 2019-12-09 23:09:01 -05:00
DavidSpek a9f95109f6 New: Add EAC3 Atmos to Mediainfo Formatting (#3900)
* Added a EAC3 Atmos option

Added a second if statement under "if (audioFormat.ContainsIgnoreCase("E-AC-3"))" to look for "JOC" in splitAdditionalFeatures to be able to identify EAC3 tracks that contain atmos. I used the audioFormat "MLP FBA" with "TrueHD Atmos" as a reference.
2019-12-09 22:52:46 -05:00
Qstick 803b44280f Fixed: Sonar failure on external forks 2019-12-09 22:48:24 -05:00
ta264 318e05ddba Fixed: Poster/Overview checkbox in Movie Editor mode 2019-12-08 17:39:20 +00:00
Qstick 000a4ec822 Fixed: Revert react-virtualized update due to issues with JumpBar 2019-12-08 01:06:06 -05:00
telans ed46ff3445 Fixed: Client strings reference episodes (#3929) 2019-12-08 00:34:59 -05:00
Qstick 7a95fb55bd Fixed: Versioned Backups Not Recognized by BackupFileRegex 2019-12-08 00:31:00 -05:00
Qstick eaf7999d70 Update DEVELOPMENT.md 2019-12-07 18:33:22 -05:00
Qstick 2633c82a01 New: Hookup SonarCloud 2019-12-07 13:27:18 -05:00
Qstick 12245d0956 Fixed: Paging on Day and Week Cal View 2019-12-07 12:19:01 -05:00
Qstick 6d87b5dfec Fixed: Show Physical Releases on Calendar 2019-12-07 02:11:29 -05:00
Qstick bbd065940e Fixed: Calendar Ledgend not being shown 2019-12-07 02:04:38 -05:00
Qstick 61450a5ca4 New: Update Radarr.ico 2019-12-07 01:33:20 -05:00
Qstick 05f0156661 New: TMDB Link on Search Results 2019-12-07 01:25:03 -05:00
Qstick 14c943bd48 Fixed: ImportDecsionMaker Fixture Failures 2019-12-07 01:17:01 -05:00
Qstick a03f6605ce Fixed: History Rename and Delete Filter Using Wrong Id 2019-12-06 23:26:58 -05:00
Qstick a7e066fc11 Fixed: Logging file release group for repack
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-12-06 23:22:28 -05:00
Qstick 7cd270d534 Fixed: Rename ProcessProviderTests to Fixture 2019-12-06 23:21:07 -05:00
Qstick b14157797e New: Show relative file name when selecting movie in Manual Import 2019-12-06 23:17:24 -05:00
Taloth Saldono 0cff6c1fdf Tiny fix in test, left-over from my on-windows test. 2019-12-06 23:08:41 -05:00
Qstick ca61ca5c40 Fixed: All Files Filter on Manual Import in MovieDetails 2019-12-06 23:06:58 -05:00
Qstick aa7ebe4168 New: UI Package Updates 2019-12-06 22:42:15 -05:00
Taloth Saldono c34dc0be13 Cleanse getnzb url 2019-12-06 22:28:40 -05:00
Taloth Saldono d5c2308587 Fixed: File imports on cloud drives slow due to transaction logic 2019-12-06 22:22:36 -05:00
Qstick 7f0581018b New: Enforce Indent after Logical for UI 2019-12-06 21:38:58 -05:00
ta264 6235225f7d New: Bump to .NET Core 3.1 2019-12-06 21:12:47 -05:00
ta264 ea9f622db2 Fixed: UI Search
Inline the worker until we come up with a better solution
2019-12-05 22:06:03 -05:00
Qstick 64d949fc96 Fixed: Ensure Fuse Worker is Loaded if Refresh on another page 2019-12-04 22:22:38 -05:00
Qstick 386315ad27 Fixed: Return null if no height on discover/list grid 2019-12-04 22:06:01 -05:00
Qstick 2b518ded37 New: Added version number to backup filename
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2019-12-04 21:30:11 -05:00
Qstick ecd4cc7b72 Fixed: Handle qBit ForcedDL State
Fixes #3870
2019-12-04 21:16:48 -05:00
Jef LeCompte 86a53141ad Fixed: Handle qBittorrent "moving" state 2019-12-04 21:09:29 -05:00
Qstick 0aa8ac5d39 New: Bump Version to V3 to please the masses 2019-12-04 19:28:34 -05:00
Qstick 29011cac5e New: Combine Solutions via Configurations 2019-12-04 19:28:34 -05:00
geogolem 9f36665d6d Fixed: Add back minimumAvailability for netimport lists (#3916) 2019-12-03 20:56:49 -05:00
ta264 abe7a85a39 Fix: Aphrodite UI enhancements
* New: Display UI before movies have loaded

* Revised webpack bundling

* New: Option for production build with profiling

* Fixed: Faster hasDifferentItems and specialized OrOrder version

* Fixed: Faster movie selector

* Fixed: Speed up release processing, add indices (migration 161)

* Fixed: Use a worker for UI fuzzy search

* Fixed: Don't loop over all movies if we know none selected

* Fixed: Strip UrlBase from UI events before sending to sentry

Should mean that source maps are picked up correctly.

* Better selection of jump bar items

Show first, last and most common items

* Fixed: Don't repeatedly re-render cells

* Rework Movie Index and virtualTable

* Corresponding improvements for AddListMovie and ImportMovie
2019-11-27 09:19:35 -05:00
Qstick 95e5e3132b Fixed: Add filterPredicates for InCinemas and PhysicalRelease
Fixes #3885
2019-11-26 23:02:04 -05:00
Jayden 99bf3cf3ba Fix: Typo in UI dropdown for time format (#3886)
The time format was incorrectly showing the D/M format as DD/MM (25/03 instead of 25/3).
2019-11-24 00:32:16 -05:00
FuNK3Y fedf2326f0 New: Add TMDB/IMDB url in Kodi metadata 2019-11-17 11:14:31 -05:00
hotio e8b658646a Update README.md
- updated downloads table
- fixed a few markdown warnings
2019-11-16 07:28:00 -05:00
geogolem 1875391a3a Fix: Movie file count in footer and update movie legend colors 2019-11-15 19:23:33 -05:00
geogolem 15b63778e5 Fix: Net import optimization 2019-11-15 19:20:13 -05:00
geogolem a06b044342 Fix: use isAvailable to determine if a movie is available. 2019-11-15 19:17:19 -05:00
devbrian c5d7cf4eeb Fixed: TVDB references in RefreshMovieService.cs 2019-11-12 10:38:59 -05:00
aPinat 8bbec88b6f Fixed: SSL certificate import 2019-11-07 11:17:03 -05:00
Devin Buhl fc5ab2fd59 New: URL Base support for NZBVortex, Hadouken, qBittorrent and uTorrent 2019-11-07 11:08:32 -05:00
Devin Buhl 003ef8747c Fixed: Updated some strings to Radarr in Download Clients 2019-11-07 10:55:27 -05:00
Devin Buhl 4e07e3bd68 New: Option to send notification when a Health Check warning occurs 2019-11-04 19:50:11 -06:00
Devin Buhl e0b6bde525 Fixed: Default id for MenuContent 2019-11-04 14:55:06 -05:00
Devin Buhl ee91cb99ce Fixed: Actually run Recycle Bin cleanup 2019-11-04 14:52:08 -05:00
Devin Buhl 7440ca9ca8 Fixed: Remove parens around year, move path below movie summary 2019-11-03 14:01:24 -05:00
Devin Buhl 238f99c8ca Fixed: Ignore gecko, vscode and Jetbrains files/folders 2019-11-03 12:00:13 -05:00
Devin Buhl 604f45bea4 Fixed: Set id in MenuContent to not required 2019-11-03 11:26:24 -05:00
Devin Buhl d1a066f601 Fixed: Set year to number instead of string 2019-11-03 11:17:05 -05:00
Devin Buhl 99509e61d3 Fixed: Add Path on Movie Details Page 2019-11-03 11:00:15 -05:00
Devin Buhl 1fe3c81b8d Fixed: Add Year next to Title on Movie Details Page 2019-11-03 10:52:52 -05:00
Devin Buhl 5d7166662c Fixed: Remove extra ; in QualityIndex 2019-11-03 10:00:30 -05:00
Qstick 0d2660ee6c Fixed: Issues with Migration 159, Bad Index, Duplicated WEBDLs 2019-11-02 22:54:44 -04:00
Qstick f489b6b506 Fixed: Disable some flaky tests on MacOS
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2019-11-02 17:59:36 -04:00
Qstick c80a492d76 Fixed: Queue Barfs on Null RemoteMovie 2019-11-02 14:11:52 -04:00
Qstick bbadf3c7e6 Fixed: Quality Parser and Augmenter Picks Wrong Quality 2019-11-02 14:11:52 -04:00
Qstick 5c3fda48f7 New: Custom Formats on Movie Details File Tab 2019-11-01 23:16:52 -04:00
Qstick 2329ec25b9 Fixed: Move Search Tab Filter Menu Up to TabPanel 2019-11-01 23:08:07 -04:00
Qstick c08ae534c5 Fixed: List Tags and Prevent Delete of Tag if on List 2019-11-01 21:31:47 -04:00
Qstick 816905a506 New: Add Availability Delay to Indexer Options UI 2019-11-01 21:01:02 -04:00
Qstick 3598d6623a Fixed: Update Icon for Windows Tray 2019-11-01 20:56:09 -04:00
Qstick ae6a95bad8 Fixed: Sort by Monitor-Status Column 2019-10-28 22:58:52 -04:00
Qstick 634697af2d New: Add digits to Deluge's category validator
Co-Authored-By: rbraunschweig <rbraunschweig@users.noreply.github.com>
2019-10-28 22:40:36 -04:00
Qstick d320f8a713 Updated: XBMC notification strings to Kodi
Co-Authored-By: sirloinofbeef <sirloinofbeef@users.noreply.github.com>
2019-10-28 22:38:03 -04:00
Qstick b1e3638b34 Fixed: Legend Colors Backward for Missing
Fixes #3846
2019-10-28 22:14:45 -04:00
Qstick 947b9e75db Fixed: Show Cutoff Status on History Items 2019-10-28 22:07:19 -04:00
Qstick 8ad995e56f Fixed: Set Default Sort Key for Blacklist Endpoint 2019-10-28 21:40:52 -04:00
Devin Buhl 96fe74760f Fixed: Instance where Sonarr was used in logging message 2019-10-26 19:58:30 +00:00
Qstick 83eeb747dc Fixed: Status Filter using Series Filter Options 2019-10-26 12:56:00 -04:00
Qstick bba5b425b3 Fixed: Delete MovieFile from MovieDetails gives 405 2019-10-26 12:29:07 -04:00
Devin Buhl f6eb6666c2 Fixed: Trakt link for movies instead of series 2019-10-26 15:57:09 +00:00
Qstick ee7e507cde Fixed: History should reload on MovieDetail Movie Change 2019-10-24 22:51:30 -04:00
Qstick 162980ccaf Fixed: Remove Series Related Cal Setting 2019-10-24 22:33:47 -04:00
Qstick 8e54809c79 Fixed: Audio and Video Columns Switched for MovieFileTable 2019-10-24 19:28:54 -04:00
Qstick abc6e28401 Fixed: Poster placeholder to new logo 2019-10-22 23:48:21 -04:00
Qstick 567824ebec New: Show Custom Formats on Queue Page 2019-10-22 21:07:11 -04:00
Qstick e827a965ad New: Show Custom Formats on Blacklist Page 2019-10-22 21:02:50 -04:00
Qstick 215d5069a9 New: Show Custom Formats on History Page 2019-10-22 21:00:55 -04:00
Qstick fb4aa58a75 Fixed: Add Delete Button to Custom Formats 2019-10-22 20:51:57 -04:00
Qstick dd00c9b53e Fixed: Remove Plist Fix to prevent Linux Core update failures 2019-10-21 19:34:26 -04:00
Qstick 3c380954ec Fixed: Missing Movie Search from Index Null Ref 2019-10-21 09:43:39 -04:00
Qstick 136432d098 Fixed: No newline on MediaInfoPopover 2019-10-19 19:07:41 -04:00
Qstick 1920bd53b6 New: Mediainfo Popover for Movie Files 2019-10-18 22:57:11 -04:00
Qstick ac8a7a9254 Fixed: Match Filter Indicator to Theme Color 2019-10-18 21:34:44 -04:00
Qstick 0039c1c393 New: Show CustomFormats on Interactive Search Results 2019-10-18 21:11:55 -04:00
Qstick 02efc655f9 Fixed: Remove Not-So-Great Parser Case 2019-10-17 23:37:34 -04:00
Qstick f43210d3d0 Fixed: Default RequireFlags Value (AKA Give torrent guys back their indexers) 2019-10-17 23:06:01 -04:00
Qstick 67dffcdc69 Fixed: 4K and Remux Parser Tweaks 2019-10-16 22:19:39 -04:00
ta264 36ab3ecf71 Fixed: Removed unused references to System.Drawing 2019-10-16 15:25:35 -04:00
ta264 e1b0dd00bb Fixed: Don't publish self contained tests 2019-10-16 15:25:35 -04:00
ta264 37a39d1624 Fixed: Make ProcessProvider tests more reliable 2019-10-16 15:25:35 -04:00
ta264 9fe978319e Fixed: Don't load Radarr.Core.dll as part of Radarr.Update 2019-10-16 15:25:35 -04:00
ta264 2a15113a74 Fixed: MultiLanguages definition 2019-10-15 20:22:50 -04:00
ta264 b5b43b8b3f Changed: Align GetValueOrDefault extension with netcore3.0 version
- netcore3.0 implements the extenion on IReadOnlyDictionary.
 - Dictionary implements both IReadonlyDictionary and IDictionary and
   so defining the extenion on both interfaces creates an ambiguous
   reference
 - IDictionary doesn't inherit from IReadOnlyDictionary

Either we have to add 'using NzbDrone.Common.Extenions;'
separately to resolve the ambiguity or we have to standardaize on only
having the extension on IReadOnlyDictionary.
2019-10-15 20:22:50 -04:00
ta264 057829c3b0 New: Multi target net framework 4.6.2 and net core 3.0 2019-10-15 20:22:50 -04:00
ta264 d0f13e16d5 Fixed: All compiler warnings 2019-10-15 20:22:50 -04:00
ta264 abde842bf0 Fixed: Remove obsolete HttpProvider 2019-10-15 20:22:50 -04:00
ta264 cf33e40e70 Fixed: Remove obsolete Plex HomeTheater/Client notifcations 2019-10-15 20:22:50 -04:00
ta264 1b34780b7e Fixed: Remove obsolete XBMC HTTP notification API 2019-10-15 20:22:50 -04:00
ta264 53ffc9867c New: Update Unity 2019-10-15 20:22:50 -04:00
ta264 8b9d64b15a Changed: Remove growl and prowl 2019-10-15 20:22:50 -04:00
ta264 350dfeabba New: Make Twitter NetStandard compatible 2019-10-15 20:22:50 -04:00
ta264 b0cbd6c6bf Net standard XMLRPC 2019-10-15 20:22:50 -04:00
ta264 70c1722ef2 New: Upgrade Ical.Net to 4.1.11 2019-10-15 20:22:50 -04:00
Qstick 225430162b Fixed: ImageResizer Tweaks
Co-Authored-By: taloth <taloth@users.noreply.github.com>
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2019-10-15 20:22:50 -04:00
ta264 bc0cc2bfa9 New: Use ImageSharp for resizing 2019-10-15 20:22:50 -04:00
ta264 c85d3119f9 Convert Interop.NetFwTypeLib to AnyCPU 2019-10-15 20:22:50 -04:00
ta264 5c07b39b04 Fixed: Convert MonoOnly to PosixOnly 2019-10-15 20:22:50 -04:00
ta264 2b39865251 Update FluentMigrator to v4 2019-10-15 20:22:50 -04:00
ta264 ca9d9f093c Fixed: Remove bad binding redirects in csproj 2019-10-15 20:22:50 -04:00
ta264 0b7c00bfe6 Improve pipeline and build 2019-10-15 20:22:50 -04:00
ta264 6ef388e258 Run integration tests for various mono versions 2019-10-15 20:22:50 -04:00
ta264 fb5b9c445b New: Switch to ASPNetCore Kestrel and SignalR 2019-10-15 20:22:50 -04:00
ta264 fdbed91a4e New: Use sqlite package from Lidarr 2019-10-15 20:22:50 -04:00
ta264 0b0d0a7353 New: Use dotnet tooling, produce 64bit build on windows 2019-10-15 20:22:50 -04:00
ta264 304382f406 Fixed: Integration tests on Mono 5.12 and 5.14
Mono 5.12 and 5.14 has a bug that means RestSharp can't handle non-200
responses.  Fix status api call so tests start and disable the tests
that use non-200 responses on these mono versions
2019-10-15 20:22:50 -04:00
Taloth Saldono 97de1b4622 Moved Platform version determination to static 2019-10-15 20:22:50 -04:00
ta264 24f1b5c0a7 Fixed: CommandExecutorFixture flakiness 2019-10-15 20:22:50 -04:00
ta264 532190ef4b Fix integration tests on linux with debug build 2019-10-15 20:22:50 -04:00
ta264 779809b78b Fixed: Normalize all line endings to LF in repo 2019-10-15 20:22:50 -04:00
ta264 9f07b65877 Fix .gitattributes 2019-10-15 20:22:50 -04:00
Qstick 1514613f61 New: Added MinAvailability Option to UI 2019-10-10 23:20:48 -04:00
Qstick 5eb7fe958f Fixed: Calendar Missing Search Not Functional, Other Tweaks 2019-10-10 21:45:27 -04:00
Qstick 24fa77e7b2 Fixed: Queue Selector Looking for Episodes not Movies 2019-10-10 21:45:00 -04:00
Qstick 9ae411802d Fixed: Hide Profile Delete if Used by List 2019-10-10 21:42:56 -04:00
Qstick 1a3e0a3163 New: Indicator when Filter is applied 2019-10-09 22:48:43 -04:00
Qstick 6e601ede37 Fixed: Custom Format Edit Modal Huge Size 2019-10-09 22:20:43 -04:00
Qstick 9bbd314452 Fixed: PageHeader Action Hover Color 2019-10-09 22:11:39 -04:00
Qstick b40c63dcfe Fixed: Logo Changes 2019-10-09 22:11:08 -04:00
Qstick 21ed073f29 New: Add List and Discovery Pages (#3803) 2019-10-05 23:21:55 -04:00
Qstick 5f396a53c3 New: Parse VFI as French 2019-10-05 18:45:35 -04:00
Qstick a368cbd265 Fixed: Hookup BulkMovieMovieCommand 2019-10-05 18:27:22 -04:00
Qstick a2cad761b9 Fixed: Status Cell Working 2019-10-03 21:42:22 -04:00
Qstick 2af273a4a1 Fixed: Update Test Fails Due to Throw Elimination 2019-10-03 20:48:34 -04:00
Qstick 01f2f754ea New: First Pass Movie Index Status Cell 2019-10-01 22:16:16 -04:00
Qstick 1b6e6f80b6 Fixed: Index Table Status shows Correct Icons 2019-10-01 21:12:15 -04:00
Qstick de7d6c68b8 Fixed: Log, Don't throw when trying update in docker 2019-09-30 20:46:47 -04:00
Qstick 31c73fe448 Changed: Add ChannelPositionText to SentryWarn 2019-09-30 20:46:22 -04:00
Qstick 526e091d22 Fixed: Provider HealthChecks persist after add until next scheduled check. 2019-09-30 19:44:23 -04:00
Taloth Saldono 40736336db Fixed: Root Folder display when free diskspace cannot be determined (FreeBSD)
closes #3275
2019-09-30 19:33:57 -04:00
Qstick f2200f793d Fixed: Quality Finder Tweaks 2019-09-29 16:27:54 -04:00
Qstick 7fd391259c Fixed: Movie Path UI Warning, Duplicate Import Fixes 2019-09-29 16:27:30 -04:00
Qstick b225435164 Fixed: Language Column not shown in Queue 2019-09-29 15:11:08 -04:00
Leonardo Galli be3152e630 Fixed: When refreshing info about a movie, the alt titles should now correctly be deleted / updated, even from TMDB. (#3603)
* Fixed: When refreshing info about a movie, the alt titles should now correctly be deleted / updated, even from TMDB.
Fixes #3542

* Fixed: Small things fixup.
2019-09-28 22:59:51 -04:00
Qstick bd374825f1 Fixed: Logging Statements and Tests 2019-09-28 22:39:09 -04:00
Qstick f395117885 Fixed: More Sentry Filtering 2019-09-26 21:41:52 -04:00
Qstick ae9c2dd830 New: Log Sentry Warn if AudioChannelPositions Error 2019-09-26 21:31:46 -04:00
Qstick 923db77751 Fixed: Show Year in Manual Import List 2019-09-23 22:25:49 -04:00
Qstick 78cac9fcfa Fixed: Use Modifier in Quality Aggregation 2019-09-23 22:10:51 -04:00
Qstick 5fe8884471 Fixed: Prevent NRE in MovieSearchService
Fixes #3728
2019-09-21 16:10:09 -04:00
Qstick fb8143bb49 Fixed: MigrationVersion not passed to UI 2019-09-21 15:42:32 -04:00
Qstick 90e58e5a22 Fixed: Add List Exclusion from Movie Editor
Fixes #3775
2019-09-21 15:40:26 -04:00
Qstick 254561aeb1 Fixed: Register Null Target for Sentry logs in Tests 2019-09-21 14:54:13 -04:00
Taloth Saldono 9a25878104 And a bunch of video codecs. Also fixed the dual-video channel issue. 2019-09-21 14:39:23 -04:00
Taloth Saldono 54cfabec5c New: Additional Atmos detection in MediaInfo
ref Radarr/Radarr#3712
2019-09-21 14:38:27 -04:00
Qstick 7dc629a647 Fixed: Rework Multiple Logging Statements 2019-09-21 14:35:07 -04:00
Qstick cbb2802383 Fixed: TrackedDownload Logging Statement 2019-09-14 18:49:32 -04:00
Qstick fd2399d589 Fixed: Update Movie Libraries in Plex not Series Libraries 2019-09-10 19:22:13 -04:00
Qstick 33d012cfc0 Fixed: Quality Definitions Don't Allow Save 2019-09-08 23:27:33 -04:00
Qstick 066bf1220f Fixed: Flaky IndexerStatusTimes Test 2019-09-08 22:29:57 -04:00
Qstick fd87be6d1e New: FutureDownloadClient Housekeeper Tests 2019-09-08 22:17:56 -04:00
Qstick fe591816bb Fixed: Remove TitleSlugRoute 2019-09-08 22:00:15 -04:00
ta264 98d987869c Changed: Point at lidarr hosted update server 2019-09-08 21:42:18 -04:00
ta264 e10e5802f4 Fixed: Don't delete folder if indeterminate sample detected 2019-09-08 21:42:03 -04:00
ta264 fd5113744f Fixed: Various tests 2019-09-08 21:41:24 -04:00
Taloth Saldono a22c946276 Fixed third-party clients calling api without Accept header 2019-09-08 20:53:37 -04:00
Taloth Saldono a862337ead Removed obsolete code. 2019-09-08 20:53:37 -04:00
ta264 79cf3079c3 Updated Nancy to 2.0 2019-09-08 20:53:37 -04:00
Taloth Saldono 6c4e3b1fd5 New: Added Auth-* log entries for fail2ban purposes 2019-09-08 20:53:37 -04:00
ta264 1368c7c6e3 Emacs gitignore 2019-09-08 20:53:37 -04:00
Qstick cb158028df Fixed: Old API use with Ombi causing NREs 2019-09-07 21:58:45 -04:00
Qstick 8f72bd5e69 Fixed: Sentry SourceMaps for Aphrodite 2019-09-04 00:28:53 -04:00
Qstick 793b723942 New: Update SQLite to 3.28.0 and System.Data.SQLite to 1.0.111.0 2019-09-03 23:21:15 -04:00
Qstick 7f221c7834 Fixed: Automation/Integration/Unit Tests 2019-09-03 23:21:15 -04:00
Qstick 944f420270 New: Build on Azure Pipeline
Co-Authored-By: ta264 <ta264@users.noreply.github.com>
2019-09-03 23:21:15 -04:00
Qstick b89c7b8675 New: Convert to New CSProj Format
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2019-09-03 23:21:15 -04:00
Qstick a3525252b7 Fixed: Log statement in movie rename service 2019-09-02 11:55:02 -04:00
Qstick b42004b32c Fixed: Another Log Statement duplicating on Sentry 2019-09-01 10:52:01 -04:00
Qstick b2268c7452 New: Get Custom Formats Working in Aphrodite 2019-08-31 15:54:33 -04:00
Qstick 86dde88fe6 Fixed: Re-DSN after log fix 2019-08-31 14:36:34 -04:00
Qstick ab7083d263 Fixed: Spec Logging Statement Causing Sentry Duplicates 2019-08-30 23:37:05 -04:00
Qstick 9ad17a04ff Fixed: Null Ref in UpgradeSpecification 2019-08-30 23:29:04 -04:00
Qstick 23670bca12 New: Upstream Updates
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-08-30 22:50:03 -04:00
Qstick bfc467dd96 New: Use MediaInfo on File Parsing 2019-08-25 15:08:25 -04:00
Qstick ada9b944dc Fixed: QueueSpecificationFixture Test 2019-08-06 22:44:36 -04:00
Qstick f6b4a463b1 New: Remove Unused TinyTwitter Nuget Package 2019-08-06 22:27:47 -04:00
Qstick d53320779c New: Update NLog to 4.6.6 2019-08-06 22:25:21 -04:00
Qstick 71c398f0fc New: Enable Sentry Analytics (#3669) 2019-08-06 22:20:47 -04:00
Qstick 64ec751938 Fixed: Theme color for poster control background 2019-08-05 22:35:31 -04:00
Qstick 08b642575f Fixed: Rejoin MovieFiles to Movies to fix Cutoff short term 2019-08-05 22:13:03 -04:00
Qstick fee8244a74 Fixed: MovieDetails Files Table not reflecting cutoff status 2019-08-05 21:01:03 -04:00
Qstick 328477a1c6 New: Required/Ignored restrictions now support /pattern/ regular expressions
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2019-08-05 20:47:46 -04:00
Qstick a771871cf8 Fixed: Protect Queue Specification from NRE
Fixes #3665
2019-08-05 20:40:05 -04:00
Qstick 1cde85bba8 New: Update Frontend Packages 2019-07-27 01:33:33 -04:00
Qstick 6288936fe2 Fixed: Calendar Items Show on Correct Day 2019-07-27 00:56:07 -04:00
Qstick 582402d45e Fixed: MediaInfo Improvements, Tests 2019-07-27 00:33:04 -04:00
Qstick 5657a4df9c Revert "Fixed: Additional FileNameBuilder Tests, {MediaInfo SubtitleLanguagesAll} Token"
This reverts commit 865b587bdd.
2019-07-26 23:54:49 -04:00
Taloth Saldono 865b587bdd Fixed: Additional FileNameBuilder Tests, {MediaInfo SubtitleLanguagesAll} Token 2019-07-26 23:22:10 -04:00
Taloth Saldono b179be78db Fixed: Heavy qbit api load when CDH Remove is disabled
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2019-07-26 22:44:22 -04:00
Qstick 5f7c7ee809 Fixed: Include HDR is naming examples
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:41:32 -04:00
Qstick fb604483ce Changed: Default to System Tray for Windows installer
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:38:00 -04:00
Qstick 6510e2c898 Fixed: Parsing BD release group as Bluray quality
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:34:07 -04:00
Qstick e3040ad2e1 Fixed: stylelint errors 2019-07-26 22:30:17 -04:00
Qstick 032b1e7a03 New: Add warning to remove from queue dialog
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:27:55 -04:00
Mark McDowall 7189719e6f Darker border for calendar 2019-07-26 22:25:01 -04:00
Mark McDowall 6a68e5ca92 New: Wider and taller scroll bar for the click to scrollers out there 2019-07-26 22:22:43 -04:00
Mark McDowall a3e312b368 Fixed: Custom Filter improvements 2019-07-26 22:21:47 -04:00
Taloth Saldono 895abe8c3b Fixed: Workaround for mono 5.16+ bug preventing the closure of sockets on timeouts (Jackett connections)
ref #2802
2019-07-26 22:16:48 -04:00
Taloth Saldono ae47ee817f Fixed: Executing powershell and python scripts directly in Connect->Custom Scripts 2019-07-26 22:13:02 -04:00
Qstick 6c84518b40 New: Improve logging when checking if release is an upgrade
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:09:46 -04:00
Qstick 3ba72fd33b New: Treat WEBMux as WebRip
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 22:05:04 -04:00
Qstick c4e01c2020 New: Improve help text for extra file importing
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 21:59:27 -04:00
Mark McDowall 80715f5e58 Fixed: oAuth actions in UI
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
2019-07-26 21:57:58 -04:00
Mark McDowall f868e8b964 Fixed: Add Tests for FirstCharTo 2019-07-26 21:49:49 -04:00
Qstick 4af18f7d4a Fixed: Some History Events don't Save Due to Languages 2019-07-25 22:52:33 -04:00
Qstick 2758adcc8c Fixed: Queue/History take two clicks to load, We don't wait on Episode Fetch 2019-07-25 22:52:03 -04:00
Qstick b6c1c81152 New: Add Release Date and Cinema Date to Custom Script 2019-07-25 22:40:52 -04:00
Qstick d263ff9a6b Fixed: Wanted/Cutoff Search from Index Page 2019-07-25 22:34:24 -04:00
Qstick 6705b59b23 Fixed: Renaming Files Command Doesn't Resolve 2019-07-25 21:11:12 -04:00
Qstick fbc2566f70 New: Smart Filter Options for Studios in MovieIndex 2019-07-16 22:58:01 -04:00
Qstick ed24cd5b52 Fixed: Movie Editor RootFolders, SelectedState persisting editor toggle 2019-07-16 22:23:10 -04:00
Qstick a6eb89e24b Fixed: Manual Search on Works with Auto Search is Enabled 2019-07-15 22:41:43 -04:00
Qstick 242d530bb4 Fixed: Unit Test Fixes 2019-07-14 21:18:03 -04:00
Qstick 3bf5476922 Fixed: Correctly handle Repacks, restrict to same group 2019-07-12 23:56:58 -04:00
Qstick 7698ae00dd Fixed: Misc UI/Test Fixes 2019-07-12 23:15:13 -04:00
Qstick a20222fbef New: Movie Editor in Movie Index (#3606)
* Fixed: Movie Editor in Movie Index

* Fixed: CSS Style Issues

* Fixed: Ensure only items shown are selected

* Fixed: Cleanup and Rename from Editor
2019-07-12 20:40:37 -04:00
Qstick b8f7ca0749 Fixed: DiskScanService Updates 2019-07-09 23:14:53 -04:00
Qstick 612d948eba Fixed: Some Tests and Added New Ones 2019-07-09 22:05:32 -04:00
Qstick a3f72bd4a0 Fixed: Ambiguous Id in Movies SQL call 2019-07-06 17:36:58 -04:00
Qstick 2d15b8b78a Fixed: Remove redundant checks from list sync 2019-07-06 17:36:58 -04:00
devbrian 12fba024f0 Fixed: Movie Details Tab (#3564)
* History Added

* History Cleanup

* History Mark Failed Fix

* History Lint Fix

* Search Tab Initial

* Interactive Search Cleanup

* Files Tab + Small Backend change to MovieFile api

* Reverse Movie History Items

* Grabbed files are not grabbable again.

* Partial movie title outline + Search not updating fix

* Lint Fix + InteractiveSearch refactor

* Rename movieLanguage.js to MovieLanguage.js

* Fixes for qstick's comments

* Rename language selector to allow for const languages

* Qstick comment changes.

* Activity Tabs - Language Column fixed

* Movie Details - MoveStatusLabel fixed

* Spaces + Lower Case added

* fixed DownloadAllowed

* Added padding to history and file tables

* Fix class =>  className

* Updated search to not refresh unless switching movie

* lint fix

* File Tab Converted to Inline Editting

* FIles tab fix + Alt Titles tab implemented

* lint fix

* Cleanup via qstick request
2019-07-06 09:47:11 -04:00
Qstick 06b1c03053 Fixed: List Exclusions, List Processing Tweaks 2019-07-05 22:26:16 -04:00
Qstick ed0e69de53 Fixed: Return all movie categories for RARBG
Co-Authored-By: FuNK3Y <funk3y@users.noreply.github.com>
2019-07-05 16:01:07 -04:00
Qstick d86c811543 Fixed: Alt Titles on Main Load, Alt Titles API updates 2019-07-02 21:45:55 -04:00
Qstick ac59b7060e New: Store Genre in DB for use in UI 2019-06-30 22:45:31 -04:00
Qstick b1d69e3949 Fixed: Queue Error on Unknown Items 2019-06-30 22:11:15 -04:00
Qstick 91ab518dfb Fixed: Backend Updates from Sonarr
Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>
Co-Authored-By: taloth <taloth@users.noreply.github.com>
2019-06-30 21:50:01 -04:00
Qstick d178dce0d3 Fixed: Backend/Frontend Cleanup 2019-06-30 00:58:54 -04:00
Qstick 286f73f38d Fixed: Refresh Indicator always spinning after refresh 2019-06-29 22:26:02 -04:00
Qstick 7760248e6b Fixed: Adjust how often info is refreshed for old movies 2019-06-29 21:44:43 -04:00
Qstick 4ddadd9a0d Fixed: Delete and Restore of Backups from UI 2019-06-29 21:38:11 -04:00
Qstick 722a996ad3 Fixed: Import not working due to Language constraint 2019-06-18 23:02:26 -04:00
Qstick 9350f6a04c Fixed: Changes to Profiles, Languages, Manual Import 2019-06-18 22:50:17 -04:00
Qstick c76364a891 Fixed: Update Unit Tests for Indexers/Clients 2019-06-16 22:18:32 -04:00
Qstick 8a9e2dc90d New: Loads of Backend Updates to Clients and Indexers 2019-06-16 16:04:38 -04:00
Qstick c48838e5b6 Fixed: Linting errors and Appveyor not triggering on frontend changes 2019-06-12 20:34:30 -04:00
Qstick 16ff1176f7 Fixed: Quality Groups and Profiles 2019-06-11 22:07:34 -04:00
Qstick 6275737ced New: Many UI Updates and Performance Tweaks 2019-06-11 22:07:34 -04:00
Taloth Saldono b24a40797f Fixed: SignalR requiring a home directory to function properly. 2019-06-11 22:07:34 -04:00
Qstick 5f2af81dda New: Bump gulp-livereload to avoid event-stream issue 2019-06-11 22:07:34 -04:00
Leonardo Galli b35d0b3f6a Added: Development Description. 2019-06-11 22:07:34 -04:00
Qstick 9ba2c8e53d New: Bump UglifyJS to 1.3.0 to fix production build 2019-06-11 22:07:34 -04:00
Qstick 4d3bfe3cf2 New: Remove old UI 2019-06-11 22:07:33 -04:00
Qstick ac0d1c92c3 New: Tooling changes for UI 2019-06-11 22:06:43 -04:00
Qstick 8430cb40ab New: Project Aphrodite 2019-06-11 22:06:43 -04:00
Qstick 65efa15551 New: Backend changes for new UI 2019-06-11 22:06:19 -04:00
Qstick e9eebd3ce6 New: Update SignalR, Nancy, Owin. Enable Websockets 2019-06-11 22:06:19 -04:00
Qstick aef89160e2 Added: Platform Detection Improvements 2019-06-11 22:06:19 -04:00
Qstick ea5ad24944 New: Re-target to .net 4.6.1 2019-06-11 22:06:19 -04:00
5039 changed files with 167038 additions and 212620 deletions
-14
View File
@@ -1,14 +0,0 @@
FROM mono:5.18
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y git ssh tar gzip ca-certificates wget zip wine wine32 wine64 libwine libwine:i386
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -E -
RUN apt-get install -y nodejs
RUN wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-7_all.deb && dpkg -i repo-mediaarea_1.0-7_all.deb && apt-get update
RUN apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
RUN npm i -g npm
RUN apt-get install -y python3-pip && pip3 install gitchangelog pystache
RUN curl -O https://dl.google.com/go/go1.10.2.linux-amd64.tar.gz && tar xvf go*.tar.gz && chown -R root:root ./go && mv go /usr/local
ENV GOPATH=$HOME/work
ENV PATH="${PATH}:/usr/local/go/bin:$GOPATH/bin"
RUN go get github.com/aktau/github-release
RUN npm install -g yarn
-169
View File
@@ -1,169 +0,0 @@
version: 2
defaults: &defaults
docker:
- image: gallileo/radarr-cci-primary:5.8.9
environment:
BUILD_VERSION: 0.2.0
jobs:
build:
<<: *defaults
steps:
- restore_cache:
keys:
- source-v1-{{ .Branch }}-{{ .Revision }}
- source-v1-{{ .Branch }}-
- source-v1-
- checkout
- run: git submodule update --init --recursive
- save_cache:
key: source-v1-{{ .Branch }}-{{ .Revision }}
paths:
- ".git"
- run:
name: Patching Assembly Info
command: sed -i "s/AssemblyVersion(\".*\")/AssemblyVersion(\"$BUILD_VERSION.$CIRCLE_BUILD_NUM\")/gi" src/NzbDrone.Common/Properties/SharedAssemblyInfo.cs && cat src/NzbDrone.Common/Properties/SharedAssemblyInfo.cs
- run:
name: Clean Build
command: ./build.sh Clean
- run:
name: Restore Nuget
command: ./build.sh NugetMono
- run:
name: Build
command: ./build.sh Build
- restore_cache:
keys:
- v1-npm-deps-{{ checksum "package.json" }}
# Find the most recent cache used from any branch
- v1-npm-deps-
- run:
name: Gulp
command: ./build.sh Gulp
- save_cache:
key: v1-npm-deps-{{ checksum "package.json" }}
paths:
- "node_modules"
- run:
name: Package
command: ./build.sh Package
- run:
name: Preparing Tests
command: mkdir -p _tests/reports/junit && mkdir -p ../.config/Radarr && chmod -R 777 ../.config
- persist_to_workspace:
root: .
# Must be relative path from root
paths:
- _output
- _output_mono
- _output_osx
- _output_osx_app
- _tests
- setup
- .circleci
- deploy.sh
unit_tests:
<<: *defaults
steps:
- attach_workspace:
at: .
- run:
name: Preparing Tests
command: mkdir -p ../.config/Radarr && chmod -R 777 ../.config
- run:
name: Unit Tests
command: ./_tests/test.sh Linux Unit
- store_test_results:
path: _tests/reports/
integration_tests:
<<: *defaults
steps:
- attach_workspace:
at: .
- run:
name: Copy Binaries for Integration Tests
command: cp -R _output_mono/ _tests/bin
- run:
name: Preparing Tests
command: mkdir -p ../.config/Radarr && chmod -R 777 ../.config
- run:
name: Integration Tests
command: ./_tests/test.sh Linux Integration
- store_test_results:
path: _tests/reports/
publish_artifacts:
<<: *defaults
steps:
- attach_workspace:
at: .
- run:
name: "Creating packages"
command: |
mkdir -p _packages/
cp -r _output/ _packages/Radarr
zip -r _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.windows.zip _packages/Radarr
rm -rf _packages/Radarr
cp -r _output_mono/ _packages/Radarr
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz -C _packages Radarr
rm -rf _packages/Radarr
cp -r _output_osx/ _packages/Radarr
tar -zcvf _packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx.tar.gz -C _packages Radarr
rm -rf _packages/Radarr
cd _output_osx_app/
zip -r ../_packages/Radarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.osx-app.zip *
- run:
name: "Creating Installer"
command: wine setup/inno/ISCC.exe setup/nzbdrone.iss && cp -r setup/Output/Radarr* _packages/
- store_artifacts:
path: _packages
destination: artifacts
#- run:
# name: "Deploying"
# command: chmod +x deploy.sh && ./deploy.sh
- persist_to_workspace:
root: .
# Must be relative path from root
paths:
- _packages
deploy:
<<: *defaults
steps:
- attach_workspace:
at: .
- restore_cache:
keys:
- source-v1-{{ .Branch }}-{{ .Revision }}
- source-v1-{{ .Branch }}-
- source-v1-
- checkout
- run:
name: Creating Release
command: export LC_ALL=C.UTF-8 && export changelog=$(GITCHANGELOG_CONFIG_FILENAME=.gitchangelog.rc.release gitchangelog) && echo "Deploying v$BUILD_VERSION.$CIRCLE_BUILD_NUM to Github, with changelog:\n\n$changelog" && github-release release -u Radarr -r Radarr -t "v$BUILD_VERSION" -p --draft -d "$changelog" -n "Pre-Release v$BUILD_VERSION"
- run:
name: Uploading Assets
command: cd _packages && ls Radarr.*.* | xargs -n1 -P0 -I{} -- github-release upload -u Radarr -r Radarr -t "v$BUILD_VERSION.$CIRCLE_BUILD_NUM" --name {} --file {}
workflows:
version: 2
build_and_test:
jobs:
- build
- unit_tests:
requires:
- build
- integration_tests:
requires:
- build
- publish_artifacts:
requires:
- build
#- request_deploy:
# type: approval
# requires:
# - publish_artifacts
#- deploy:
# requires:
# - request_deploy
-69
View File
@@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/test-run">
<testsuites tests="{@testcasecount}" failures="{@failed}" disabled="{@skipped}" time="{@duration}">
<xsl:apply-templates/>
</testsuites>
</xsl:template>
<xsl:template match="test-suite">
<xsl:if test="test-case">
<testsuite tests="{@testcasecount}" time="{@duration}" errors="{@testcasecount - @passed - @skipped - @failed}" failures="{@failed}" skipped="{@skipped}" timestamp="{@start-time}">
<xsl:attribute name="name">
<xsl:for-each select="ancestor-or-self::test-suite/@name">
<xsl:value-of select="concat(., '.')"/>
</xsl:for-each>
</xsl:attribute>
<xsl:apply-templates select="test-case"/>
</testsuite>
<xsl:apply-templates select="test-suite"/>
</xsl:if>
<xsl:if test="not(test-case)">
<xsl:apply-templates/>
</xsl:if>
</xsl:template>
<xsl:template match="test-case">
<testcase name="{@name}" assertions="{@asserts}" time="{@duration}" status="{@result}" classname="{@classname}">
<xsl:if test="@runstate = 'Skipped' or @runstate = 'Ignored'">
<skipped/>
</xsl:if>
<xsl:apply-templates/>
</testcase>
</xsl:template>
<xsl:template match="command-line"/>
<xsl:template match="settings"/>
<xsl:template match="output">
<system-out>
<xsl:value-of select="."/>
</system-out>
</xsl:template>
<xsl:template match="stack-trace">
</xsl:template>
<xsl:template match="test-case/failure">
<failure message="{./message}">
<xsl:value-of select="./stack-trace"/>
</failure>
</xsl:template>
<xsl:template match="test-suite/failure"/>
<xsl:template match="test-case/reason">
<skipped message="{./message}"/>
</xsl:template>
<xsl:template match="test-case/assertions">
</xsl:template>
<xsl:template match="test-suite/reason"/>
<xsl:template match="properties"/>
</xsl:stylesheet>
+31 -2
View File
@@ -2,14 +2,43 @@
# editorconfig.org
root = true
[*.{cs,html,js,hbs}]
[*.cs]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
[*.less]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
[*.{js,html,js,hbs,less,css}]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
+9
View File
@@ -0,0 +1,9 @@
{
"paths": [
"frontend/src/**/*.js"
],
"ignored": [
"**/node_modules/**/*"
],
"port": 5004
}
+6 -18
View File
@@ -1,22 +1,10 @@
# Auto detect text files and perform LF normalization
*text eol=lf
* text=auto
# Explicitly set bash scripts to have unix endings
*.sh text eol=lf
macOS/Radarr text eol=lf
# Custom for Visual Studio
*.cs diff=csharp
#*.sln merge=union
#*.csproj merge=union
#*.vbproj merge=union
#*.fsproj merge=union
#*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.sln merge=union
+5 -2
View File
@@ -2,6 +2,9 @@
name: Bug report
about: Support requests will be closed immediately, if you are unsure go to our Discord
or Subreddit first. Exceptions do not mean you found a bug!
title: ''
labels: bug
assignees: ''
---
@@ -21,11 +24,11 @@ A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
**Platform Information (please complete the following information):**
- OS: [e.g. Windows]
- Mono Version: [e.g. Mono 5.8] (Only needed under Linux and Mac, found under System -> Status)
- Browser and Version [e.g. chrome, safari] (Only needed for UI issues)
- Version [e.g. 22]
- Radarr Version [e.g. 3.0.0.2956]
**Debug Logs**
Turn on debug logs under Settings -> General and wait for the bug to occur again. **Upload the full log file here (or another site and link it). Issues will be closed, if they do not include this!**
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Support via Discord
url: https://discord.gg/AD3UP37
about: Chat with users and devs on support and setup related topics.
- name: Support via Reddit
url: https://reddit.com/r/radarr
about: Discuss and search thru support topics.
@@ -1,6 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
+2
View File
@@ -7,6 +7,7 @@ exemptLabels:
- feature request
- parser
- confirmed
- aphrodite
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
@@ -14,3 +15,4 @@ markComment: >
This issue has been automatically marked as stale because it has not had recent activity. Please verify that this is still an issue with the latest version of Radarr and report back. Otherwise this issue will be closed.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
only: issues
+21 -11
View File
@@ -11,6 +11,7 @@ src/**/[Oo]bj/
*.user
*.sln.docstates
.vs/
.vscode/
# Build results
*_i.c
@@ -45,6 +46,10 @@ _dotCover*
# DevExpress CodeRush
src/.cr/
# Emacs
*~
\#*\#
# NCrunch
*.ncrunch*
.*crunch*.local.xml
@@ -80,7 +85,6 @@ TestResults
[Tt]est[Rr]esult*
*.Cache
ClientBin
[Ss]tyle[Cc]op.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
@@ -102,40 +106,44 @@ _NCrunch_*
_TeamCity*
# Radarr
Backups/
logs/
#MediaCover/
UpdateLogs/
xdg/
config.xml
logs.db*
nzbdrone.db*
nzbdrone.pid
nzbdrone.log*txt
UpdateLogs/
*workspace.xml
*.test-cache
*.userprefs
*/test-results/*
src/UI/.idea/*
*log.txt
node_modules/
_output*
_artifacts
_rawPackage/
_dotTrace*
_tests/
*.Result.xml
coverage*.xml
coverage*.json
setup/Output/
*.~is
UI.Phantom/
# VS outout folders
bin
obj
output/*
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# Packages
Radarr_*/
Radarr_*.zip
Radarr_*.gz
gecko.zip
geckodriver.exe
# macOS metadata files
._*
@@ -163,6 +171,8 @@ packages.config.md5sum
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/.idea.Radarr.Posix
**/.idea/.idea.Radarr.Windows
# Sensitive or high-churn files
**/.idea/**/dataSources/
-4
View File
@@ -1,4 +0,0 @@
[submodule "src/ExternalModules/CurlSharp"]
path = src/ExternalModules/CurlSharp
url = https://github.com/Sonarr/CurlSharp.git
branch = master
-16
View File
@@ -1,16 +0,0 @@
language: csharp
solution: src/NzbDrone.sln
addons:
apt:
packages:
- nodejs
# - npm apparently not needed anymore.
script:
- ./build.sh
- chmod +x test.sh
# - ./test.sh Linux Unit Takes far too long, maybe even crashes travis :/
after_success:
- chmod +x package.sh
- ./package.sh
notifications:
- webhooks: https://discordapp.com/api/webhooks/266910310219251712/V-QvCcnYkg3O8PMevcAJOJyCgrYkZQoF2pupLDGbaISNUECmYPd6LRwl3avKHsPyfgWP
+1
View File
@@ -0,0 +1 @@
save-prefix ""
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -15
View File
@@ -7,21 +7,7 @@ Setup guides, FAQ, the more information we have on the wiki the better.
## Development ##
### Tools required ###
- Visual Studio 2015
- HTML/Javascript editor of choice (Sublime Text/Webstorm/Atom/etc)
- npm (node package manager)
- git
### Getting started ###
1. Fork Radarr
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
3. Run `npm install`
4. Run `npm start` - Used to compile the UI components and copy them.
Leave this window open.
If you have gulp globally installed you can use `gulp watch` instead
5. Compile in Visual Studio
See the readme for information on setting up your development environment.
### Contributing Code ###
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Radarr/Radarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 49 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 605 B

After

Width:  |  Height:  |  Size: 577 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 10 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 49 KiB

+10 -19
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+21 -33
View File
@@ -1,13 +1,15 @@
<p align="center">
<img src="/Logo/text256.png" alt="Radarr">
</p>
# Radarr
[![Build Status](https://dev.azure.com/Radarr/Radarr/_apis/build/status/Radarr.Radarr?branchName=develop)](https://dev.azure.com/Radarr/Radarr/_build/latest?definitionId=1&branchName=develop)
[![Translated](https://translate.servarr.com/widgets/radarr/-/radarr/svg-badge.svg)](https://translate.servarr.com/engage/radarr/?utm_source=widget)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/radarr.svg)](https://github.com/Radarr/Radarr/wiki/Docker)
![Github Downloads](https://img.shields.io/github/downloads/Radarr/Radarr/total.svg)
[![Backers on Open Collective](https://opencollective.com/Radarr/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Radarr/sponsors/badge.svg)](#sponsors)
Radarr is an __independent__ fork of [Sonarr](https://github.com/Sonarr/Sonarr) reworked for automatically downloading movies via Usenet and BitTorrent.
The project was inspired by other Usenet/BitTorrent movie downloaders such as CouchPotato.
See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/11/roadmap-update.html) for an overview of planned features.
## Getting Started
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/wiki/Installation)
@@ -22,19 +24,14 @@ See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/
## Downloads
Branch | develop (stable) | nightly (semi-unstable) |
---|---|---
Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![AppVeyor Builds](https://img.shields.io/badge/downloads-continuous-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts)
Docker (linuxserver.io, x86_64, arm64, armhf) | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://store.docker.com/community/images/linuxserver/radarr)
Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more information) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker release / nightly](https://img.shields.io/badge/docker-release/nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr)
| Release Type | Branch: develop (stable) | Branch: nightly (semi-unstable) | Branch: aphrodite (very-unstable) |
|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Binary Releases | [![GitHub Releases](https://img.shields.io/badge/downloads-releases-brightgreen.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases) | [![AppVeyor Builds](https://img.shields.io/badge/downloads-nightly-green.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/radarr-usby1/branch/develop/artifacts) | |
| Docker | [![Docker release](https://img.shields.io/badge/linuxserver-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) | [![Docker nightly](https://img.shields.io/badge/linuxserver-radarr:nightly-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) | [![Docker aphrodite](https://img.shields.io/badge/linuxserver-radarr:preview-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr) |
| Docker | [![Docker release](https://img.shields.io/badge/hotio-radarr:latest-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker nightly](https://img.shields.io/badge/hotio-radarr:unstable-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) | [![Docker aphrodite](https://img.shields.io/badge/hotio-radarr:aphrodite-blue.svg?colorB=1488C6&maxAge=60&style=flat-square)](https://hub.docker.com/r/hotio/radarr) |
## Support
[![OpenCollective](https://opencollective.com/radarr/tiers/backer/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/radarr/tiers/flexible-sponsor/badge.svg)](#flexible-sponsors)
[![OpenCollective](https://opencollective.com/radarr/tiers/sponsor/badge.svg)](#sponsors)
[![Discord](https://img.shields.io/badge/discord-chat-7289DA.svg?maxAge=60&style=flat-square)](https://discord.gg/AD3UP37)
[![Reddit](https://img.shields.io/badge/reddit-discussion-FF4500.svg?maxAge=60&style=flat-square)](https://www.reddit.com/r/radarr)
[![Feathub](https://img.shields.io/badge/feathub-requests-lightgrey.svg?maxAge=60&style=flat-square)](http://feathub.com/Radarr/Radarr)
@@ -46,23 +43,13 @@ Docker (hotio, see [here](https://github.com/hotio/docker-radarr) for more infor
[![GitHub issues](https://img.shields.io/github/issues/radarr/radarr.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/issues)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/radarr/radarr.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/pulls)
[![GNU GPL v3](https://img.shields.io/badge/license-GNU%20GPL%20v3-blue.svg?maxAge=60&style=flat-square)](http://www.gnu.org/licenses/gpl.html)
[![Copyright 2010-2017](https://img.shields.io/badge/copyright-2017-blue.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr)
[![Copyright 2010-2020](https://img.shields.io/badge/copyright-2020-blue.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr)
[![Github Releases](https://img.shields.io/github/downloads/Radarr/Radarr/total.svg?maxAge=60&style=flat-square)](https://github.com/Radarr/Radarr/releases/)
[![Docker Pulls](https://img.shields.io/docker/pulls/linuxserver/radarr.svg?maxAge=60&style=flat-square)](https://hub.docker.com/r/linuxserver/radarr/)
[![Changelog](https://img.shields.io/github/commit-activity/w/radarr/radarr.svg?style=flat-square)](/CHANGELOG.md#unreleased)
| Service | Master | Develop |
|----------|:---------------------------:|:----------------------------:|
| AppVeyor | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr/master.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr) | [![AppVeyor](https://img.shields.io/appveyor/ci/galli-leo/Radarr-usby1/develop.svg?maxAge=60&style=flat-square)](https://ci.appveyor.com/project/galli-leo/Radarr-usby1) |
| Travis | [![Travis](https://img.shields.io/travis/Radarr/Radarr/master.svg?maxAge=60&style=flat-square)](https://travis-ci.org/Radarr/Radarr) | [![Travis](https://img.shields.io/travis/Radarr/Radarr/develop.svg?maxAge=60&style=flat-square)](https://travis-ci.org/Radarr/Radarr) |
### [Site and API Status](https://status.radarr.video)
| API | Updates | Sites |
|-------|:----:|:----:|
| [![API V2 (develop)](http://status.radarr.video/component/1/shield?style=flat-square)](https://api.radarr.video/v2/) | [![Update Server](http://status.radarr.video/component/4/shield?style=flat-square)](https://radarr.aeonlucid.com) | [![Radarr Mappings](http://status.radarr.video/component/6/shield?style=flat-square)](https://mappings.radarr.video/)
| [![API Staging (nightly)](http://status.radarr.video/component/2/shield?style=flat-square)](https://staging.api.radarr.video/) | [![Github Updates](http://status.radarr.video/component/5/shield?style=flat-square)](https://api.github.com/v3/) | [![Main Site](http://status.radarr.video/component/7/shield?style=flat-square)](https://radarr.video/)
Radarr is currently undergoing rapid development and pull requests are actively added into the repository.
## Features
@@ -102,17 +89,17 @@ See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/
### Requirements
* [Visual Studio Community 2017](https://www.visualstudio.com/vs/community/) or [Rider](http://www.jetbrains.com/rider/)
* [Visual Studio Community 2019](https://www.visualstudio.com/vs/community/) or [Rider](http://www.jetbrains.com/rider/)
* [Git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/)
* [Yarn](https://yarnpkg.com/)
### Setup
* Make sure all the required software mentioned above are installed
* Clone the repository into your development machine ([*info*](https://help.github.com/desktop/guides/contributing/working-with-your-remote-repository-on-github-or-github-enterprise))
* Grab the submodules `git submodule init && git submodule update`
* Install the required Node Packages `npm install`
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
* Install the required Node Packages `yarn install`
* Start gulp to monitor your dev environment for any changes that need post processing using `yarn start` command.
> **Notice**
> Gulp must be running at all times while you are working with Radarr client source files.
@@ -125,9 +112,9 @@ See the [Roadmap blogpost](https://blog.radarr.video/development/update/2018/11/
### Development
* Open `NzbDrone.sln` in Visual Studio 2017 or run the build.sh script, if Mono is installed. Alternatively you can use Jetbrains Rider, since it works on all Platforms.
* Open `Radarr.sln` in Visual Studio 2019 or run the build.sh script, if Mono is installed. Alternatively you can use Jetbrains Rider, since it works on all Platforms.
* Make sure `NzbDrone.Console` is set as the startup project
* Run `build.sh` before running
* Run `build.sh` before running, or build in VS
## Supporters
@@ -148,6 +135,7 @@ This project would not be possible without the support by these amazing folks. [
### JetBrains
Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools
* [<img src="/Logo/resharper.svg" alt="ReSharper" width="32"> ReSharper](http://www.jetbrains.com/resharper/)
* [<img src="/Logo/webstorm.svg" alt="WebStorm" width="32"> WebStorm](http://www.jetbrains.com/webstorm/)
* [<img src="/Logo/rider.svg" alt="Rider" width="32"> Rider](http://www.jetbrains.com/rider/)
@@ -156,4 +144,4 @@ Thank you to [<img src="/Logo/jetbrains.svg" alt="JetBrains" width="32"> JetBrai
## License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
* Copyright 2010-2018
* Copyright 2010-2020
-59
View File
@@ -1,59 +0,0 @@
version: '0.2.0.{build}'
image: Visual Studio 2017
assembly_info:
patch: true
file: 'src\NzbDrone.Common\Properties\SharedAssemblyInfo.cs'
assembly_version: '{version}'
assembly_file_version: '{version}'
assembly_informational_version: '{version}-rc1'
environment:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
install:
- git submodule update --init --recursive
#init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
build_script:
- ps: ./build-appveyor.ps1
test: off
#test:
# assemblies:
# - '_tests\*Test.dll'
# categories:
# except:
# - IntegrationTest
# - AutomationTest
artifacts:
- path: '_artifacts\*.zip'
- path: '_artifacts\*.exe'
- path: '_artifacts\*.tar.gz'
cache:
- '%USERPROFILE%\.nuget\packages'
- node_modules -> package.json
pull_requests:
do_not_increment_build_number: true
on_failure:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
- ps: Get-ChildItem .\_artifacts\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
- ps: Get-ChildItem .\_artifacts\*.exe | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
- ps: Get-ChildItem .\_artifacts\*.tar.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
only_commits:
files:
- src/
- osx/
- gulp/
- logo/
- setup/
- appveyor.yml
- build-appveyor.cake
+851
View File
@@ -0,0 +1,851 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
variables:
outputFolder: './_output'
artifactsFolder: './_artifacts'
testsFolder: './_tests'
majorVersion: '3.0.0'
minorVersion: $[counter('minorVersion', 2000)]
radarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(radarrVersion)'
sentryOrg: 'servarr'
sentryUrl: 'https://sentry.servarr.com'
dotnetVersion: '3.1.302'
trigger:
branches:
include:
- develop
- aphrodite
pr:
- develop
- aphrodite
stages:
- stage: Setup
displayName: Setup
jobs:
- job:
displayName: Build Variables
pool:
vmImage: 'ubuntu-18.04'
steps:
# Set the build name properly. The 'name' property won't recursively expand so hack here:
- bash: echo "##vso[build.updatebuildnumber]$RADARRVERSION"
displayName: Set Build Name
- bash: |
if [[ $BUILD_REASON == "PullRequest" ]]; then
git diff origin/aphrodite...HEAD --name-only | grep -E "^(src/|azure-pipelines.yml)"
echo $? > not_backend_update
else
echo 0 > not_backend_update
fi
cat not_backend_update
displayName: Check for Backend File Changes
- publish: not_backend_update
artifact: not_backend_update
displayName: Publish update type
- stage: Build_Backend
displayName: Build Backend
dependsOn: Setup
jobs:
- job: Backend
strategy:
matrix:
Linux:
osName: 'Linux'
imageName: 'ubuntu-18.04'
Mac:
osName: 'Mac'
imageName: 'macos-10.14'
Windows:
osName: 'Windows'
imageName: 'windows-2019'
pool:
vmImage: $(imageName)
steps:
- checkout: self
submodules: true
fetchDepth: 1
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- bash: ./build.sh --backend
displayName: Build Radarr Backend
- bash: |
find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
find ${TESTSFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
displayName: Clean up intermediate output
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- publish: $(outputFolder)
artifact: '$(osName)Backend'
displayName: Publish Backend
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/netcoreapp3.1/win-x64/publish'
artifact: WindowsCoreTests
displayName: Publish Windows Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/net462/linux-x64/publish'
artifact: LinuxTests
displayName: Publish Linux Mono Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/netcoreapp3.1/linux-x64/publish'
artifact: LinuxCoreTests
displayName: Publish Linux Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/netcoreapp3.1/linux-musl-x64/publish'
artifact: LinuxMuslCoreTests
displayName: Publish Linux Musl Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- publish: '$(testsFolder)/netcoreapp3.1/osx-x64/publish'
artifact: MacCoreTests
displayName: Publish MacOS Test Package
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- stage: Build_Frontend
displayName: Frontend
dependsOn: Setup
jobs:
- job: Build
strategy:
matrix:
Linux:
osName: 'Linux'
imageName: 'ubuntu-18.04'
Mac:
osName: 'Mac'
imageName: 'macos-10.14'
Windows:
osName: 'Windows'
imageName: 'windows-2019'
pool:
vmImage: $(imageName)
steps:
- task: NodeTool@0
displayName: Set Node.js version
inputs:
versionSpec: '10.x'
- checkout: self
submodules: true
fetchDepth: 1
- bash: ./build.sh --frontend
displayName: Build Radarr Frontend
env:
FORCE_COLOR: 0
- publish: $(outputFolder)
artifact: '$(osName)Frontend'
displayName: Publish Frontend
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- stage: Installer
dependsOn:
- Build_Backend
- Build_Frontend
jobs:
- job: Windows_Installer
displayName: Create Installer
pool:
vmImage: 'windows-2019'
steps:
- checkout: self
fetchDepth: 1
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: WindowsBackend
targetPath: _output
displayName: Fetch Backend
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: WindowsFrontend
targetPath: _output
displayName: Fetch Frontend
- bash: ./build.sh --packages
displayName: Create Packages
- bash: |
setup/inno/ISCC.exe setup/radarr.iss //DFramework=netcoreapp3.1
cp setup/output/Radarr.*windows.netcoreapp3.1.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Radarr.${BUILDNAME}.windows-core-x64-installer.exe
displayName: Create .NET Core Windows installer
- publish: $(Build.ArtifactStagingDirectory)
artifact: 'WindowsInstaller'
displayName: Publish Installer
- stage: Packages
dependsOn:
- Build_Backend
- Build_Frontend
jobs:
- job: Other_Packages
displayName: Create Standard Packages
pool:
vmImage: 'ubuntu-18.04'
steps:
- checkout: self
fetchDepth: 1
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: WindowsBackend
targetPath: _output
displayName: Fetch Backend
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: WindowsFrontend
targetPath: _output
displayName: Fetch Frontend
- bash: ./build.sh --packages
displayName: Create Packages
- bash: |
find . -name "Radarr" -exec chmod a+x {} \;
find . -name "Radarr.Update" -exec chmod a+x {} \;
displayName: Set executable bits
- task: ArchiveFiles@2
displayName: Create Windows Core zip
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).windows-core-x64.zip'
archiveType: 'zip'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/windows/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create MacOS Core app
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-app-core-x64.zip'
archiveType: 'zip'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/macos-app/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create MacOS Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).osx-core-x64.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/macos/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create Linux Mono tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x64/net462
- task: ArchiveFiles@2
displayName: Create Linux Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-x64.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-x64/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create Linux Musl Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-x64.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create ARM32 Linux Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create ARM64 Linux Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-core-arm64.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-arm64/netcoreapp3.1
- task: ArchiveFiles@2
displayName: Create ARM64 Linux Musl Core tar
inputs:
archiveFile: '$(Build.ArtifactStagingDirectory)/Radarr.$(buildName).linux-musl-core-arm64.tar.gz'
archiveType: 'tar'
tarCompression: 'gz'
includeRootFolder: false
rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/netcoreapp3.1
- publish: $(Build.ArtifactStagingDirectory)
artifact: 'Packages'
displayName: Publish Packages
- bash: |
echo "Uploading source maps to sentry"
curl -sL https://sentry.io/get-cli/ | bash
RELEASENAME="${RADARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
sentry-cli releases new --finalize -p radarr -p radarr-ui -p radarr-update "${RELEASENAME}"
sentry-cli releases -p radarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
sentry-cli releases set-commits --auto "${RELEASENAME}"
sentry-cli releases deploys "${RELEASENAME}" new -e aphrodite
if [ $? -gt 0 ]; then
echo "##vso[task.logissue type=warning]Error uploading source maps."
fi
exit 0
displayName: Publish Sentry Source Maps
continueOnError: true
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/aphrodite'))
env:
SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
SENTRY_ORG: $(sentryOrg)
SENTRY_URL: $(sentryUrl)
- stage: Unit_Test
displayName: Unit Tests
dependsOn: Build_Backend
jobs:
- job: Prepare
pool:
vmImage: 'ubuntu-18.04'
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Unit
displayName: Unit Native
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
MacCore:
osName: 'Mac'
testName: 'MacCore'
imageName: 'macos-10.14'
WindowsCore:
osName: 'Windows'
testName: 'WindowsCore'
imageName: 'windows-2019'
LinuxCore:
osName: 'Linux'
testName: 'LinuxCore'
imageName: 'ubuntu-18.04'
pattern: 'Radarr.**.linux-core-x64.tar.gz'
pool:
vmImage: $(imageName)
steps:
- checkout: none
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: '$(testName)Tests'
targetPath: $(testsFolder)
- bash: |
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-11_all.deb
sudo dpkg -i repo-mediaarea_1.0-11_all.deb
sudo apt-get update
sudo apt-get install -y --allow-unauthenticated libmediainfo-dev libmediainfo0v5 mediainfo
displayName: Install mediainfo
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
- powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service
condition: and(succeeded(), eq(variables['osName'], 'Windows'))
- bash: |
wget https://github.com/acoustid/chromaprint/releases/download/v1.4.3/chromaprint-fpcalc-1.4.3-linux-x86_64.tar.gz
sudo tar xf chromaprint-fpcalc-1.4.3-linux-x86_64.tar.gz --strip-components=1 --directory /usr/bin
displayName: Install fpcalc
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
- bash: |
SYMLINK=6_6_0
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
echo "##vso[task.setvariable variable=MONOPREFIX;]$MONOPREFIX"
echo "##vso[task.setvariable variable=PKG_CONFIG_PATH;]$MONOPREFIX/lib/pkgconfig:$MONOPREFIX/share/pkgconfig:$PKG_CONFIG_PATH"
echo "##vso[task.setvariable variable=PATH;]$MONOPREFIX/bin:$PATH"
displayName: Set Mono Version
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
displayName: Make Test Dummy Executable
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh ${OSNAME} Unit Test
displayName: Run Tests
env:
TEST_DIR: $(Build.SourcesDirectory)/_tests
- task: PublishTestResults@2
displayName: Publish Test Results
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Unit Tests'
failTaskOnFailedTests: true
- job: Unit_Docker
displayName: Unit Docker
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
mono508:
testName: 'Mono 5.8'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-5.8
mono520:
testName: 'Mono 5.20'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-5.20
mono610:
testName: 'Mono 6.10'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-6.10
mono612:
testName: 'Mono 6.12'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-6.12
alpine:
testName: 'Musl Net Core'
artifactName: LinuxMuslCoreTests
containerImage: servarr/testimages:alpine
pool:
vmImage: 'ubuntu-18.04'
container: $[ variables['containerImage'] ]
timeoutInMinutes: 10
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: $(artifactName)
targetPath: $(testsFolder)
- bash: find ${TESTSFOLDER} -name "Radarr.Test.Dummy" -exec chmod a+x {} \;
displayName: Make Test Dummy Executable
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
ls -lR ${TESTSFOLDER}
${TESTSFOLDER}/test.sh Linux Unit Test
displayName: Run Tests
- task: PublishTestResults@2
displayName: Publish Test Results
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Unit Tests'
failTaskOnFailedTests: true
- stage: Integration
displayName: Integration
dependsOn: Packages
jobs:
- job: Prepare
pool:
vmImage: 'ubuntu-18.04'
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Integration_Native
displayName: Integration Native
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
MacCore:
osName: 'Mac'
testName: 'MacCore'
imageName: 'macos-10.14'
pattern: 'Radarr.**.osx-core-x64.tar.gz'
WindowsCore:
osName: 'Windows'
testName: 'WindowsCore'
imageName: 'windows-2019'
pattern: 'Radarr.**.windows-core-x64.zip'
LinuxCore:
osName: 'Linux'
testName: 'LinuxCore'
imageName: 'ubuntu-18.04'
pattern: 'Radarr.**.linux-core-x64.tar.gz'
pool:
vmImage: $(imageName)
steps:
- bash: |
SYMLINK=6_6_0
MONOPREFIX=/Library/Frameworks/Mono.framework/Versions/$SYMLINK
echo "##vso[task.setvariable variable=MONOPREFIX;]$MONOPREFIX"
echo "##vso[task.setvariable variable=PKG_CONFIG_PATH;]$MONOPREFIX/lib/pkgconfig:$MONOPREFIX/share/pkgconfig:$PKG_CONFIG_PATH"
echo "##vso[task.setvariable variable=PATH;]$MONOPREFIX/bin:$PATH"
displayName: Set Mono Version
condition: and(succeeded(), eq(variables['osName'], 'Mac'))
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: '$(testName)Tests'
targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2
displayName: Download Build Artifact
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
mkdir -p ./bin/
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
displayName: Move Package Contents
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh ${OSNAME} Integration Test
displayName: Run Integration Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Integration Tests'
failTaskOnFailedTests: true
displayName: Publish Test Results
- job: Integration_Docker
displayName: Integration Docker
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
strategy:
matrix:
mono508:
testName: 'Mono 5.8'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-5.8
pattern: 'Radarr.**.linux.tar.gz'
mono520:
testName: 'Mono 5.20'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-5.20
pattern: 'Radarr.**.linux.tar.gz'
mono610:
testName: 'Mono 6.10'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-6.10
pattern: 'Radarr.**.linux.tar.gz'
mono612:
testName: 'Mono 6.12'
artifactName: LinuxTests
containerImage: servarr/testimages:mono-6.12
pattern: 'Radarr.**.linux.tar.gz'
alpine:
testName: 'Musl Net Core'
artifactName: LinuxCoreTests
containerImage: servarr/testimages:alpine
pattern: 'Radarr.**.linux-musl-core-x64.tar.gz'
pool:
vmImage: 'ubuntu-18.04'
container: $[ variables['containerImage'] ]
timeoutInMinutes: 15
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: $(artifactName)
targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2
displayName: Download Build Artifact
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
mkdir -p ./bin/
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
displayName: Move Package Contents
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh Linux Integration Test
displayName: Run Integration Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(testName) Integration Tests'
failTaskOnFailedTests: true
displayName: Publish Test Results
- stage: Automation
displayName: Automation
dependsOn: Packages
jobs:
- job: Automation
strategy:
matrix:
Linux:
osName: 'Linux'
imageName: 'ubuntu-18.04'
pattern: 'Radarr.**.linux-core-x64.tar.gz'
failBuild: true
Mac:
osName: 'Mac'
imageName: 'macos-10.14' # Fails due to firefox not being installed on image
pattern: 'Radarr.**.osx-core-x64.tar.gz'
failBuild: false
Windows:
osName: 'Windows'
imageName: 'windows-2019'
pattern: 'Radarr.**.windows-core-x64.zip'
failBuild: $(failOnAutomationFailure)
pool:
vmImage: $(imageName)
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: none
- task: DownloadPipelineArtifact@2
displayName: Download Test Artifact
inputs:
buildType: 'current'
artifactName: '$(osName)CoreTests'
targetPath: $(testsFolder)
- task: DownloadPipelineArtifact@2
displayName: Download Build Artifact
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
displayName: Extract Package
- bash: |
mkdir -p ./bin/
cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Radarr/. ./bin/
displayName: Move Package Contents
- bash: |
if [[ $OSNAME == "Mac" ]]; then
url=https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-macos.tar.gz
elif [[ $OSNAME == "Linux" ]]; then
url=https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
else
echo "Unhandled OS"
exit 1
fi
curl -s -L "$url" | tar -xz
chmod +x geckodriver
mv geckodriver _tests
displayName: Install Gecko Driver
condition: and(succeeded(), ne(variables['osName'], 'Windows'))
- bash: |
chmod a+x ${TESTSFOLDER}/test.sh
${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
displayName: Run Integration Tests
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '**/TestResult.xml'
testRunTitle: '$(osName) Automation Tests'
failTaskOnFailedTests: $(failBuild)
displayName: Publish Test Results
- stage: Analyze
dependsOn:
- Setup
displayName: Analyze
jobs:
- job: Prepare
pool:
vmImage: 'ubuntu-18.04'
steps:
- checkout: none
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'not_backend_update'
targetPath: '.'
- bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
name: setVar
- job: Lint_Frontend
displayName: Lint Frontend
strategy:
matrix:
Linux:
osName: 'Linux'
imageName: 'ubuntu-18.04'
Windows:
osName: 'Windows'
imageName: 'windows-2019'
pool:
vmImage: $(imageName)
steps:
- task: NodeTool@0
displayName: Set Node.js version
inputs:
versionSpec: '10.x'
- checkout: self
submodules: true
fetchDepth: 1
- bash: ./build.sh --lint
displayName: Lint Radarr Frontend
env:
FORCE_COLOR: 0
- job: Analyze_Frontend
displayName: Frontend
condition: eq(variables['System.PullRequest.IsFork'], 'False')
pool:
vmImage: windows-2019
steps:
- checkout: self # Need history for Sonar analysis
- task: SonarCloudPrepare@1
env:
SONAR_SCANNER_OPTS: ''
inputs:
SonarCloud: 'SonarCloud'
organization: 'radarr'
scannerMode: 'CLI'
configMode: 'manual'
cliProjectKey: 'Radarr_Radarr.UI'
cliProjectName: 'RadarrUI'
cliProjectVersion: '$(radarrVersion)'
cliSources: './frontend'
- task: SonarCloudAnalyze@1
- job: Analyze_Backend
displayName: Backend
dependsOn: Prepare
condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
variables:
disable.coverage.autogenerate: 'true'
pool:
vmImage: windows-2019
steps:
- task: UseDotNet@2
displayName: 'Install .net core'
inputs:
version: $(dotnetVersion)
- checkout: self # Need history for Sonar analysis
submodules: true
- powershell: Set-Service SCardSvr -StartupType Manual
displayName: Enable Windows Test Service
- task: SonarCloudPrepare@1
condition: eq(variables['System.PullRequest.IsFork'], 'False')
inputs:
SonarCloud: 'SonarCloud'
organization: 'radarr'
scannerMode: 'MSBuild'
projectKey: 'Radarr_Radarr'
projectName: 'Radarr'
projectVersion: '$(radarrVersion)'
extraProperties: |
sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
sonar.coverage.exclusions=**/Radarr.Api.V3/**/*,**/NzbDrone.Api/**/*,**/MonoTorrent/**/*,**/Marr.Data/**/*
sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
- bash: |
./build.sh --backend -f netcoreapp3.1 -r win-x64
TEST_DIR=_tests/netcoreapp3.1/win-x64/publish/ ./test.sh Windows Unit Coverage
displayName: Coverage Unit Tests
- task: SonarCloudAnalyze@1
condition: eq(variables['System.PullRequest.IsFork'], 'False')
displayName: Publish SonarCloud Results
- task: reportgenerator@4
displayName: Generate Coverage Report
inputs:
reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
- task: PublishCodeCoverageResults@1
displayName: Publish Coverage Report
inputs:
codeCoverageTool: 'cobertura'
summaryFileLocation: './CoverageResults/combined/Cobertura.xml'
reportDirectory: './CoverageResults/combined/'
- stage: Report_Out
dependsOn:
- Analyze
- Unit_Test
- Integration
- Automation
condition: eq(variables['system.pullrequest.isfork'], false)
displayName: Build Status Report
jobs:
- job:
displayName: Discord Notification
pool:
vmImage: 'ubuntu-18.04'
steps:
- checkout: none
- powershell: |
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey)
-323
View File
@@ -1,323 +0,0 @@
#addin nuget:?package=Cake.Npm
#addin nuget:?package=SharpZipLib
#addin nuget:?package=Cake.Compression
#addin "Cake.FileHelpers"
// Build variables
var outputFolder = "./_output";
var outputFolderMono = outputFolder + "_mono";
var outputFolderOsx = outputFolder + "_osx";
var outputFolderOsxApp = outputFolderOsx + "_app";
var testPackageFolder = "./_tests";
var testSearchPattern = "*.Test/bin/x86/Release";
var sourceFolder = "./src";
var solutionFile = sourceFolder + "/NzbDrone.sln";
var updateFolder = outputFolder + "/NzbDrone.Update";
var updateFolderMono = outputFolderMono + "/NzbDrone.Update";
// Artifact variables
var artifactsFolder = "./_artifacts";
var artifactsFolderWindows = artifactsFolder + "/windows";
var artifactsFolderLinux = artifactsFolder + "/linux";
var artifactsFolderOsx = artifactsFolder + "/osx";
var artifactsFolderOsxApp = artifactsFolder + "/osx-app";
// Utility methods
public void RemoveEmptyFolders(string startLocation) {
foreach (var directory in System.IO.Directory.GetDirectories(startLocation))
{
RemoveEmptyFolders(directory);
if (System.IO.Directory.GetFiles(directory).Length == 0 &&
System.IO.Directory.GetDirectories(directory).Length == 0)
{
DeleteDirectory(directory, false);
}
}
}
public void CleanFolder(string path, bool keepConfigFiles) {
DeleteFiles(path + "/**/*.transform");
if (!keepConfigFiles) {
DeleteFiles(path + "/**/*.dll.config");
}
DeleteFiles(path + "/**/FluentValidation.resources.dll");
DeleteFiles(path + "/**/App.config");
DeleteFiles(path + "/**/*.less");
DeleteFiles(path + "/**/*.vshost.exe");
DeleteFiles(path + "/**/*.dylib");
RemoveEmptyFolders(path);
}
public void CreateMdbs(string path) {
foreach (var file in System.IO.Directory.EnumerateFiles(path, "*.pdb", System.IO.SearchOption.AllDirectories)) {
var actualFile = file.Substring(0, file.Length - 4);
if (FileExists(actualFile + ".exe")) {
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
.WithArguments(args => args.Append(actualFile + ".exe")));
}
if (FileExists(actualFile + ".dll")) {
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
.WithArguments(args => args.Append(actualFile + ".dll")));
}
}
}
// Build Tasks
Task("Compile").Does(() => {
// Build
if (DirectoryExists(outputFolder)) {
DeleteDirectory(outputFolder, true);
}
MSBuild(solutionFile, config =>
config.UseToolVersion(MSBuildToolVersion.VS2017)
.WithTarget("Clean")
.SetVerbosity(Verbosity.Minimal));
NuGetRestore(solutionFile);
MSBuild(solutionFile, config =>
config.UseToolVersion(MSBuildToolVersion.VS2017)
.SetPlatformTarget(PlatformTarget.x86)
.SetConfiguration("Release")
.WithProperty("AllowedReferenceRelatedFileExtensions", new string[] { ".pdb" })
.WithTarget("Build")
.SetVerbosity(Verbosity.Minimal));
CleanFolder(outputFolder, false);
// Add JsonNet
DeleteFiles(outputFolder + "/Newtonsoft.Json.*");
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", outputFolder);
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", updateFolder);
// Remove Mono stuff
DeleteFile(outputFolder + "/Mono.Posix.dll");
});
Task("Gulp").Does(() => {
NpmInstall(new NpmInstallSettings {
LogLevel = NpmLogLevel.Silent,
WorkingDirectory = "./",
Production = true
});
NpmRunScript("build");
});
Task("PackageMono").Does(() => {
// Start mono package
if (DirectoryExists(outputFolderMono)) {
DeleteDirectory(outputFolderMono, true);
}
CopyDirectory(outputFolder, outputFolderMono);
// Create MDBs
CreateMdbs(outputFolderMono);
// Remove PDBs
DeleteFiles(outputFolderMono + "/**/*.pdb");
// Remove service helpers
DeleteFiles(outputFolderMono + "/ServiceUninstall.*");
DeleteFiles(outputFolderMono + "/ServiceInstall.*");
// Remove native windows binaries
DeleteFiles(outputFolderMono + "/sqlite3.*");
DeleteFiles(outputFolderMono + "/MediaInfo.*");
// Adding NzbDrone.Core.dll.config (for dllmap)
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", outputFolderMono + "/NzbDrone.Core.dll.config");
// Adding CurlSharp.dll.config (for dllmap)
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", outputFolderMono + "/CurlSharp.dll.config");
// Renaming Radarr.Console.exe to Radarr.exe
DeleteFiles(outputFolderMono + "/Radarr.exe*");
MoveFile(outputFolderMono + "/Radarr.Console.exe", outputFolderMono + "/Radarr.exe");
MoveFile(outputFolderMono + "/Radarr.Console.exe.config", outputFolderMono + "/Radarr.exe.config");
MoveFile(outputFolderMono + "/Radarr.Console.exe.mdb", outputFolderMono + "/Radarr.exe.mdb");
// Remove NzbDrone.Windows.*
DeleteFiles(outputFolderMono + "/NzbDrone.Windows.*");
// Adding NzbDrone.Mono to updatePackage
CopyFiles(outputFolderMono + "/NzbDrone.Mono.*", updateFolderMono);
});
Task("PackageOsx").Does(() => {
// Start osx package
if (DirectoryExists(outputFolderOsx)) {
DeleteDirectory(outputFolderOsx, true);
}
CopyDirectory(outputFolderMono, outputFolderOsx);
// Adding sqlite dylibs
CopyFiles(sourceFolder + "/Libraries/Sqlite/*.dylib", outputFolderOsx);
// Adding MediaInfo dylib
CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx);
// Chmod as executable
StartProcess(@"C:\cygwin64\bin\chmod.exe", new ProcessSettings()
.WithArguments(args => args
.Append("+x")
.Append(outputFolderOsx + "/Radarr")));
// Adding Startup script
CopyFile("./osx/Radarr", outputFolderOsx + "/Radarr");
});
Task("PackageOsxApp").Does((ctx) => {
// Start osx app package
if (DirectoryExists(outputFolderOsxApp)) {
DeleteDirectory(outputFolderOsxApp, true);
}
CreateDirectory(outputFolderOsxApp);
// Copy osx package files
CopyDirectory("./osx/Radarr.app", outputFolderOsxApp + "/Radarr.app");
CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Radarr.app/Contents/MacOS");
// Edit version of osx app
ctx.ReplaceTextInFiles(outputFolderOsxApp + "/Radarr.app/Contents/Info.plist", "2.0", ctx.EnvironmentVariable("APPVEYOR_BUILD_VERSION") ?? "unknown");
});
Task("PackageTests").Does(() => {
// Start tests package
if (DirectoryExists(testPackageFolder)) {
DeleteDirectory(testPackageFolder, true);
}
CreateDirectory(testPackageFolder);
// Copy tests
CopyFiles(sourceFolder + "/" + testSearchPattern + "/*", testPackageFolder);
foreach (var directory in System.IO.Directory.GetDirectories(sourceFolder, "*.Test")) {
var releaseDirectory = directory + "/bin/x86/Release";
if (DirectoryExists(releaseDirectory)) {
foreach (var releaseSubDirectory in System.IO.Directory.GetDirectories(releaseDirectory)) {
Information(System.IO.Path.GetDirectoryName(releaseSubDirectory));
CopyDirectory(releaseSubDirectory, testPackageFolder + "/" + System.IO.Path.GetFileName(releaseSubDirectory));
}
}
}
// Install NUnit.ConsoleRunner
NuGetInstall("NUnit.ConsoleRunner", new NuGetInstallSettings {
Version = "3.2.0",
OutputDirectory = testPackageFolder
});
// Copy dlls
CopyFiles(outputFolder + "/*.dll", testPackageFolder);
// Copy scripts
CopyFiles("./*.sh", testPackageFolder);
// Create MDBs for tests
CreateMdbs(testPackageFolder);
// Remove config
DeleteFiles(testPackageFolder + "/*.log.config");
// Clean
CleanFolder(testPackageFolder, true);
// Adding NzbDrone.Core.dll.config (for dllmap)
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", testPackageFolder + "/NzbDrone.Core.dll.config");
// Adding CurlSharp.dll.config (for dllmap)
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", testPackageFolder + "/CurlSharp.dll.config");
// Adding CurlSharp libraries
CopyFiles(sourceFolder + "/ExternalModules/CurlSharp/libs/i386/*", testPackageFolder);
});
Task("CleanupWindowsPackage").Does(() => {
// Remove mono
DeleteFiles(outputFolder + "/NzbDrone.Mono.*");
// Adding NzbDrone.Windows to updatePackage
CopyFiles(outputFolder + "/NzbDrone.Windows.*", updateFolder);
});
Task("Build")
.IsDependentOn("Compile")
.IsDependentOn("Gulp")
.IsDependentOn("PackageMono")
.IsDependentOn("PackageOsx")
.IsDependentOn("PackageOsxApp")
.IsDependentOn("PackageTests")
.IsDependentOn("CleanupWindowsPackage");
// Build Artifacts
Task("CleanArtifacts").Does(() => {
if (DirectoryExists(artifactsFolder)) {
DeleteDirectory(artifactsFolder, true);
}
CreateDirectory(artifactsFolder);
});
Task("ArtifactsWindows").Does(() => {
CopyDirectory(outputFolder, artifactsFolderWindows + "/Radarr");
});
Task("ArtifactsWindowsInstaller").Does(() => {
InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings {
OutputDirectory = artifactsFolder,
ToolPath = "./setup/inno/ISCC.exe"
});
});
Task("ArtifactsLinux").Does(() => {
CopyDirectory(outputFolderMono, artifactsFolderLinux + "/Radarr");
});
Task("ArtifactsOsx").Does(() => {
CopyDirectory(outputFolderOsx, artifactsFolderOsx + "/Radarr");
});
Task("ArtifactsOsxApp").Does(() => {
CopyDirectory(outputFolderOsxApp, artifactsFolderOsxApp);
});
Task("CompressArtifacts").Does(() => {
var prefix = "";
if (AppVeyor.IsRunningOnAppVeyor) {
prefix += AppVeyor.Environment.Repository.Branch.Replace("/", "-") + ".";
prefix += AppVeyor.Environment.Build.Version + ".";
}
Zip(artifactsFolderWindows, artifactsFolder + "/Radarr." + prefix + "windows.zip");
GZipCompress(artifactsFolderLinux, artifactsFolder + "/Radarr." + prefix + "linux.tar.gz");
GZipCompress(artifactsFolderOsx, artifactsFolder + "/Radarr." + prefix + "osx.tar.gz");
Zip(artifactsFolderOsxApp, artifactsFolder + "/Radarr." + prefix + "osx-app.zip");
});
Task("Artifacts")
.IsDependentOn("CleanArtifacts")
.IsDependentOn("ArtifactsWindows")
.IsDependentOn("ArtifactsWindowsInstaller")
.IsDependentOn("ArtifactsLinux")
.IsDependentOn("ArtifactsOsx")
.IsDependentOn("ArtifactsOsxApp")
.IsDependentOn("CompressArtifacts");
// Run
RunTarget("Build");
RunTarget("Artifacts");
-189
View File
@@ -1,189 +0,0 @@
##########################################################################
# This is the Cake bootstrapper script for PowerShell.
# This file was downloaded from https://github.com/cake-build/resources
# Feel free to change this file to fit your needs.
##########################################################################
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
.PARAMETER Experimental
Tells Cake to use the latest Roslyn release.
.PARAMETER WhatIf
Performs a dry run of the build script.
No tasks will be executed.
.PARAMETER Mono
Tells Cake to use the Mono scripting engine.
.PARAMETER SkipToolPackageRestore
Skips restoring of packages.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
http://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build-appveyor.cake",
[string]$Target = "Default",
[ValidateSet("Release", "Debug")]
[string]$Configuration = "Release",
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity = "Verbose",
[switch]$Experimental,
[Alias("DryRun","Noop")]
[switch]$WhatIf,
[switch]$Mono,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
function MD5HashFile([string] $filePath)
{
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
{
return $null
}
[System.IO.Stream] $file = $null;
[System.Security.Cryptography.MD5] $md5 = $null;
try
{
$md5 = [System.Security.Cryptography.MD5]::Create()
$file = [System.IO.File]::OpenRead($filePath)
return [System.BitConverter]::ToString($md5.ComputeHash($file))
}
finally
{
if ($file -ne $null)
{
$file.Dispose()
}
}
}
Write-Host "Preparing to run build script..."
if(!$PSScriptRoot){
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
}
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget/nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
# Should we use mono?
$UseMono = "";
if($Mono.IsPresent) {
Write-Verbose -Message "Using the Mono based scripting engine."
$UseMono = "-mono"
}
# Should we use the new Roslyn?
$UseExperimental = "";
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
Write-Verbose -Message "Using experimental version of Roslyn."
$UseExperimental = "-experimental"
}
# Is this a dry run?
$UseDryRun = "";
if($WhatIf.IsPresent) {
$UseDryRun = "-dryrun"
}
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..."
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) {
Write-Verbose -Message "Downloading packages.config..."
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
Throw "Could not download packages.config."
}
}
# Try find NuGet.exe in path if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
}
}
# Try download NuGet.exe if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
}
# Save nuget.exe path to environment to be available to child processed
$ENV:NUGET_EXE = $NUGET_EXE
# Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent) {
Push-Location
Set-Location $TOOLS_DIR
# Check for changes in packages.config and remove installed tools if true.
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
Write-Verbose -Message "Missing or changed package.config hash..."
Get-ChildItem -Path $TOOLS_DIR -Recurse -Exclude packages.config |
Select -ExpandProperty FullName |
Where {$_ -notlike (Join-Path $TOOLS_DIR "pdb2mdb*")} |
Where {$_ -notlike (Join-Path $TOOLS_DIR "nuget*")} |
sort length -Descending |
Remove-Item -Recurse
}
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
if ($LASTEXITCODE -ne 0) {
Throw "An error occured while restoring NuGet tools."
}
else
{
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
}
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
}
# Make sure that Cake has been installed.
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Start Cake
Write-Host "Running build script..."
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
exit $LASTEXITCODE
-1
View File
@@ -1 +0,0 @@
Write-Warning "DEPRECATED -- Please use build.sh instead."
+283 -233
View File
@@ -1,313 +1,363 @@
#! /bin/bash
msBuild='/MSBuild/15.0/Bin'
outputFolder='./_output'
outputFolderMono='./_output_mono'
outputFolderOsx='./_output_osx'
outputFolderOsxApp='./_output_osx_app'
testPackageFolder='./_tests/'
testSearchPattern='*.Test/bin/x86/Release'
sourceFolder='./src'
slnFile=$sourceFolder/NzbDrone.sln
updateFolder=$outputFolder/NzbDrone.Update
updateFolderMono=$outputFolderMono/NzbDrone.Update
set -e
nuget='tools/nuget/nuget.exe';
CheckExitCode()
outputFolder='_output'
testPackageFolder='_tests'
artifactsFolder="_artifacts";
ProgressStart()
{
"$@"
local status=$?
if [ $status -ne 0 ]; then
echo "error with $1" >&2
exit 1
echo "Start '$1'"
}
ProgressEnd()
{
echo "Finish '$1'"
}
UpdateVersionNumber()
{
if [ "$RADARRVERSION" != "" ]; then
echo "Updating Version Info"
sed -i'' -e "s/<AssemblyVersion>[0-9.*]\+<\/AssemblyVersion>/<AssemblyVersion>$RADARRVERSION<\/AssemblyVersion>/g" src/Directory.Build.props
sed -i'' -e "s/<AssemblyConfiguration>[\$()A-Za-z-]\+<\/AssemblyConfiguration>/<AssemblyConfiguration>${BUILD_SOURCEBRANCHNAME}<\/AssemblyConfiguration>/g" src/Directory.Build.props
sed -i'' -e "s/<string>10.0.0.0<\/string>/<string>$RADARRVERSION<\/string>/g" macOS/Radarr.app/Contents/Info.plist
fi
return $status
}
CleanFolder()
LintUI()
{
local path=$1
local keepConfigFiles=$2
ProgressStart 'ESLint'
yarn lint
ProgressEnd 'ESLint'
find $path -name "*.transform" -exec rm "{}" \;
if [ $keepConfigFiles != true ] ; then
find $path -name "*.dll.config" -exec rm "{}" \;
ProgressStart 'Stylelint'
if [ "$os" = "windows" ]; then
yarn stylelint-windows
else
yarn stylelint-linux
fi
echo "Removing FluentValidation.Resources files"
find $path -name "FluentValidation.resources.dll" -exec rm "{}" \;
find $path -name "App.config" -exec rm "{}" \;
echo "Removing .less files"
find $path -name "*.less" -exec rm "{}" \;
echo "Removing vshost files"
find $path -name "*.vshost.exe" -exec rm "{}" \;
echo "Removing dylib files"
find $path -name "*.dylib" -exec rm "{}" \;
echo "Removing Empty folders"
find $path -depth -empty -type d -exec rm -r "{}" \;
}
AddJsonNet()
{
rm $outputFolder/Newtonsoft.Json.*
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder
cp $sourceFolder/packages/Newtonsoft.Json.*/lib/net35/*.dll $outputFolder/NzbDrone.Update
}
BuildWithMSBuild()
{
export PATH=$msBuild:$PATH
echo $PATH
CheckExitCode MSBuild.exe $slnFile //t:Clean //m
$nuget restore $slnFile
CheckExitCode MSBuild.exe $slnFile //p:Configuration=Release //p:Platform=x86 //t:Build //m //p:AllowedReferenceRelatedFileExtensions=.pdb
}
RestoreNuget()
{
export MONO_IOMAP=case
mono $nuget restore $slnFile
}
CleanWithXbuild()
{
export MONO_IOMAP=case
CheckExitCode msbuild /t:Clean $slnFile
}
BuildWithXbuild()
{
export MONO_IOMAP=case
CheckExitCode msbuild /p:Configuration=Release /p:Platform=x86 /t:Build /p:AllowedReferenceRelatedFileExtensions=.pdb /maxcpucount:3 $slnFile
ProgressEnd 'Stylelint'
}
Build()
{
echo "##teamcity[progressStart 'Build']"
ProgressStart 'Build'
rm -rf $outputFolder
rm -rf $testPackageFolder
if [ $runtime = "dotnet" ] ; then
BuildWithMSBuild
slnFile=src/Radarr.sln
if [ $os = "windows" ]; then
platform=Windows
else
CleanWithXbuild
RestoreNuget
BuildWithXbuild
platform=Posix
fi
CleanFolder $outputFolder false
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
AddJsonNet
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
then
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -t:PublishAllRids
else
dotnet msbuild -restore $slnFile -p:Configuration=Release -p:Platform=$platform -p:RuntimeIdentifiers=$RID -t:PublishAllRids
fi
echo "Removing Mono.Posix.dll"
rm $outputFolder/Mono.Posix.dll
ProgressEnd 'Build'
}
echo "##teamcity[progressFinish 'Build']"
YarnInstall()
{
ProgressStart 'yarn install'
yarn install --frozen-lockfile --network-timeout 120000
ProgressEnd 'yarn install'
}
RunGulp()
{
echo "##teamcity[progressStart 'npm install']"
npm-cache install npm || CheckExitCode npm install
echo "##teamcity[progressFinish 'npm install']"
echo "##teamcity[progressStart 'Running gulp']"
CheckExitCode npm run build
echo "##teamcity[progressFinish 'Running gulp']"
ProgressStart 'Running gulp'
yarn run build --production
ProgressEnd 'Running gulp'
}
CreateMdbs()
PackageFiles()
{
local path=$1
if [ $runtime = "dotnet" ] ; then
local pdbFiles=( $(find $path -name "*.pdb") )
for filename in "${pdbFiles[@]}"
do
if [ -e ${filename%.pdb}.dll ] ; then
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.dll
fi
if [ -e ${filename%.pdb}.exe ] ; then
tools/pdb2mdb/pdb2mdb.exe ${filename%.pdb}.exe
fi
done
fi
local folder="$1"
local framework="$2"
local runtime="$3"
rm -rf $folder
mkdir -p $folder
cp -r $outputFolder/$framework/$runtime/publish/* $folder
cp -r $outputFolder/Radarr.Update/$framework/$runtime/publish $folder/Radarr.Update
cp -r $outputFolder/UI $folder
echo "Adding LICENSE"
cp LICENSE $folder
}
PackageMono()
PackageLinux()
{
echo "##teamcity[progressStart 'Creating Mono Package']"
rm -rf $outputFolderMono
cp -r $outputFolder $outputFolderMono
local framework="$1"
local runtime="$2"
echo "Creating MDBs"
CreateMdbs $outputFolderMono
ProgressStart "Creating $runtime Package for $framework"
echo "Removing PDBs"
find $outputFolderMono -name "*.pdb" -exec rm "{}" \;
local folder=$artifactsFolder/$runtime/$framework/Radarr
PackageFiles "$folder" "$framework" "$runtime"
echo "Removing Service helpers"
rm -f $outputFolderMono/ServiceUninstall.*
rm -f $outputFolderMono/ServiceInstall.*
rm -f $folder/ServiceUninstall.*
rm -f $folder/ServiceInstall.*
echo "Removing native windows binaries Sqlite, MediaInfo"
rm -f $outputFolderMono/sqlite3.*
rm -f $outputFolderMono/MediaInfo.*
echo "Removing Radarr.Windows"
rm $folder/Radarr.Windows.*
echo "Adding NzbDrone.Core.dll.config (for dllmap)"
cp $sourceFolder/NzbDrone.Core/NzbDrone.Core.dll.config $outputFolderMono
echo "Adding Radarr.Mono to UpdatePackage"
cp $folder/Radarr.Mono.* $folder/Radarr.Update
if [ "$framework" = "netcoreapp3.1" ]; then
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
fi
echo "Adding CurlSharp.dll.config (for dllmap)"
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $outputFolderMono
echo "Renaming NzbDrone.Console.exe to NzbDrone.exe"
rm $outputFolderMono/Radarr.exe*
for file in $outputFolderMono/Radarr.Console.exe*; do
mv "$file" "${file//.Console/}"
done
echo "Removing NzbDrone.Windows"
rm $outputFolderMono/NzbDrone.Windows.*
echo "Adding NzbDrone.Mono to UpdatePackage"
cp $outputFolderMono/NzbDrone.Mono.* $updateFolderMono
echo "##teamcity[progressFinish 'Creating Mono Package']"
ProgressEnd "Creating $runtime Package for $framework"
}
PackageOsx()
PackageMacOS()
{
echo "##teamcity[progressStart 'Creating OS X Package']"
rm -rf $outputFolderOsx
cp -r $outputFolderMono $outputFolderOsx
local framework="$1"
echo "Adding sqlite dylibs"
cp $sourceFolder/Libraries/Sqlite/*.dylib $outputFolderOsx
ProgressStart "Creating MacOS Package for $framework"
echo "Adding MediaInfo dylib"
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
local folder=$artifactsFolder/macos/$framework/Radarr
PackageFiles "$folder" "$framework" "osx-x64"
if [ "$framework" = "net462" ]; then
echo "Adding Startup script"
cp ./osx/Radarr $outputFolderOsx
cp macOS/Radarr $folder
fi
echo "##teamcity[progressFinish 'Creating OS X Package']"
echo "Removing Service helpers"
rm -f $folder/ServiceUninstall.*
rm -f $folder/ServiceInstall.*
echo "Removing Radarr.Windows"
rm $folder/Radarr.Windows.*
echo "Adding Radarr.Mono to UpdatePackage"
cp $folder/Radarr.Mono.* $folder/Radarr.Update
if [ "$framework" = "netcoreapp3.1" ]; then
cp $folder/Mono.Posix.NETStandard.* $folder/Radarr.Update
cp $folder/libMonoPosixHelper.* $folder/Radarr.Update
fi
ProgressEnd 'Creating MacOS Package'
}
PackageOsxApp()
PackageMacOSApp()
{
echo "##teamcity[progressStart 'Creating OS X App Package']"
rm -rf $outputFolderOsxApp
mkdir $outputFolderOsxApp
local framework="$1"
cp -r ./osx/Radarr.app $outputFolderOsxApp
cp -r $outputFolderOsx $outputFolderOsxApp/Radarr.app/Contents/MacOS
ProgressStart "Creating macOS App Package for $framework"
echo "##teamcity[progressFinish 'Creating OS X App Package']"
local folder=$artifactsFolder/macos-app/$framework
rm -rf $folder
mkdir -p $folder
cp -r macOS/Radarr.app $folder
mkdir -p $folder/Radarr.app/Contents/MacOS
echo "Copying Binaries"
cp -r $artifactsFolder/macos/$framework/Radarr/* $folder/Radarr.app/Contents/MacOS
echo "Removing Update Folder"
rm -r $folder/Radarr.app/Contents/MacOS/Radarr.Update
ProgressEnd 'Creating macOS App Package'
}
PackageWindows()
{
local framework="$1"
ProgressStart "Creating Windows Package for $framework"
local folder=$artifactsFolder/windows/$framework/Radarr
PackageFiles "$folder" "$framework" "win-x64"
echo "Removing Radarr.Mono"
rm -f $folder/Radarr.Mono.*
rm -f $folder/Mono.Posix.NETStandard.*
rm -f $folder/libMonoPosixHelper.*
echo "Adding Radarr.Windows to UpdatePackage"
cp $folder/Radarr.Windows.* $folder/Radarr.Update
ProgressEnd 'Creating Windows Package'
}
Package()
{
local framework="$1"
local runtime="$2"
local SPLIT
IFS='-' read -ra SPLIT <<< "$runtime"
case "${SPLIT[0]}" in
linux)
PackageLinux "$framework" "$runtime"
;;
win)
PackageWindows "$framework"
;;
osx)
PackageMacOS "$framework"
PackageMacOSApp "$framework"
;;
esac
}
PackageTests()
{
echo "Packaging Tests"
echo "##teamcity[progressStart 'Creating Test Package']"
rm -rf $testPackageFolder
mkdir $testPackageFolder
local framework="$1"
local runtime="$2"
find $sourceFolder -path $testSearchPattern -exec cp -r -u -T "{}" $testPackageFolder \;
cp test.sh "$testPackageFolder/$framework/$runtime/publish"
if [ $runtime = "dotnet" ] ; then
$nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
else
mono $nuget install NUnit.Runners -Version 3.9.0 -Output $testPackageFolder
rm -f $testPackageFolder/$framework/$runtime/*.log.config
# geckodriver.exe isn't copied by dotnet publish
if [ "$runtime" = "win-x64" ];
then
curl -Lso gecko.zip "https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-win64.zip"
unzip -o gecko.zip
cp geckodriver.exe "$testPackageFolder/$framework/win-x64/publish"
fi
cp $outputFolder/*.dll $testPackageFolder
cp ./*.sh $testPackageFolder
echo "Creating MDBs for tests"
CreateMdbs $testPackageFolder
rm -f $testPackageFolder/*.log.config
CleanFolder $testPackageFolder true
echo "Adding NzbDrone.Core.dll.config (for dllmap)"
cp $sourceFolder/NzbDrone.Core/NzbDrone.Core.dll.config $testPackageFolder
echo "Adding CurlSharp.dll.config (for dllmap)"
cp $sourceFolder/NzbDrone.Common/CurlSharp.dll.config $testPackageFolder
echo "Copying CurlSharp libraries"
cp $sourceFolder/ExternalModules/CurlSharp/libs/i386/* $testPackageFolder
echo "##teamcity[progressFinish 'Creating Test Package']"
}
CleanupWindowsPackage()
{
echo "Removing NzbDrone.Mono"
rm -f $outputFolder/NzbDrone.Mono.*
echo "Adding NzbDrone.Windows to UpdatePackage"
cp $outputFolder/NzbDrone.Windows.* $updateFolder
ProgressEnd 'Creating Test Package'
}
# Use mono or .net depending on OS
case "$(uname -s)" in
CYGWIN*|MINGW32*|MINGW64*|MSYS*)
# on windows, use dotnet
runtime="dotnet"
vsLoc=$(./vswhere.exe -property installationPath)
vsLoc=$(echo "/$vsLoc" | sed -e 's/\\/\//g' -e 's/://')
msBuild="$vsLoc$msBuild"
os="windows"
;;
*)
# otherwise use mono
runtime="mono"
os="posix"
;;
esac
if [ $# -eq 0 ]
then
POSITIONAL=()
if [ $# -eq 0 ]; then
echo "No arguments provided, building everything"
BACKEND=YES
FRONTEND=YES
PACKAGES=YES
LINT=YES
fi
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
--backend)
BACKEND=YES
shift # past argument
;;
-r|--runtime)
RID="$2"
shift # past argument
shift # past value
;;
-f|--framework)
FRAMEWORK="$2"
shift # past argument
shift # past value
;;
--frontend)
FRONTEND=YES
shift # past argument
;;
--packages)
PACKAGES=YES
shift # past argument
;;
--lint)
LINT=YES
shift # past argument
;;
--all)
BACKEND=YES
FRONTEND=YES
PACKAGES=YES
LINT=YES
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [ "$BACKEND" = "YES" ];
then
UpdateVersionNumber
Build
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
then
PackageTests "netcoreapp3.1" "win-x64"
PackageTests "netcoreapp3.1" "linux-x64"
PackageTests "netcoreapp3.1" "linux-musl-x64"
PackageTests "netcoreapp3.1" "osx-x64"
PackageTests "net462" "linux-x64"
else
PackageTests "$FRAMEWORK" "$RID"
fi
fi
if [ "$FRONTEND" = "YES" ];
then
YarnInstall
RunGulp
PackageMono
PackageOsx
PackageOsxApp
PackageTests
CleanupWindowsPackage
fi
if [ "$1" = "CleanXbuild" ]
then rm -rf $outputFolder
CleanWithXbuild
if [ "$LINT" = "YES" ];
then
if [ -z "$FRONTEND" ];
then
YarnInstall
fi
LintUI
fi
if [ "$1" = "NugetMono" ]
then rm -rf $outputFolder
RestoreNuget
fi
if [ "$PACKAGES" = "YES" ];
then
UpdateVersionNumber
if [ "$1" = "Build" ]
then BuildWithXbuild
CleanFolder $outputFolder false
AddJsonNet
rm $outputFolder/Mono.Posix.dll
fi
if [ "$1" = "Gulp" ]
then RunGulp
fi
if [ "$1" = "Package" ]
then PackageMono
PackageOsx
PackageOsxApp
PackageTests
CleanupWindowsPackage
if [[ -z "$RID" || -z "$FRAMEWORK" ]];
then
Package "netcoreapp3.1" "win-x64"
Package "netcoreapp3.1" "linux-x64"
Package "netcoreapp3.1" "linux-musl-x64"
Package "netcoreapp3.1" "linux-arm64"
Package "netcoreapp3.1" "linux-musl-arm64"
Package "netcoreapp3.1" "linux-arm"
Package "netcoreapp3.1" "osx-x64"
Package "net462" "linux-x64"
else
Package "$FRAMEWORK" "$RID"
fi
fi
-7
View File
@@ -1,7 +0,0 @@
if [ -z "$CIRCLE_PULL_REQUEST" ]; then
echo "We are building a normal branch, deploying as such..."
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=branch&name=${CIRCLE_BRANCH}"
else
echo "We are building a pr, deploying as such..."
curl "http://pr.radarr.video:4466/deploy?url=https%3A%2F%2F${CIRCLE_BUILD_NUM}-77323220-gh.circle-artifacts.com%2F0%2Fartifacts%2FRadarr.${CIRCLE_BRANCH//\//-}.$BUILD_VERSION.$CIRCLE_BUILD_NUM.linux.tar.gz&b=pr&name=${CIRCLE_PR_NUMBER}"
fi
+25
View File
@@ -0,0 +1,25 @@
{
"remove-empty-rulesets": true,
"always-semicolon": true,
"color-case": "lower",
"block-indent": " ",
"color-shorthand": false,
"element-case": "lower",
"eof-newline": true,
"leading-zero": true,
"quotes": "double",
"sort-order-fallback": "abc",
"space-before-colon": "",
"space-after-colon": " ",
"space-before-combinator": " ",
"space-after-combinator": " ",
"space-between-declarations": "\n",
"space-before-opening-brace": " ",
"space-after-opening-brace": "\n",
"space-after-selector-delimiter": " ",
"space-before-selector-delimiter": "",
"space-before-closing-brace": "\n",
"strip-spaces": true,
"tab-size": true,
"unitless-zero": false
}
+335
View File
@@ -0,0 +1,335 @@
{
"indent": {
"value": " ",
"FunctionExpression": 1,
"ArrayExpression": 1,
"ObjectExpression": 1
},
"lineBreak": {
"value": "\n",
"before": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": ">=1",
"ArrowFunctionExpressionOpeningBrace": 0,
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": ">=1",
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": 0,
"CatchClosingBrace": ">=1",
"CatchKeyword": 0,
"CatchOpeningBrace": 0,
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": 0,
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": ">=1",
"DoWhileStatementOpeningBrace": 0,
"ElseIfStatement": 0,
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": 0,
"ElseStatement": 0,
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": 0,
"EmptyStatement": -1,
"EndOfFile": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": 0,
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 0,
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": "<2",
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 0,
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace":0,
"IIFEClosingParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": 0,
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": -1,
"MethodDefinition": ">=1",
"ObjectExpressionClosingBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": "<=2",
"PropertyValue": 0,
"ReturnStatement": -1,
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": 0,
"ThisExpression": -1,
"ThrowStatement": ">=1",
"TryClosingBrace": ">=1",
"TryKeyword": -1,
"TryOpeningBrace": 0,
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": 0,
"VariableDeclarationWithoutInit": ">=1",
"VariableName": ">=1",
"VariableValue": 0,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": 0
},
"after": {
"ArrayPatternClosing": 0,
"ArrayPatternComma": 0,
"ArrayPatternOpening": 0,
"ArrowFunctionExpressionArrow": 0,
"ArrowFunctionExpressionClosingBrace": -1,
"ArrowFunctionExpressionOpeningBrace": ">=1",
"AssignmentExpression": ">=1",
"AssignmentOperator": 0,
"BlockStatement": 0,
"BreakKeyword": -1,
"CallExpression": -1,
"CallExpressionClosingParentheses": -1,
"CallExpressionOpeningParentheses": -1,
"CatchClosingBrace": ">=0",
"CatchKeyword": 0,
"CatchOpeningBrace": ">=1",
"ClassDeclaration": ">=1",
"ClassDeclarationClosingBrace": ">=1",
"ClassDeclarationOpeningBrace": ">=1",
"ConditionalExpression": ">=1",
"DeleteOperator": ">=1",
"DoWhileStatement": ">=1",
"DoWhileStatementClosingBrace": 0,
"DoWhileStatementOpeningBrace": ">=1",
"ElseIfStatement": ">=1",
"ElseIfStatementClosingBrace": ">=1",
"ElseIfStatementOpeningBrace": ">=1",
"ElseStatement": ">=1",
"ElseStatementClosingBrace": ">=1",
"ElseStatementOpeningBrace": ">=1",
"EmptyStatement": -1,
"FinallyClosingBrace": ">=1",
"FinallyKeyword": -1,
"FinallyOpeningBrace": ">=1",
"ForInStatement": ">=1",
"ForInStatementClosingBrace": ">=1",
"ForInStatementExpressionClosing": -1,
"ForInStatementExpressionOpening": "<2",
"ForInStatementOpeningBrace": ">=1",
"ForStatement": ">=1",
"ForStatementClosingBrace": ">=1",
"ForStatementExpressionClosing": -1,
"ForStatementExpressionOpening": "<2",
"ForStatementOpeningBrace": ">=1",
"FunctionDeclaration": ">=1",
"FunctionDeclarationClosingBrace": ">=1",
"FunctionDeclarationOpeningBrace": ">=1",
"FunctionExpression": 0,
"FunctionExpressionClosingBrace": -1,
"FunctionExpressionOpeningBrace": 1,
"IIFEOpeningParentheses": 0,
"IfStatement": ">=1",
"IfStatementClosingBrace": ">=1",
"IfStatementOpeningBrace": ">=1",
"LogicalExpression": -1,
"MemberExpressionClosing": 0,
"MemberExpressionOpening": 0,
"MemberExpressionPeriod": 0,
"MethodDefinition": ">=1",
"ObjectExpressionOpeningBrace": "<=1",
"ObjectPatternClosingBrace": 0,
"ObjectPatternComma": 0,
"ObjectPatternOpeningBrace": 0,
"ParameterDefault": 0,
"Property": -1,
"PropertyName": 0,
"ReturnStatement": -1,
"SwitchCaseColon": ">=1",
"SwitchClosingBrace": ">=1",
"SwitchOpeningBrace": ">=1",
"ThisExpression": 0,
"ThrowStatement": ">=1",
"TryClosingBrace": 0,
"TryKeyword": -1,
"TryOpeningBrace": ">=1",
"VariableDeclaration": ">=1",
"VariableDeclarationSemiColon": ">=1",
"VariableValue": -1,
"WhileStatement": ">=1",
"WhileStatementClosingBrace": ">=1",
"WhileStatementOpeningBrace": ">=1"
}
},
"whiteSpace": {
"value": " ",
"removeTrailing": 1,
"before": {
"ArgumentComma": 0,
"ArgumentList": 0,
"ArgumentListArrayExpression": 0,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 0,
"ArrayExpressionOpening": 1,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 1,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 0,
"ConditionalExpressionAlternate": 1,
"ConditionalExpressionConsequent": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementConditional": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionClosingParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 0,
"ForInStatementExpressionOpening": 1,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 0,
"ForStatementExpressionOpening": 1,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 0,
"FunctionDeclarationClosingBrace": 1,
"FunctionDeclarationOpeningBrace": 1,
"FunctionExpressionClosingBrace": 1,
"FunctionExpressionOpeningBrace": 1,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 1,
"IfStatementOpeningBrace": 1,
"LineComment": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionClosing": 0,
"ObjectExpressionClosingBrace": 1,
"ParameterComma": 0,
"ParameterList": 0,
"Property": 1,
"PropertyName": 1,
"PropertyValue": 1,
"SwitchDiscriminantClosing": 0,
"SwitchDiscriminantOpening": 1,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"VariableValue": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 0,
"WhileStatementConditionalOpening": 1,
"WhileStatementOpeningBrace": 1
},
"after": {
"ArgumentComma": 1,
"ArgumentList": 0,
"ArgumentListArrayExpression": 1,
"ArgumentListFunctionExpression": 1,
"ArgumentListObjectExpression": 0,
"ArrayExpressionClosing": 0,
"ArrayExpressionComma": 1,
"ArrayExpressionOpening": 0,
"AssignmentOperator": 1,
"BinaryExpression": 0,
"BinaryExpressionOperator": 1,
"BlockComment": 1,
"CallExpression": 0,
"CatchClosingBrace": 1,
"CatchKeyword": 1,
"CatchOpeningBrace": 1,
"CatchParameterList": 0,
"CommaOperator": 1,
"ConditionalExpressionConsequent": 1,
"ConditionalExpressionTest": 1,
"DoWhileStatementBody": 1,
"DoWhileStatementClosingBrace": 1,
"DoWhileStatementOpeningBrace": 1,
"ElseIfStatementClosingBrace": 1,
"ElseIfStatementOpeningBrace": 1,
"ElseStatementClosingBrace": 1,
"ElseStatementOpeningBrace": 1,
"EmptyStatement": 0,
"ExpressionOpeningParentheses": 0,
"FinallyClosingBrace": 1,
"FinallyKeyword": -1,
"FinallyOpeningBrace": 1,
"ForInStatement": 1,
"ForInStatementClosingBrace": 1,
"ForInStatementExpressionClosing": 1,
"ForInStatementExpressionOpening": 0,
"ForInStatementOpeningBrace": 1,
"ForStatement": 1,
"ForStatementClosingBrace": 1,
"ForStatementExpressionClosing": 1,
"ForStatementExpressionOpening": 0,
"ForStatementOpeningBrace": 1,
"ForStatementSemicolon": 1,
"FunctionDeclarationClosingBrace": 0,
"FunctionDeclarationOpeningBrace": 0,
"FunctionExpressionClosingBrace": 0,
"FunctionExpressionOpeningBrace": 0,
"FunctionName": 0,
"FunctionReservedWord": 0,
"IfStatementClosingBrace": 1,
"IfStatementConditionalClosing": 0,
"IfStatementConditionalOpening": 0,
"IfStatementOpeningBrace": 1,
"LogicalExpressionOperator": 1,
"MemberExpressionOpening": 0,
"ObjectExpressionClosingBrace": 0,
"ObjectExpressionOpeningBrace": 1,
"ParameterComma": 1,
"ParameterList": 0,
"PropertyName": 0,
"PropertyValue": 0,
"SwitchDiscriminantClosing": 1,
"SwitchDiscriminantOpening": 0,
"ThrowKeyword": 1,
"TryClosingBrace": 1,
"TryKeyword": -1,
"TryOpeningBrace": 1,
"UnaryExpressionOperator": 0,
"VariableName": 1,
"WhileStatementClosingBrace": 1,
"WhileStatementConditionalClosing": 1,
"WhileStatementConditionalOpening": 0,
"WhileStatementOpeningBrace": 1
}
}
}
+1
View File
@@ -0,0 +1 @@
**/JsLibraries/**
+327
View File
@@ -0,0 +1,327 @@
const fs = require('fs');
const dirs = fs
.readdirSync('frontend/src', { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.join('|');
module.exports = {
parser: 'babel-eslint',
env: {
browser: true,
commonjs: true,
node: true,
es6: true
},
globals: {
expect: false,
chai: false,
sinon: false
},
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
modules: true,
impliedStrict: true
}
},
plugins: [
'filenames',
'react',
'simple-import-sort',
'import'
],
settings: {
react: {
version: 'detect'
}
},
rules: {
'filenames/match-exported': ['error'],
// ECMAScript 6
'arrow-body-style': [0],
'arrow-parens': ['error', 'always'],
'arrow-spacing': ['error', { before: true, after: true }],
'constructor-super': 'error',
'generator-star-spacing': 'off',
'no-class-assign': 'error',
'no-confusing-arrow': 'error',
'no-const-assign': 'error',
'no-dupe-class-members': 'error',
'no-duplicate-imports': 'error',
'no-new-symbol': 'error',
'no-this-before-super': 'error',
'no-useless-escape': 'error',
'no-useless-computed-key': 'error',
'no-useless-constructor': 'error',
'no-var': 'warn',
'object-shorthand': ['error', 'properties'],
'prefer-arrow-callback': 'error',
'prefer-const': 'warn',
'prefer-reflect': 'off',
'prefer-rest-params': 'off',
'prefer-spread': 'warn',
'prefer-template': 'error',
'require-yield': 'off',
'template-curly-spacing': ['error', 'never'],
'yield-star-spacing': 'off',
// Possible Errors
'comma-dangle': 'error',
'no-cond-assign': 'error',
'no-console': 'off',
'no-constant-condition': 'warn',
'no-control-regex': 'error',
'no-debugger': 'off',
'no-dupe-args': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-empty': 'warn',
'no-empty-character-class': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
'no-extra-parens': ['error', 'functions'],
'no-extra-semi': 'error',
'no-func-assign': 'error',
'no-inner-declarations': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-negated-in-lhs': 'error',
'no-obj-calls': 'error',
'no-regex-spaces': 'error',
'no-sparse-arrays': 'error',
'no-unexpected-multiline': 'error',
'no-unreachable': 'warn',
'no-unsafe-finally': 'error',
'use-isnan': 'error',
'valid-jsdoc': 'off',
'valid-typeof': 'error',
// Best Practices
'accessor-pairs': 'off',
'array-callback-return': 'warn',
'block-scoped-var': 'warn',
'consistent-return': 'off',
curly: 'error',
'default-case': 'error',
'dot-location': ['error', 'property'],
'dot-notation': 'error',
eqeqeq: ['error', 'smart'],
'guard-for-in': 'error',
'no-alert': 'warn',
'no-caller': 'error',
'no-case-declarations': 'error',
'no-div-regex': 'error',
'no-else-return': 'error',
'no-empty-function': ['error', { allow: ['arrowFunctions'] }],
'no-empty-pattern': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-fallthrough': 'error',
'no-floating-decimal': 'error',
'no-implicit-coercion': ['error', {
boolean: false,
number: true,
string: true,
allow: [/* "!!", "~", "*", "+" */]
}],
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-this': 'off',
'no-iterator': 'error',
'no-labels': 'error',
'no-lone-blocks': 'error',
'no-loop-func': 'error',
'no-magic-numbers': ['off', { ignoreArrayIndexes: true, ignore: [0, 1] }],
'no-multi-spaces': 'error',
'no-multi-str': 'error',
'no-native-reassign': ['error', { exceptions: ['console'] }],
'no-new': 'off',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-octal': 'error',
'no-octal-escape': 'error',
'no-param-reassign': 'off',
'no-process-env': 'off',
'no-proto': 'error',
'no-redeclare': 'error',
'no-return-assign': 'warn',
'no-script-url': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'error',
'no-unused-expressions': 'error',
'no-unused-labels': 'error',
'no-useless-call': 'error',
'no-useless-concat': 'error',
'no-void': 'error',
'no-warning-comments': 'off',
'no-with': 'error',
radix: ['error', 'as-needed'],
'vars-on-top': 'off',
'wrap-iife': ['error', 'inside'],
yoda: 'error',
// Strict Mode
strict: ['error', 'never'],
// Variables
'init-declarations': ['error', 'always'],
'no-catch-shadow': 'error',
'no-delete-var': 'error',
'no-label-var': 'error',
'no-restricted-globals': 'off',
'no-shadow': 'error',
'no-shadow-restricted-names': 'error',
'no-undef': 'error',
'no-undef-init': 'off',
'no-undefined': 'off',
'no-unused-vars': ['error', { args: 'none', ignoreRestSiblings: true }],
'no-use-before-define': 'error',
// Node.js and CommonJS
'callback-return': 'warn',
'global-require': 'error',
'handle-callback-err': 'warn',
'no-mixed-requires': 'error',
'no-new-require': 'error',
'no-path-concat': 'error',
'no-process-exit': 'error',
// Stylistic Issues
'array-bracket-spacing': ['error', 'never'],
'block-spacing': ['error', 'always'],
'brace-style': ['error', '1tbs', { allowSingleLine: false }],
camelcase: 'off',
'comma-spacing': ['error', { before: false, after: true }],
'comma-style': ['error', 'last'],
'computed-property-spacing': ['error', 'never'],
'consistent-this': ['error', 'self'],
'eol-last': 'error',
'func-names': 'off',
'func-style': ['error', 'declaration'],
indent: ['error', 2, { SwitchCase: 1 }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'keyword-spacing': ['error', { before: true, after: true }],
'lines-around-comment': ['error', { beforeBlockComment: true, afterBlockComment: false }],
'max-depth': ['error', { maximum: 5 }],
'max-nested-callbacks': ['error', 4],
'max-statements': 'off',
'max-statements-per-line': ['error', { max: 1 }],
'new-cap': ['error', { capIsNewExceptions: ['$.Deferred', 'DragDropContext', 'DragLayer', 'DragSource', 'DropTarget'] }],
'new-parens': 'error',
'newline-after-var': 'off',
'newline-before-return': 'off',
'newline-per-chained-call': 'off',
'no-array-constructor': 'error',
'no-bitwise': 'error',
'no-continue': 'error',
'no-inline-comments': 'off',
'no-lonely-if': 'warn',
'no-mixed-spaces-and-tabs': 'error',
'no-multiple-empty-lines': ['error', { max: 1 }],
'no-negated-condition': 'warn',
'no-nested-ternary': 'error',
'no-new-object': 'error',
'no-plusplus': 'off',
'no-restricted-syntax': 'off',
'no-spaced-func': 'error',
'no-ternary': 'off',
'no-trailing-spaces': 'error',
'no-underscore-dangle': ['error', { allowAfterThis: true }],
'no-unneeded-ternary': 'error',
'no-whitespace-before-property': 'error',
'object-curly-spacing': ['error', 'always'],
'one-var': ['error', 'never'],
'one-var-declaration-per-line': ['error', 'always'],
'operator-assignment': ['off', 'never'],
'operator-linebreak': ['error', 'after'],
'quote-props': ['error', 'as-needed'],
quotes: ['error', 'single'],
'require-jsdoc': 'off',
semi: 'error',
'semi-spacing': ['error', { before: false, after: true }],
'sort-vars': 'off',
'space-before-blocks': ['error', 'always'],
'space-before-function-paren': ['error', 'never'],
'space-in-parens': 'off',
'space-infix-ops': 'off',
'space-unary-ops': 'off',
'spaced-comment': 'error',
'wrap-regex': 'error',
// ImportSort
'simple-import-sort/sort': 'error',
'import/newline-after-import': 'error',
// React
'react/jsx-boolean-value': [2, 'always'],
'react/jsx-uses-vars': 2,
'react/jsx-closing-bracket-location': 2,
'react/jsx-tag-spacing': ['error'],
'react/jsx-curly-spacing': [2, 'never'],
'react/jsx-equals-spacing': [2, 'never'],
'react/jsx-indent-props': [2, 2],
'react/jsx-indent': [2, 2, { indentLogicalExpressions: true }],
'react/jsx-key': 2,
'react/jsx-no-bind': [2, { allowArrowFunctions: true }],
'react/jsx-no-duplicate-props': [2, { ignoreCase: true }],
'react/jsx-max-props-per-line': [2, { maximum: 2 }],
'react/jsx-handler-names': [2, { eventHandlerPrefix: '(on|dispatch)', eventHandlerPropPrefix: 'on' }],
'react/jsx-no-undef': 2,
'react/jsx-pascal-case': 2,
'react/jsx-uses-react': 2,
// Explicitly disabled in case we want to enable them again
'react/no-did-mount-set-state': 0,
'react/no-did-update-set-state': 0,
'react/no-direct-mutation-state': 2,
'react/no-multi-comp': [2, { ignoreStateless: true }],
'react/no-unknown-property': 2,
'react/prefer-es6-class': 2,
'react/prop-types': 2,
'react/react-in-jsx-scope': 2,
'react/self-closing-comp': 2,
'react/sort-comp': 2,
'react/jsx-wrap-multilines': 2
},
overrides: [
{
files: ['*.js'],
rules: {
'simple-import-sort/sort': [
'error',
{
groups: [
// Packages
// Absolute Paths
// Relative Paths
// Css
['^@?\\w', `^(${dirs})(/.*|$)`, '^\\.', '^\\..*css$']
]
}
]
}
}
]
};
+12
View File
@@ -0,0 +1,12 @@
{
"js": {
"indent_size": 2,
"indent_char": " ",
"indent_level": 2,
"indent_with_tabs": false,
"preserve_newlines": true,
"brace_style": "collapse",
"max_preserve_newlines": 2,
"jslint_happy": true
}
}
+396
View File
@@ -0,0 +1,396 @@
{
"plugins": [
"stylelint-order"
],
"ignoreFiles": [
"frontend/src/Styles/scaffolding.css",
"**/*.js"
],
"rules": {
"at-rule-empty-line-before": [
"always",
{
"except": [
"inside-block"
]
}
],
"at-rule-name-case": "lower",
"at-rule-name-newline-after": "always-multi-line",
"at-rule-name-space-after": "always",
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"/^add\\-mixin$/",
"/^define\\-mixin$/"
]
}
],
"at-rule-no-vendor-prefix": true,
"at-rule-semicolon-newline-after": "always",
"at-rule-semicolon-space-before": "never",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always",
"block-closing-brace-space-after": "always-single-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always",
"block-opening-brace-newline-before": "never-single-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short",
"color-named": "never",
"color-no-invalid-hex": true,
"comment-whitespace-inside": "always",
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [
true,
{
"ignoreProperties": [
"composes"
]
}
],
"declaration-block-no-redundant-longhand-properties": true,
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always",
"declaration-block-semicolon-newline-before": "never-multi-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-space-after": "always",
"declaration-colon-space-before": "never",
"font-family-name-quotes": "always-unless-keyword",
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "never-multi-line",
"function-comma-newline-before": "never-multi-line",
"function-comma-space-after": "always",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-name-case": "lower",
"function-parentheses-newline-inside": "never-multi-line",
"function-parentheses-space-inside": "never",
"function-url-quotes": "always",
"function-url-scheme-blacklist": [
"data"
],
"function-whitespace-after": "always",
"indentation": 2,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"max-empty-lines": 1,
"max-line-length": [
100,
{
"ignore": [
"non-comments"
]
}
],
"max-nesting-depth": 2,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-vendor-prefix": true,
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"order/order": [
"custom-properties",
"dollar-variables",
{
"hasBlock": false,
"name": "add-mixin",
"type": "at-rule"
},
"declarations",
"rules",
"at-rules"
],
"order/properties-order": [
{
"emptyLineBefore": "always",
"properties": [
"composes"
]
},
{
"emptyLineBefore": "always",
"properties": [
"position",
"top",
"right",
"bottom",
"left",
"z-index",
"display",
"visibility",
"align-content",
"align-items",
"align-self",
"justify-content",
"flex",
"flex-direction",
"flex-order",
"flex-pack",
"flex-align",
"flex-grow",
"flex-shrink",
"flex-basis",
"flex-wrap",
"flex-flow",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"-webkit-overflow-scrolling",
"clip",
"box-sizing",
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"min-width",
"min-height",
"max-width",
"max-height",
"width",
"height",
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"border",
"border-spacing",
"border-collapse",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"border-radius",
"border-top-left-radius",
"border-top-right-radius",
"border-bottom-right-radius",
"border-bottom-left-radius",
"border-image",
"border-image-source",
"border-image-slice",
"border-image-width",
"border-image-outset",
"border-image-repeat",
"border-top-image",
"border-right-image",
"border-bottom-image",
"border-left-image",
"border-corner-image",
"border-top-left-image",
"border-top-right-image",
"border-bottom-right-image",
"border-bottom-left-image",
"background",
"background-color",
"background-image",
"background-attachment",
"background-position",
"background-position-x",
"background-position-y",
"background-clip",
"background-origin",
"background-size",
"background-repeat",
"box-decoration-break",
"box-shadow",
"color",
"table-layout",
"caption-side",
"empty-cells",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image",
"quotes",
"content",
"counter-increment",
"counter-reset",
"-ms-writing-mode",
"vertical-align",
"text-align",
"text-align-last",
"text-decoration",
"text-emphasis",
"text-emphasis-position",
"text-emphasis-style",
"text-emphasis-color",
"text-indent",
"text-justify",
"text-outline",
"text-transform",
"text-wrap",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"text-shadow",
"white-space",
"word-spacing",
"word-wrap",
"word-break",
"tab-size",
"hyphens",
"letter-spacing",
"font",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-size",
"font-family",
"font-smoothing",
"-moz-osx-font-smoothing",
"-webkit-font-smoothing",
"src",
"line-height",
"opacity",
"filter",
"resize",
"cursor",
"appearance",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"transition",
"transition-delay",
"transition-timing-function",
"transition-duration",
"transition-property",
"transform",
"transform-origin",
"transform-style",
"backface-visibility",
"animation",
"animation-name",
"animation-duration",
"animation-play-state",
"animation-timing-function",
"animation-delay",
"animation-iteration-count",
"animation-direction",
"animation-fill-mode",
"pointer-events",
"user-select",
"touch-action",
"-webkit-tap-highlight-color",
"unicode-bidi",
"direction",
"columns",
"column-span",
"column-width",
"column-count",
"column-fill",
"column-gap",
"column-rule",
"column-rule-width",
"column-rule-style",
"column-rule-color",
"break-before",
"break-inside",
"break-after",
"page-break-before",
"page-break-inside",
"page-break-after",
"orphans",
"widows",
"zoom",
"max-zoom",
"min-zoom",
"user-zoom",
"orientation"
]
}
],
"property-case": "lower",
"property-no-vendor-prefix": true,
"rule-empty-line-before": [
"always",
{
"except": [
"first-nested"
],
"ignore": [
"after-comment"
]
}
],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-attribute-quotes": "never",
"selector-class-pattern": "^[A-Za-z0-9]+$",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-newline-before": "never-multi-line",
"selector-list-comma-space-before": "never",
"selector-max-attribute": 0,
"selector-max-class": 3,
"selector-max-compound-selectors": 3,
"selector-max-empty-lines": 0,
"selector-max-id": 0,
"selector-max-universal": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"shorthand-property-no-redundant-values": true,
"string-no-newline": true,
"string-quotes": "single",
"time-min-milliseconds": 100,
"unit-case": "lower",
"unit-no-unknown": true,
"value-list-comma-newline-after": "never-multi-line",
"value-list-comma-newline-before": "never-multi-line",
"value-list-comma-space-after": "always",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0,
"value-no-vendor-prefix": true
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"ecmaVersion": 6,
"libs": [
"browser",
"jquery"
]
}
+35
View File
@@ -0,0 +1,35 @@
const loose = true;
module.exports = {
plugins: [
// Stage 1
'@babel/plugin-proposal-export-default-from',
['@babel/plugin-proposal-optional-chaining', { loose }],
['@babel/plugin-proposal-nullish-coalescing-operator', { loose }],
// Stage 2
'@babel/plugin-proposal-export-namespace-from',
// Stage 3
['@babel/plugin-proposal-class-properties', { loose }],
'@babel/plugin-syntax-dynamic-import'
],
env: {
development: {
presets: [
['@babel/preset-react', { development: true }]
],
plugins: [
'babel-plugin-inline-classnames'
]
},
production: {
presets: [
'@babel/preset-react'
],
plugins: [
'babel-plugin-transform-react-remove-prop-types'
]
}
}
};
+17
View File
@@ -0,0 +1,17 @@
const gulp = require('gulp');
require('./clean');
require('./copy');
require('./webpack');
gulp.task('build',
gulp.series('clean',
gulp.parallel(
'webpack',
'copyHtml',
'copyFonts',
'copyImages'
)
)
);
+8
View File
@@ -0,0 +1,8 @@
const gulp = require('gulp');
const del = require('del');
const paths = require('./helpers/paths');
gulp.task('clean', () => {
return del([paths.dest.root]);
});
+34
View File
@@ -0,0 +1,34 @@
const path = require('path');
const gulp = require('gulp');
const print = require('gulp-print').default;
const cache = require('gulp-cached');
const livereload = require('gulp-livereload');
const paths = require('./helpers/paths.js');
gulp.task('copyHtml', () => {
return gulp.src(paths.src.html, { base: paths.src.root })
.pipe(cache('copyHtml'))
.pipe(print())
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});
gulp.task('copyFonts', () => {
return gulp.src(
path.join(paths.src.fonts, '**', '*.*'), { base: paths.src.root }
)
.pipe(cache('copyFonts'))
.pipe(print())
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});
gulp.task('copyImages', () => {
return gulp.src(
path.join(paths.src.images, '**', '*.*'), { base: paths.src.root }
)
.pipe(cache('copyImages'))
.pipe(print())
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});
+5
View File
@@ -0,0 +1,5 @@
require('./build.js');
require('./clean.js');
require('./copy.js');
require('./watch.js');
require('./webpack.js');
+6
View File
@@ -0,0 +1,6 @@
const colors = require('ansi-colors');
module.exports = function errorHandler(error) {
console.log(colors.red(`Error (${error.plugin}): ${error.message}`));
this.emit('end');
};
+23
View File
@@ -0,0 +1,23 @@
const root = './frontend/src';
const paths = {
src: {
root,
html: `${root}/*.html`,
scripts: `${root}/**/*.js`,
content: `${root}/Content/`,
fonts: `${root}/Content/Fonts/`,
images: `${root}/Content/Images/`,
exclude: {
libs: `!${root}/JsLibraries/**`
}
},
dest: {
root: './_output/UI/',
content: './_output/UI/Content/',
fonts: './_output/UI/Content/Fonts/',
images: './_output/UI/Content/Images/'
}
};
module.exports = paths;
+18
View File
@@ -0,0 +1,18 @@
const gulp = require('gulp');
const livereload = require('gulp-livereload');
const gulpWatch = require('gulp-watch');
const paths = require('./helpers/paths.js');
require('./copy.js');
require('./webpack.js');
function watch() {
livereload.listen({ start: true });
gulp.task('webpackWatch')();
gulpWatch(paths.src.html, gulp.series('copyHtml'));
gulpWatch(`${paths.src.fonts}**/*.*`, gulp.series('copyFonts'));
gulpWatch(`${paths.src.images}**/*.*`, gulp.series('copyImages'));
}
gulp.task('watch', gulp.series('build', watch));
+266
View File
@@ -0,0 +1,266 @@
const gulp = require('gulp');
const webpackStream = require('webpack-stream');
const livereload = require('gulp-livereload');
const path = require('path');
const webpack = require('webpack');
const errorHandler = require('./helpers/errorHandler');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const uiFolder = 'UI';
const frontendFolder = path.join(__dirname, '..');
const srcFolder = path.join(frontendFolder, 'src');
const isProduction = process.argv.indexOf('--production') > -1;
const isProfiling = isProduction && process.argv.indexOf('--profile') > -1;
const inlineWebWorkers = true;
const distFolder = path.resolve(frontendFolder, '..', '_output', uiFolder);
console.log('Source Folder:', srcFolder);
console.log('Output Folder:', distFolder);
console.log('isProduction:', isProduction);
console.log('isProfiling:', isProfiling);
const cssVarsFiles = [
'../src/Styles/Variables/colors',
'../src/Styles/Variables/dimensions',
'../src/Styles/Variables/fonts',
'../src/Styles/Variables/animations',
'../src/Styles/Variables/zIndexes'
].map(require.resolve);
// Override the way HtmlWebpackPlugin injects the scripts
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets, assetTags) {
const head = assetTags.head.map((v) => {
v.attributes = { rel: 'stylesheet', type: 'text/css', href: `/${v.attributes.href.replace('\\', '/')}` };
return this.createHtmlTag(v);
});
const body = assetTags.body.map((v) => {
v.attributes = { src: `/${v.attributes.src}` };
return this.createHtmlTag(v);
});
return html
.replace('<!-- webpack bundles head -->', head.join('\r\n '))
.replace('<!-- webpack bundles body -->', body.join('\r\n '));
};
const plugins = [
new OptimizeCssAssetsPlugin({}),
new webpack.DefinePlugin({
__DEV__: !isProduction,
'process.env.NODE_ENV': isProduction ? JSON.stringify('production') : JSON.stringify('development')
}),
new MiniCssExtractPlugin({
filename: path.join('Content', 'styles.css')
}),
new HtmlWebpackPlugin({
template: 'frontend/src/index.html',
filename: 'index.html'
})
];
const config = {
mode: isProduction ? 'production' : 'development',
devtool: '#source-map',
stats: {
children: false
},
watchOptions: {
ignored: /node_modules/
},
entry: {
index: 'index.js'
},
resolve: {
modules: [
srcFolder,
path.join(srcFolder, 'Shims'),
'node_modules'
],
alias: {
jquery: 'jquery/src/jquery'
}
},
output: {
path: distFolder,
filename: '[name].js',
sourceMapFilename: '[file].map'
},
optimization: {
chunkIds: 'named',
splitChunks: {
chunks: 'initial'
}
},
performance: {
hints: false
},
plugins,
resolveLoader: {
modules: [
'node_modules',
'frontend/gulp/webpack/'
]
},
module: {
rules: [
{
test: /\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
name: '[name].js',
inline: inlineWebWorkers,
fallback: !inlineWebWorkers
}
}
},
{
test: /\.js?$/,
exclude: /(node_modules|JsLibraries)/,
use: [
{
loader: 'babel-loader',
options: {
configFile: `${frontendFolder}/babel.config.js`,
envName: isProduction ? 'production' : 'development',
presets: [
[
'@babel/preset-env',
{
modules: false,
loose: true,
debug: false,
useBuiltIns: 'entry',
corejs: 3
}
]
]
}
}
]
},
// CSS Modules
{
test: /\.css$/,
exclude: /(node_modules|globals.css)/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: {
localIdentName: '[name]/[local]/[hash:base64:5]'
}
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
config: {
ctx: {
cssVarsFiles
},
path: 'frontend/postcss.config.js'
}
}
}
]
},
// Global styles
{
test: /\.css$/,
include: /(node_modules|globals.css)/,
use: [
'style-loader',
{
loader: 'css-loader'
}
]
},
// Fonts
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
mimetype: 'application/font-woff',
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
}
}
]
},
{
test: /\.(ttf|eot|eot?#iefix|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
emitFile: false,
name: 'Content/Fonts/[name].[ext]'
}
}
]
}
]
}
};
if (isProfiling) {
config.resolve.alias['react-dom$'] = 'react-dom/profiling';
config.resolve.alias['scheduler/tracing'] = 'scheduler/tracing-profiling';
config.optimization.minimizer = [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
mangle: false,
keep_classnames: true,
keep_fnames: true
}
})
];
}
gulp.task('webpack', () => {
return webpackStream(config)
.pipe(gulp.dest('_output/UI'));
});
gulp.task('webpackWatch', () => {
config.watch = true;
return webpackStream(config, webpack)
.on('error', errorHandler)
.pipe(gulp.dest('_output/UI'))
.on('error', errorHandler)
.pipe(livereload())
.on('error', errorHandler);
});
@@ -0,0 +1,11 @@
const loaderUtils = require('loader-utils');
module.exports = function cssVariablesLoader(source) {
const options = loaderUtils.getOptions(this);
options.cssVarsFiles.forEach((cssVarsFile) => {
this.addDependency(cssVarsFile);
});
return source;
};
+23
View File
@@ -0,0 +1,23 @@
const reload = require('require-nocache')(module);
module.exports = (ctx, configPath, options) => {
const config = {
plugins: {
'postcss-mixins': {
mixinsDir: [
'frontend/src/Styles/Mixins'
]
},
'postcss-simple-vars': {
variables: () =>
ctx.options.cssVarsFiles.reduce((acc, vars) => {
return Object.assign(acc, reload(vars));
}, {})
},
'postcss-color-function': {},
'postcss-nested': {}
}
};
return config;
};
+4
View File
@@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.insertFinalNewline": true
}
@@ -0,0 +1,124 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import BlacklistRowConnector from './BlacklistRowConnector';
class Blacklist extends Component {
//
// Render
render() {
const {
isFetching,
isPopulated,
error,
items,
columns,
totalRecords,
isClearingBlacklistExecuting,
onClearBlacklistPress,
...otherProps
} = this.props;
return (
<PageContent title="Blacklist">
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label={translate('Clear')}
iconName={icons.CLEAR}
isSpinning={isClearingBlacklistExecuting}
onPress={onClearBlacklistPress}
/>
</PageToolbarSection>
<PageToolbarSection alignContent={align.RIGHT}>
<TableOptionsModalWrapper
{...otherProps}
columns={columns}
>
<PageToolbarButton
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
</PageToolbarSection>
</PageToolbar>
<PageContentBody>
{
isFetching && !isPopulated &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<div>Unable to load blacklist</div>
}
{
isPopulated && !error && !items.length &&
<div>
No history blacklist
</div>
}
{
isPopulated && !error && !!items.length &&
<div>
<Table
columns={columns}
{...otherProps}
>
<TableBody>
{
items.map((item) => {
return (
<BlacklistRowConnector
key={item.id}
columns={columns}
{...item}
/>
);
})
}
</TableBody>
</Table>
<TablePager
totalRecords={totalRecords}
isFetching={isFetching}
{...otherProps}
/>
</div>
}
</PageContentBody>
</PageContent>
);
}
}
Blacklist.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
onClearBlacklistPress: PropTypes.func.isRequired
};
export default Blacklist;
@@ -0,0 +1,154 @@
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 withCurrentPage from 'Components/withCurrentPage';
import * as blacklistActions from 'Store/Actions/blacklistActions';
import { executeCommand } from 'Store/Actions/commandActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
import Blacklist from './Blacklist';
function createMapStateToProps() {
return createSelector(
(state) => state.blacklist,
createCommandExecutingSelector(commandNames.CLEAR_BLACKLIST),
(blacklist, isClearingBlacklistExecuting) => {
return {
isClearingBlacklistExecuting,
...blacklist
};
}
);
}
const mapDispatchToProps = {
...blacklistActions,
executeCommand
};
class BlacklistConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
useCurrentPage,
fetchBlacklist,
gotoBlacklistFirstPage
} = this.props;
registerPagePopulator(this.repopulate);
if (useCurrentPage) {
fetchBlacklist();
} else {
gotoBlacklistFirstPage();
}
}
componentDidUpdate(prevProps) {
if (prevProps.isClearingBlacklistExecuting && !this.props.isClearingBlacklistExecuting) {
this.props.gotoBlacklistFirstPage();
}
}
componentWillUnmount() {
this.props.clearBlacklist();
unregisterPagePopulator(this.repopulate);
}
//
// Control
repopulate = () => {
this.props.fetchBlacklist();
}
//
// Listeners
onFirstPagePress = () => {
this.props.gotoBlacklistFirstPage();
}
onPreviousPagePress = () => {
this.props.gotoBlacklistPreviousPage();
}
onNextPagePress = () => {
this.props.gotoBlacklistNextPage();
}
onLastPagePress = () => {
this.props.gotoBlacklistLastPage();
}
onPageSelect = (page) => {
this.props.gotoBlacklistPage({ page });
}
onSortPress = (sortKey) => {
this.props.setBlacklistSort({ sortKey });
}
onTableOptionChange = (payload) => {
this.props.setBlacklistTableOption(payload);
if (payload.pageSize) {
this.props.gotoBlacklistFirstPage();
}
}
onClearBlacklistPress = () => {
this.props.executeCommand({ name: commandNames.CLEAR_BLACKLIST });
}
onTableOptionChange = (payload) => {
this.props.setBlacklistTableOption(payload);
if (payload.pageSize) {
this.props.gotoBlacklistFirstPage();
}
}
//
// Render
render() {
return (
<Blacklist
onFirstPagePress={this.onFirstPagePress}
onPreviousPagePress={this.onPreviousPagePress}
onNextPagePress={this.onNextPagePress}
onLastPagePress={this.onLastPagePress}
onPageSelect={this.onPageSelect}
onSortPress={this.onSortPress}
onTableOptionChange={this.onTableOptionChange}
onClearBlacklistPress={this.onClearBlacklistPress}
{...this.props}
/>
);
}
}
BlacklistConnector.propTypes = {
useCurrentPage: PropTypes.bool.isRequired,
isClearingBlacklistExecuting: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchBlacklist: PropTypes.func.isRequired,
gotoBlacklistFirstPage: PropTypes.func.isRequired,
gotoBlacklistPreviousPage: PropTypes.func.isRequired,
gotoBlacklistNextPage: PropTypes.func.isRequired,
gotoBlacklistLastPage: PropTypes.func.isRequired,
gotoBlacklistPage: PropTypes.func.isRequired,
setBlacklistSort: PropTypes.func.isRequired,
setBlacklistTableOption: PropTypes.func.isRequired,
clearBlacklist: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
};
export default withCurrentPage(
connect(createMapStateToProps, mapDispatchToProps)(BlacklistConnector)
);
@@ -0,0 +1,89 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
import ModalBody from 'Components/Modal/ModalBody';
import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
class BlacklistDetailsModal extends Component {
//
// Render
render() {
const {
isOpen,
sourceTitle,
protocol,
indexer,
message,
onModalClose
} = this.props;
return (
<Modal
isOpen={isOpen}
onModalClose={onModalClose}
>
<ModalContent
onModalClose={onModalClose}
>
<ModalHeader>
Details
</ModalHeader>
<ModalBody>
<DescriptionList>
<DescriptionListItem
title="Name"
data={sourceTitle}
/>
<DescriptionListItem
title="Protocol"
data={protocol}
/>
{
!!message &&
<DescriptionListItem
title="Indexer"
data={indexer}
/>
}
{
!!message &&
<DescriptionListItem
title="Message"
data={message}
/>
}
</DescriptionList>
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
BlacklistDetailsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
protocol: PropTypes.string.isRequired,
indexer: PropTypes.string,
message: PropTypes.string,
onModalClose: PropTypes.func.isRequired
};
export default BlacklistDetailsModal;
@@ -0,0 +1,18 @@
.language,
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 100px;
}
.indexer {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
}
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 70px;
}
@@ -0,0 +1,200 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import IconButton from 'Components/Link/IconButton';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import { icons, kinds } from 'Helpers/Props';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import MovieTitleLink from 'Movie/MovieTitleLink';
import BlacklistDetailsModal from './BlacklistDetailsModal';
import styles from './BlacklistRow.css';
class BlacklistRow extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isDetailsModalOpen: false
};
}
//
// Listeners
onDetailsPress = () => {
this.setState({ isDetailsModalOpen: true });
}
onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false });
}
//
// Render
render() {
const {
movie,
sourceTitle,
quality,
customFormats,
languages,
date,
protocol,
indexer,
message,
columns,
onRemovePress
} = this.props;
if (!movie) {
return null;
}
return (
<TableRow>
{
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'movies.sortTitle') {
return (
<TableRowCell key={name}>
<MovieTitleLink
titleSlug={movie.titleSlug}
title={movie.title}
/>
</TableRowCell>
);
}
if (name === 'sourceTitle') {
return (
<TableRowCell key={name}>
{sourceTitle}
</TableRowCell>
);
}
if (name === 'languages') {
return (
<TableRowCell key={name}>
<MovieLanguage
languages={languages}
/>
</TableRowCell>
);
}
if (name === 'quality') {
return (
<TableRowCell
key={name}
className={styles.quality}
>
<MovieQuality
quality={quality}
/>
</TableRowCell>
);
}
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<MovieFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'date') {
return (
<RelativeDateCellConnector
key={name}
date={date}
/>
);
}
if (name === 'indexer') {
return (
<TableRowCell
key={name}
className={styles.indexer}
>
{indexer}
</TableRowCell>
);
}
if (name === 'actions') {
return (
<TableRowCell
key={name}
className={styles.actions}
>
<IconButton
name={icons.INFO}
onPress={this.onDetailsPress}
/>
<IconButton
title="Remove from blacklist"
name={icons.REMOVE}
kind={kinds.DANGER}
onPress={onRemovePress}
/>
</TableRowCell>
);
}
return null;
})
}
<BlacklistDetailsModal
isOpen={this.state.isDetailsModalOpen}
sourceTitle={sourceTitle}
protocol={protocol}
indexer={indexer}
message={message}
onModalClose={this.onDetailsModalClose}
/>
</TableRow>
);
}
}
BlacklistRow.propTypes = {
id: PropTypes.number.isRequired,
movie: PropTypes.object.isRequired,
sourceTitle: PropTypes.string.isRequired,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
date: PropTypes.string.isRequired,
protocol: PropTypes.string.isRequired,
indexer: PropTypes.string,
message: PropTypes.string,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onRemovePress: PropTypes.func.isRequired
};
export default BlacklistRow;
@@ -0,0 +1,26 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { removeFromBlacklist } from 'Store/Actions/blacklistActions';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import BlacklistRow from './BlacklistRow';
function createMapStateToProps() {
return createSelector(
createMovieSelector(),
(movie) => {
return {
movie
};
}
);
}
function createMapDispatchToProps(dispatch, props) {
return {
onRemovePress() {
dispatch(removeFromBlacklist({ id: props.id }));
}
};
}
export default connect(createMapStateToProps, createMapDispatchToProps)(BlacklistRow);
@@ -0,0 +1,5 @@
.description {
composes: description from '~Components/DescriptionList/DescriptionListItemDescription.css';
overflow-wrap: break-word;
}
@@ -0,0 +1,278 @@
import PropTypes from 'prop-types';
import React from 'react';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
import Link from 'Components/Link/Link';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatAge from 'Utilities/Number/formatAge';
import styles from './HistoryDetails.css';
function HistoryDetails(props) {
const {
eventType,
sourceTitle,
data,
shortDateFormat,
timeFormat
} = props;
if (eventType === 'grabbed') {
const {
indexer,
releaseGroup,
nzbInfoUrl,
downloadClient,
downloadId,
age,
ageHours,
ageMinutes,
publishedDate
} = data;
return (
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
data={sourceTitle}
/>
{
!!indexer &&
<DescriptionListItem
title="Indexer"
data={indexer}
/>
}
{
!!releaseGroup &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Release Group"
data={releaseGroup}
/>
}
{
!!nzbInfoUrl &&
<span>
<DescriptionListItemTitle>
Info URL
</DescriptionListItemTitle>
<DescriptionListItemDescription>
<Link to={nzbInfoUrl}>{nzbInfoUrl}</Link>
</DescriptionListItemDescription>
</span>
}
{
!!downloadClient &&
<DescriptionListItem
title="Download Client"
data={downloadClient}
/>
}
{
!!downloadId &&
<DescriptionListItem
title="Grab ID"
data={downloadId}
/>
}
{
!!indexer &&
<DescriptionListItem
title="Age (when grabbed)"
data={formatAge(age, ageHours, ageMinutes)}
/>
}
{
!!publishedDate &&
<DescriptionListItem
title="Published Date"
data={formatDateTime(publishedDate, shortDateFormat, timeFormat, { includeSeconds: true })}
/>
}
</DescriptionList>
);
}
if (eventType === 'downloadFailed') {
const {
message
} = data;
return (
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
data={sourceTitle}
/>
{
!!message &&
<DescriptionListItem
title="Message"
data={message}
/>
}
</DescriptionList>
);
}
if (eventType === 'downloadFolderImported') {
const {
droppedPath,
importedPath
} = data;
return (
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
data={sourceTitle}
/>
{
!!droppedPath &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Source"
data={droppedPath}
/>
}
{
!!importedPath &&
<DescriptionListItem
descriptionClassName={styles.description}
title="Imported To"
data={importedPath}
/>
}
</DescriptionList>
);
}
if (eventType === 'movieFileDeleted') {
const {
reason
} = data;
let reasonMessage = '';
switch (reason) {
case 'Manual':
reasonMessage = 'File was deleted by via UI';
break;
case 'MissingFromDisk':
reasonMessage = 'Radarr was unable to find the file on disk so it was removed';
break;
case 'Upgrade':
reasonMessage = 'File was deleted to import an upgrade';
break;
default:
reasonMessage = '';
}
return (
<DescriptionList>
<DescriptionListItem
title="Name"
data={sourceTitle}
/>
<DescriptionListItem
title="Reason"
data={reasonMessage}
/>
</DescriptionList>
);
}
if (eventType === 'movieFileRenamed') {
const {
sourcePath,
sourceRelativePath,
path,
relativePath
} = data;
return (
<DescriptionList>
<DescriptionListItem
title="Source Path"
data={sourcePath}
/>
<DescriptionListItem
title="Source Relative Path"
data={sourceRelativePath}
/>
<DescriptionListItem
title="Destination Path"
data={path}
/>
<DescriptionListItem
title="Destination Relative Path"
data={relativePath}
/>
</DescriptionList>
);
}
if (eventType === 'downloadIgnored') {
const {
message
} = data;
return (
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
data={sourceTitle}
/>
{
!!message &&
<DescriptionListItem
title="Message"
data={message}
/>
}
</DescriptionList>
);
}
return (
<DescriptionList>
<DescriptionListItem
descriptionClassName={styles.description}
title="Name"
data={sourceTitle}
/>
</DescriptionList>
);
}
HistoryDetails.propTypes = {
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired
};
export default HistoryDetails;
@@ -0,0 +1,19 @@
import _ from 'lodash';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import HistoryDetails from './HistoryDetails';
function createMapStateToProps() {
return createSelector(
createUISettingsSelector(),
(uiSettings) => {
return _.pick(uiSettings, [
'shortDateFormat',
'timeFormat'
]);
}
);
}
export default connect(createMapStateToProps)(HistoryDetails);
@@ -0,0 +1,5 @@
.markAsFailedButton {
composes: button from '~Components/Link/Button.css';
margin-right: auto;
}
@@ -0,0 +1,106 @@
import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import SpinnerButton from 'Components/Link/SpinnerButton';
import Modal from 'Components/Modal/Modal';
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 { kinds } from 'Helpers/Props';
import HistoryDetails from './HistoryDetails';
import styles from './HistoryDetailsModal.css';
function getHeaderTitle(eventType) {
switch (eventType) {
case 'grabbed':
return 'Grabbed';
case 'downloadFailed':
return 'Download Failed';
case 'downloadFolderImported':
return 'Movie Imported';
case 'movieFileDeleted':
return 'Movie File Deleted';
case 'movieFileRenamed':
return 'Movie File Renamed';
case 'downloadIgnored':
return 'Download Ignored';
default:
return 'Unknown';
}
}
function HistoryDetailsModal(props) {
const {
isOpen,
eventType,
sourceTitle,
data,
isMarkingAsFailed,
shortDateFormat,
timeFormat,
onMarkAsFailedPress,
onModalClose
} = props;
return (
<Modal
isOpen={isOpen}
onModalClose={onModalClose}
>
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{getHeaderTitle(eventType)}
</ModalHeader>
<ModalBody>
<HistoryDetails
eventType={eventType}
sourceTitle={sourceTitle}
data={data}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
/>
</ModalBody>
<ModalFooter>
{
eventType === 'grabbed' &&
<SpinnerButton
className={styles.markAsFailedButton}
kind={kinds.DANGER}
isSpinning={isMarkingAsFailed}
onPress={onMarkAsFailedPress}
>
Mark as Failed
</SpinnerButton>
}
<Button
onPress={onModalClose}
>
Close
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
HistoryDetailsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
isMarkingAsFailed: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
onMarkAsFailedPress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
HistoryDetailsModal.defaultProps = {
isMarkingAsFailed: false
};
export default HistoryDetailsModal;
+151
View File
@@ -0,0 +1,151 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import FilterMenu from 'Components/Menu/FilterMenu';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import HistoryRowConnector from './HistoryRowConnector';
class History extends Component {
//
// Render
render() {
const {
isFetching,
isPopulated,
error,
isMoviesFetching,
isMoviesPopulated,
moviesError,
items,
columns,
selectedFilterKey,
filters,
totalRecords,
onFilterSelect,
onFirstPagePress,
...otherProps
} = this.props;
const isFetchingAny = isFetching || isMoviesFetching;
const isAllPopulated = isPopulated && (isMoviesPopulated || !items.length);
const hasError = error || moviesError;
return (
<PageContent title="History">
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label={translate('Refresh')}
iconName={icons.REFRESH}
isSpinning={isFetching}
onPress={onFirstPagePress}
/>
</PageToolbarSection>
<PageToolbarSection alignContent={align.RIGHT}>
<TableOptionsModalWrapper
{...otherProps}
columns={columns}
>
<PageToolbarButton
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
<FilterMenu
alignMenu={align.RIGHT}
selectedFilterKey={selectedFilterKey}
filters={filters}
customFilters={[]}
onFilterSelect={onFilterSelect}
/>
</PageToolbarSection>
</PageToolbar>
<PageContentBody>
{
isFetchingAny && !isAllPopulated &&
<LoadingIndicator />
}
{
!isFetchingAny && hasError &&
<div>Unable to load history</div>
}
{
// If history isPopulated and it's empty show no history found and don't
// wait for the episodes to populate because they are never coming.
isPopulated && !hasError && !items.length &&
<div>
No history found
</div>
}
{
isAllPopulated && !hasError && !!items.length &&
<div>
<Table
columns={columns}
{...otherProps}
>
<TableBody>
{
items.map((item) => {
return (
<HistoryRowConnector
key={item.id}
columns={columns}
{...item}
/>
);
})
}
</TableBody>
</Table>
<TablePager
totalRecords={totalRecords}
isFetching={isFetching}
onFirstPagePress={onFirstPagePress}
{...otherProps}
/>
</div>
}
</PageContentBody>
</PageContent>
);
}
}
History.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
isMoviesFetching: PropTypes.bool.isRequired,
isMoviesPopulated: PropTypes.bool.isRequired,
moviesError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
selectedFilterKey: PropTypes.string.isRequired,
filters: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
onFilterSelect: PropTypes.func.isRequired,
onFirstPagePress: PropTypes.func.isRequired
};
export default History;
@@ -0,0 +1,138 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import withCurrentPage from 'Components/withCurrentPage';
import * as historyActions from 'Store/Actions/historyActions';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
import History from './History';
function createMapStateToProps() {
return createSelector(
(state) => state.history,
(state) => state.movies,
(history, movies) => {
return {
isMoviesFetching: movies.isFetching,
isMoviesPopulated: movies.isPopulated,
moviesError: movies.error,
...history
};
}
);
}
const mapDispatchToProps = {
...historyActions
};
class HistoryConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
useCurrentPage,
fetchHistory,
gotoHistoryFirstPage
} = this.props;
registerPagePopulator(this.repopulate);
if (useCurrentPage) {
fetchHistory();
} else {
gotoHistoryFirstPage();
}
}
componentWillUnmount() {
unregisterPagePopulator(this.repopulate);
this.props.clearHistory();
}
//
// Control
repopulate = () => {
this.props.fetchHistory();
}
//
// Listeners
onFirstPagePress = () => {
this.props.gotoHistoryFirstPage();
}
onPreviousPagePress = () => {
this.props.gotoHistoryPreviousPage();
}
onNextPagePress = () => {
this.props.gotoHistoryNextPage();
}
onLastPagePress = () => {
this.props.gotoHistoryLastPage();
}
onPageSelect = (page) => {
this.props.gotoHistoryPage({ page });
}
onSortPress = (sortKey) => {
this.props.setHistorySort({ sortKey });
}
onFilterSelect = (selectedFilterKey) => {
this.props.setHistoryFilter({ selectedFilterKey });
}
onTableOptionChange = (payload) => {
this.props.setHistoryTableOption(payload);
if (payload.pageSize) {
this.props.gotoHistoryFirstPage();
}
}
//
// Render
render() {
return (
<History
onFirstPagePress={this.onFirstPagePress}
onPreviousPagePress={this.onPreviousPagePress}
onNextPagePress={this.onNextPagePress}
onLastPagePress={this.onLastPagePress}
onPageSelect={this.onPageSelect}
onSortPress={this.onSortPress}
onFilterSelect={this.onFilterSelect}
onTableOptionChange={this.onTableOptionChange}
{...this.props}
/>
);
}
}
HistoryConnector.propTypes = {
useCurrentPage: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchHistory: PropTypes.func.isRequired,
gotoHistoryFirstPage: PropTypes.func.isRequired,
gotoHistoryPreviousPage: PropTypes.func.isRequired,
gotoHistoryNextPage: PropTypes.func.isRequired,
gotoHistoryLastPage: PropTypes.func.isRequired,
gotoHistoryPage: PropTypes.func.isRequired,
setHistorySort: PropTypes.func.isRequired,
setHistoryFilter: PropTypes.func.isRequired,
setHistoryTableOption: PropTypes.func.isRequired,
clearHistory: PropTypes.func.isRequired
};
export default withCurrentPage(
connect(createMapStateToProps, mapDispatchToProps)(HistoryConnector)
);
@@ -0,0 +1,6 @@
.cell {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 35px;
text-align: center;
}
@@ -0,0 +1,86 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import { icons, kinds } from 'Helpers/Props';
import styles from './HistoryEventTypeCell.css';
function getIconName(eventType) {
switch (eventType) {
case 'grabbed':
return icons.DOWNLOADING;
case 'movieFolderImported':
return icons.DRIVE;
case 'downloadFolderImported':
return icons.DOWNLOADED;
case 'downloadFailed':
return icons.DOWNLOADING;
case 'movieFileDeleted':
return icons.DELETE;
case 'movieFileRenamed':
return icons.ORGANIZE;
case 'downloadIgnored':
return icons.IGNORE;
default:
return icons.UNKNOWN;
}
}
function getIconKind(eventType) {
switch (eventType) {
case 'downloadFailed':
return kinds.DANGER;
default:
return kinds.DEFAULT;
}
}
function getTooltip(eventType, data) {
switch (eventType) {
case 'grabbed':
return `Movie grabbed from ${data.indexer} and sent to ${data.downloadClient}`;
case 'movieFolderImported':
return 'Movie imported from movie folder';
case 'downloadFolderImported':
return 'Movie downloaded successfully and picked up from download client';
case 'downloadFailed':
return 'Movie download failed';
case 'movieFileDeleted':
return 'Movie file deleted';
case 'movieFileRenamed':
return 'Movie file renamed';
case 'downloadIgnored':
return 'Movie Download Ignored';
default:
return 'Unknown event';
}
}
function HistoryEventTypeCell({ eventType, data }) {
const iconName = getIconName(eventType);
const iconKind = getIconKind(eventType);
const tooltip = getTooltip(eventType, data);
return (
<TableRowCell
className={styles.cell}
title={tooltip}
>
<Icon
name={iconName}
kind={iconKind}
/>
</TableRowCell>
);
}
HistoryEventTypeCell.propTypes = {
eventType: PropTypes.string.isRequired,
data: PropTypes.object
};
HistoryEventTypeCell.defaultProps = {
data: {}
};
export default HistoryEventTypeCell;
@@ -0,0 +1,23 @@
.downloadClient {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 120px;
}
.indexer {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 80px;
}
.releaseGroup {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 110px;
}
.details {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 30px;
}
+236
View File
@@ -0,0 +1,236 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import IconButton from 'Components/Link/IconButton';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableRow from 'Components/Table/TableRow';
import { icons } from 'Helpers/Props';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import MovieTitleLink from 'Movie/MovieTitleLink';
import HistoryDetailsModal from './Details/HistoryDetailsModal';
import HistoryEventTypeCell from './HistoryEventTypeCell';
import styles from './HistoryRow.css';
class HistoryRow extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isDetailsModalOpen: false
};
}
componentDidUpdate(prevProps) {
if (
prevProps.isMarkingAsFailed &&
!this.props.isMarkingAsFailed &&
!this.props.markAsFailedError
) {
this.setState({ isDetailsModalOpen: false });
}
}
//
// Listeners
onDetailsPress = () => {
this.setState({ isDetailsModalOpen: true });
}
onDetailsModalClose = () => {
this.setState({ isDetailsModalOpen: false });
}
//
// Render
render() {
const {
movie,
quality,
customFormats,
languages,
qualityCutoffNotMet,
eventType,
sourceTitle,
date,
data,
isMarkingAsFailed,
columns,
shortDateFormat,
timeFormat,
onMarkAsFailedPress
} = this.props;
if (!movie) {
return null;
}
return (
<TableRow>
{
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'eventType') {
return (
<HistoryEventTypeCell
key={name}
eventType={eventType}
data={data}
/>
);
}
if (name === 'movies.sortTitle') {
return (
<TableRowCell key={name}>
<MovieTitleLink
titleSlug={movie.titleSlug}
title={movie.title}
/>
</TableRowCell>
);
}
if (name === 'languages') {
return (
<TableRowCell key={name}>
<MovieLanguage
languages={languages}
/>
</TableRowCell>
);
}
if (name === 'quality') {
return (
<TableRowCell key={name}>
<MovieQuality
quality={quality}
isCutoffMet={qualityCutoffNotMet}
/>
</TableRowCell>
);
}
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<MovieFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'date') {
return (
<RelativeDateCellConnector
key={name}
date={date}
/>
);
}
if (name === 'downloadClient') {
return (
<TableRowCell
key={name}
className={styles.downloadClient}
>
{data.downloadClient}
</TableRowCell>
);
}
if (name === 'indexer') {
return (
<TableRowCell
key={name}
className={styles.indexer}
>
{data.indexer}
</TableRowCell>
);
}
if (name === 'releaseGroup') {
return (
<TableRowCell
key={name}
className={styles.releaseGroup}
>
{data.releaseGroup}
</TableRowCell>
);
}
if (name === 'details') {
return (
<TableRowCell
key={name}
className={styles.details}
>
<IconButton
name={icons.INFO}
onPress={this.onDetailsPress}
/>
</TableRowCell>
);
}
return null;
})
}
<HistoryDetailsModal
isOpen={this.state.isDetailsModalOpen}
eventType={eventType}
sourceTitle={sourceTitle}
data={data}
isMarkingAsFailed={isMarkingAsFailed}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
onMarkAsFailedPress={onMarkAsFailedPress}
onModalClose={this.onDetailsModalClose}
/>
</TableRow>
);
}
}
HistoryRow.propTypes = {
movieId: PropTypes.number,
movie: PropTypes.object.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
quality: PropTypes.object.isRequired,
qualityCutoffNotMet: PropTypes.bool.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
eventType: PropTypes.string.isRequired,
sourceTitle: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
isMarkingAsFailed: PropTypes.bool,
markAsFailedError: PropTypes.object,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
onMarkAsFailedPress: PropTypes.func.isRequired
};
export default HistoryRow;
@@ -0,0 +1,73 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { fetchHistory, markAsFailed } from 'Store/Actions/historyActions';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import HistoryRow from './HistoryRow';
function createMapStateToProps() {
return createSelector(
createMovieSelector(),
createUISettingsSelector(),
(movie, uiSettings) => {
return {
movie,
shortDateFormat: uiSettings.shortDateFormat,
timeFormat: uiSettings.timeFormat
};
}
);
}
const mapDispatchToProps = {
fetchHistory,
markAsFailed
};
class HistoryRowConnector extends Component {
//
// Lifecycle
componentDidUpdate(prevProps) {
if (
prevProps.isMarkingAsFailed &&
!this.props.isMarkingAsFailed &&
!this.props.markAsFailedError
) {
this.props.fetchHistory();
}
}
//
// Listeners
onMarkAsFailedPress = () => {
this.props.markAsFailed({ id: this.props.id });
}
//
// Render
render() {
return (
<HistoryRow
{...this.props}
onMarkAsFailedPress={this.onMarkAsFailedPress}
/>
);
}
}
HistoryRowConnector.propTypes = {
id: PropTypes.number.isRequired,
isMarkingAsFailed: PropTypes.bool,
markAsFailedError: PropTypes.object,
fetchHistory: PropTypes.func.isRequired,
markAsFailed: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(HistoryRowConnector);
@@ -0,0 +1,13 @@
.torrent {
composes: label from '~Components/Label.css';
border-color: $torrentColor;
background-color: $torrentColor;
}
.usenet {
composes: label from '~Components/Label.css';
border-color: $usenetColor;
background-color: $usenetColor;
}
@@ -0,0 +1,20 @@
import PropTypes from 'prop-types';
import React from 'react';
import Label from 'Components/Label';
import styles from './ProtocolLabel.css';
function ProtocolLabel({ protocol }) {
const protocolName = protocol === 'usenet' ? 'nzb' : protocol;
return (
<Label className={styles[protocol]}>
{protocolName}
</Label>
);
}
ProtocolLabel.propTypes = {
protocol: PropTypes.string.isRequired
};
export default ProtocolLabel;
+292
View File
@@ -0,0 +1,292 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import PageToolbar from 'Components/Page/Toolbar/PageToolbar';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import PageToolbarSection from 'Components/Page/Toolbar/PageToolbarSection';
import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody';
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
import TablePager from 'Components/Table/TablePager';
import { align, icons } from 'Helpers/Props';
import getRemovedItems from 'Utilities/Object/getRemovedItems';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import translate from 'Utilities/String/translate';
import getSelectedIds from 'Utilities/Table/getSelectedIds';
import removeOldSelectedState from 'Utilities/Table/removeOldSelectedState';
import selectAll from 'Utilities/Table/selectAll';
import toggleSelected from 'Utilities/Table/toggleSelected';
import QueueOptionsConnector from './QueueOptionsConnector';
import QueueRowConnector from './QueueRowConnector';
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
class Queue extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
allSelected: false,
allUnselected: false,
lastToggled: null,
selectedState: {},
isPendingSelected: false,
isConfirmRemoveModalOpen: false,
items: props.items
};
}
componentDidUpdate(prevProps) {
const {
items,
isFetching,
isMoviesFetching
} = this.props;
if (
(!isMoviesFetching && prevProps.isMoviesFetching) ||
(!isFetching && prevProps.isFetching) ||
(hasDifferentItems(prevProps.items, items) && !items.some((e) => e.movieId))
) {
this.setState((state) => {
return {
...removeOldSelectedState(state, getRemovedItems(prevProps.items, items)),
items
};
});
return;
}
const selectedIds = this.getSelectedIds();
const isPendingSelected = _.some(this.props.items, (item) => {
return selectedIds.indexOf(item.id) > -1 && item.status === 'delay';
});
if (isPendingSelected !== this.state.isPendingSelected) {
this.setState({ isPendingSelected });
}
}
//
// Control
getSelectedIds = () => {
return getSelectedIds(this.state.selectedState);
}
//
// Listeners
onSelectAllChange = ({ value }) => {
this.setState(selectAll(this.state.selectedState, value));
}
onSelectedChange = ({ id, value, shiftKey = false }) => {
this.setState((state) => {
return toggleSelected(state, this.props.items, id, value, shiftKey);
});
}
onGrabSelectedPress = () => {
this.props.onGrabSelectedPress(this.getSelectedIds());
}
onRemoveSelectedPress = () => {
this.setState({ isConfirmRemoveModalOpen: true });
}
onRemoveSelectedConfirmed = (payload) => {
this.props.onRemoveSelectedPress({ ids: this.getSelectedIds(), ...payload });
this.setState({ isConfirmRemoveModalOpen: false });
}
onConfirmRemoveModalClose = () => {
this.setState({ isConfirmRemoveModalOpen: false });
}
//
// Render
render() {
const {
isFetching,
isPopulated,
error,
isMoviesFetching,
isMoviesPopulated,
moviesError,
columns,
totalRecords,
isGrabbing,
isRemoving,
isRefreshMonitoredDownloadsExecuting,
onRefreshPress,
...otherProps
} = this.props;
const {
allSelected,
allUnselected,
selectedState,
isConfirmRemoveModalOpen,
isPendingSelected,
items
} = this.state;
const isRefreshing = isFetching || isMoviesFetching || isRefreshMonitoredDownloadsExecuting;
const isAllPopulated = isPopulated && (isMoviesPopulated || !items.length || items.every((e) => !e.movieId));
const hasError = error || moviesError;
const selectedIds = this.getSelectedIds();
const selectedCount = selectedIds.length;
const disableSelectedActions = selectedCount === 0;
return (
<PageContent title="Queue">
<PageToolbar>
<PageToolbarSection>
<PageToolbarButton
label={translate('Refresh')}
iconName={icons.REFRESH}
isSpinning={isRefreshing}
onPress={onRefreshPress}
/>
<PageToolbarSeparator />
<PageToolbarButton
label="Grab Selected"
iconName={icons.DOWNLOAD}
isDisabled={disableSelectedActions || !isPendingSelected}
isSpinning={isGrabbing}
onPress={this.onGrabSelectedPress}
/>
<PageToolbarButton
label="Remove Selected"
iconName={icons.REMOVE}
isDisabled={disableSelectedActions}
isSpinning={isRemoving}
onPress={this.onRemoveSelectedPress}
/>
</PageToolbarSection>
<PageToolbarSection
alignContent={align.RIGHT}
>
<TableOptionsModalWrapper
columns={columns}
{...otherProps}
optionsComponent={QueueOptionsConnector}
>
<PageToolbarButton
label={translate('Options')}
iconName={icons.TABLE}
/>
</TableOptionsModalWrapper>
</PageToolbarSection>
</PageToolbar>
<PageContentBody>
{
isRefreshing && !isAllPopulated &&
<LoadingIndicator />
}
{
!isRefreshing && hasError &&
<div>
Failed to load Queue
</div>
}
{
isPopulated && !hasError && !items.length &&
<div>
Queue is empty
</div>
}
{
isAllPopulated && !hasError && !!items.length &&
<div>
<Table
columns={columns}
selectAll={true}
allSelected={allSelected}
allUnselected={allUnselected}
{...otherProps}
optionsComponent={QueueOptionsConnector}
onSelectAllChange={this.onSelectAllChange}
>
<TableBody>
{
items.map((item) => {
return (
<QueueRowConnector
key={item.id}
movieId={item.movieId}
isSelected={selectedState[item.id]}
columns={columns}
{...item}
onSelectedChange={this.onSelectedChange}
/>
);
})
}
</TableBody>
</Table>
<TablePager
totalRecords={totalRecords}
isFetching={isRefreshing}
{...otherProps}
/>
</div>
}
</PageContentBody>
<RemoveQueueItemsModal
isOpen={isConfirmRemoveModalOpen}
selectedCount={selectedCount}
canIgnore={isConfirmRemoveModalOpen && (
selectedIds.every((id) => {
const item = items.find((i) => i.id === id);
return !!(item && item.movieId);
})
)}
onRemovePress={this.onRemoveSelectedConfirmed}
onModalClose={this.onConfirmRemoveModalClose}
/>
</PageContent>
);
}
}
Queue.propTypes = {
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
isMoviesFetching: PropTypes.bool.isRequired,
isMoviesPopulated: PropTypes.bool.isRequired,
moviesError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
totalRecords: PropTypes.number,
isGrabbing: PropTypes.bool.isRequired,
isRemoving: PropTypes.bool.isRequired,
isRefreshMonitoredDownloadsExecuting: PropTypes.bool.isRequired,
onRefreshPress: PropTypes.func.isRequired,
onGrabSelectedPress: PropTypes.func.isRequired,
onRemoveSelectedPress: PropTypes.func.isRequired
};
export default Queue;
@@ -0,0 +1,170 @@
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 withCurrentPage from 'Components/withCurrentPage';
import { executeCommand } from 'Store/Actions/commandActions';
import * as queueActions from 'Store/Actions/queueActions';
import createCommandExecutingSelector from 'Store/Selectors/createCommandExecutingSelector';
import { registerPagePopulator, unregisterPagePopulator } from 'Utilities/pagePopulator';
import Queue from './Queue';
function createMapStateToProps() {
return createSelector(
(state) => state.movies,
(state) => state.queue.options,
(state) => state.queue.paged,
createCommandExecutingSelector(commandNames.REFRESH_MONITORED_DOWNLOADS),
(movies, options, queue, isRefreshMonitoredDownloadsExecuting) => {
return {
isMoviesFetching: movies.isFetching,
isMoviesPopulated: movies.isPopulated,
moviesError: movies.error,
isRefreshMonitoredDownloadsExecuting,
...options,
...queue
};
}
);
}
const mapDispatchToProps = {
...queueActions,
executeCommand
};
class QueueConnector extends Component {
//
// Lifecycle
componentDidMount() {
const {
useCurrentPage,
fetchQueue,
gotoQueueFirstPage
} = this.props;
registerPagePopulator(this.repopulate);
if (useCurrentPage) {
fetchQueue();
} else {
gotoQueueFirstPage();
}
}
componentDidUpdate(prevProps) {
if (
this.props.includeUnknownMovieItems !==
prevProps.includeUnknownMovieItems
) {
this.repopulate();
}
}
componentWillUnmount() {
unregisterPagePopulator(this.repopulate);
this.props.clearQueue();
}
//
// Control
repopulate = () => {
this.props.fetchQueue();
}
//
// Listeners
onFirstPagePress = () => {
this.props.gotoQueueFirstPage();
}
onPreviousPagePress = () => {
this.props.gotoQueuePreviousPage();
}
onNextPagePress = () => {
this.props.gotoQueueNextPage();
}
onLastPagePress = () => {
this.props.gotoQueueLastPage();
}
onPageSelect = (page) => {
this.props.gotoQueuePage({ page });
}
onSortPress = (sortKey) => {
this.props.setQueueSort({ sortKey });
}
onTableOptionChange = (payload) => {
this.props.setQueueTableOption(payload);
if (payload.pageSize) {
this.props.gotoQueueFirstPage();
}
}
onRefreshPress = () => {
this.props.executeCommand({
name: commandNames.REFRESH_MONITORED_DOWNLOADS
});
}
onGrabSelectedPress = (ids) => {
this.props.grabQueueItems({ ids });
}
onRemoveSelectedPress = (payload) => {
this.props.removeQueueItems(payload);
}
//
// Render
render() {
return (
<Queue
onFirstPagePress={this.onFirstPagePress}
onPreviousPagePress={this.onPreviousPagePress}
onNextPagePress={this.onNextPagePress}
onLastPagePress={this.onLastPagePress}
onPageSelect={this.onPageSelect}
onSortPress={this.onSortPress}
onTableOptionChange={this.onTableOptionChange}
onRefreshPress={this.onRefreshPress}
onGrabSelectedPress={this.onGrabSelectedPress}
onRemoveSelectedPress={this.onRemoveSelectedPress}
{...this.props}
/>
);
}
}
QueueConnector.propTypes = {
includeUnknownMovieItems: PropTypes.bool.isRequired,
useCurrentPage: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
fetchQueue: PropTypes.func.isRequired,
gotoQueueFirstPage: PropTypes.func.isRequired,
gotoQueuePreviousPage: PropTypes.func.isRequired,
gotoQueueNextPage: PropTypes.func.isRequired,
gotoQueueLastPage: PropTypes.func.isRequired,
gotoQueuePage: PropTypes.func.isRequired,
setQueueSort: PropTypes.func.isRequired,
setQueueTableOption: PropTypes.func.isRequired,
clearQueue: PropTypes.func.isRequired,
grabQueueItems: PropTypes.func.isRequired,
removeQueueItems: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired
};
export default withCurrentPage(
connect(createMapStateToProps, mapDispatchToProps)(QueueConnector)
);
@@ -0,0 +1,97 @@
import moment from 'moment';
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import { icons, kinds } from 'Helpers/Props';
function QueueDetails(props) {
const {
title,
size,
sizeleft,
estimatedCompletionTime,
status: queueStatus,
errorMessage,
progressBar
} = props;
const status = queueStatus.toLowerCase();
const progress = (100 - sizeleft / size * 100);
if (status === 'pending') {
return (
<Icon
name={icons.PENDING}
title={`Release will be processed ${moment(estimatedCompletionTime).fromNow()}`}
/>
);
}
if (status === 'completed') {
if (errorMessage) {
return (
<Icon
name={icons.DOWNLOAD}
kind={kinds.DANGER}
title={`Import failed: ${errorMessage}`}
/>
);
}
// TODO: show an icon when download is complete, but not imported yet?
}
if (errorMessage) {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title={`Download failed: ${errorMessage}`}
/>
);
}
if (status === 'failed') {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.DANGER}
title="Download failed: check download client for more details"
/>
);
}
if (status === 'warning') {
return (
<Icon
name={icons.DOWNLOADING}
kind={kinds.WARNING}
title="Download warning: check download client for more details"
/>
);
}
if (progress < 5) {
return (
<Icon
name={icons.DOWNLOADING}
title={`Movie is downloading - ${progress.toFixed(1)}% ${title}`}
/>
);
}
return progressBar;
}
QueueDetails.propTypes = {
title: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
sizeleft: PropTypes.number.isRequired,
estimatedCompletionTime: PropTypes.string,
status: PropTypes.string.isRequired,
errorMessage: PropTypes.string,
progressBar: PropTypes.node.isRequired
};
export default QueueDetails;
@@ -0,0 +1,77 @@
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import { inputTypes } from 'Helpers/Props';
class QueueOptions extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
includeUnknownMovieItems: props.includeUnknownMovieItems
};
}
componentDidUpdate(prevProps) {
const {
includeUnknownMovieItems
} = this.props;
if (includeUnknownMovieItems !== prevProps.includeUnknownMovieItems) {
this.setState({
includeUnknownMovieItems
});
}
}
//
// Listeners
onOptionChange = ({ name, value }) => {
this.setState({
[name]: value
}, () => {
this.props.onOptionChange({
[name]: value
});
});
}
//
// Render
render() {
const {
includeUnknownMovieItems
} = this.state;
return (
<Fragment>
<FormGroup>
<FormLabel>Show Unknown Movie Items</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="includeUnknownMovieItems"
value={includeUnknownMovieItems}
helpText="Show items without a movie in the queue, this could include removed movie, movies or anything else in Radarr's category"
onChange={this.onOptionChange}
/>
</FormGroup>
</Fragment>
);
}
}
QueueOptions.propTypes = {
includeUnknownMovieItems: PropTypes.bool.isRequired,
onOptionChange: PropTypes.func.isRequired
};
export default QueueOptions;
@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { setQueueOption } from 'Store/Actions/queueActions';
import QueueOptions from './QueueOptions';
function createMapStateToProps() {
return createSelector(
(state) => state.queue.options,
(options) => {
return options;
}
);
}
const mapDispatchToProps = {
onOptionChange: setQueueOption
};
export default connect(createMapStateToProps, mapDispatchToProps)(QueueOptions);
+23
View File
@@ -0,0 +1,23 @@
.quality {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 150px;
}
.protocol {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 100px;
}
.progress {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 150px;
}
.actions {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 70px;
}
+369
View File
@@ -0,0 +1,369 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ProtocolLabel from 'Activity/Queue/ProtocolLabel';
import IconButton from 'Components/Link/IconButton';
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
import ProgressBar from 'Components/ProgressBar';
// import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import TableRow from 'Components/Table/TableRow';
import { icons, kinds } from 'Helpers/Props';
import InteractiveImportModal from 'InteractiveImport/InteractiveImportModal';
import MovieFormats from 'Movie/MovieFormats';
import MovieLanguage from 'Movie/MovieLanguage';
import MovieQuality from 'Movie/MovieQuality';
import MovieTitleLink from 'Movie/MovieTitleLink';
import formatBytes from 'Utilities/Number/formatBytes';
import QueueStatusCell from './QueueStatusCell';
import RemoveQueueItemModal from './RemoveQueueItemModal';
import TimeleftCell from './TimeleftCell';
import styles from './QueueRow.css';
class QueueRow extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
isRemoveQueueItemModalOpen: false,
isInteractiveImportModalOpen: false
};
}
//
// Listeners
onRemoveQueueItemPress = () => {
this.setState({ isRemoveQueueItemModalOpen: true });
}
onRemoveQueueItemModalConfirmed = (blacklist) => {
this.props.onRemoveQueueItemPress(blacklist);
this.setState({ isRemoveQueueItemModalOpen: false });
}
onRemoveQueueItemModalClose = () => {
this.setState({ isRemoveQueueItemModalOpen: false });
}
onInteractiveImportPress = () => {
this.setState({ isInteractiveImportModalOpen: true });
}
onInteractiveImportModalClose = () => {
this.setState({ isInteractiveImportModalOpen: false });
}
//
// Render
render() {
const {
id,
downloadId,
title,
status,
trackedDownloadStatus,
trackedDownloadState,
statusMessages,
errorMessage,
movie,
quality,
customFormats,
languages,
protocol,
indexer,
outputPath,
downloadClient,
estimatedCompletionTime,
timeleft,
size,
sizeleft,
showRelativeDates,
shortDateFormat,
timeFormat,
isGrabbing,
grabError,
isRemoving,
isSelected,
columns,
onSelectedChange,
onGrabPress
} = this.props;
const {
isRemoveQueueItemModalOpen,
isInteractiveImportModalOpen
} = this.state;
const progress = 100 - (sizeleft / size * 100);
const showInteractiveImport = status === 'completed' && trackedDownloadStatus === 'warning';
const isPending = status === 'delay' || status === 'downloadClientUnavailable';
return (
<TableRow>
<TableSelectCell
id={id}
isSelected={isSelected}
onSelectedChange={onSelectedChange}
/>
{
columns.map((column) => {
const {
name,
isVisible
} = column;
if (!isVisible) {
return null;
}
if (name === 'status') {
return (
<QueueStatusCell
key={name}
sourceTitle={title}
status={status}
trackedDownloadStatus={trackedDownloadStatus}
trackedDownloadState={trackedDownloadState}
statusMessages={statusMessages}
errorMessage={errorMessage}
/>
);
}
if (name === 'movies.sortTitle') {
return (
<TableRowCell key={name}>
{
movie ?
<MovieTitleLink
titleSlug={movie.titleSlug}
title={movie.title}
/> :
title
}
</TableRowCell>
);
}
if (name === 'languages') {
return (
<TableRowCell key={name}>
<MovieLanguage
languages={languages}
/>
</TableRowCell>
);
}
if (name === 'quality') {
return (
<TableRowCell key={name}>
{
quality ?
<MovieQuality
quality={quality}
/> :
null
}
</TableRowCell>
);
}
if (name === 'customFormats') {
return (
<TableRowCell key={name}>
<MovieFormats
formats={customFormats}
/>
</TableRowCell>
);
}
if (name === 'protocol') {
return (
<TableRowCell key={name}>
<ProtocolLabel
protocol={protocol}
/>
</TableRowCell>
);
}
if (name === 'indexer') {
return (
<TableRowCell key={name}>
{indexer}
</TableRowCell>
);
}
if (name === 'downloadClient') {
return (
<TableRowCell key={name}>
{downloadClient}
</TableRowCell>
);
}
if (name === 'size') {
return (
<TableRowCell key={name}>
{formatBytes(size)}
</TableRowCell>
);
}
if (name === 'title') {
return (
<TableRowCell key={name}>
{title}
</TableRowCell>
);
}
if (name === 'outputPath') {
return (
<TableRowCell key={name}>
{outputPath}
</TableRowCell>
);
}
if (name === 'estimatedCompletionTime') {
return (
<TimeleftCell
key={name}
status={status}
estimatedCompletionTime={estimatedCompletionTime}
timeleft={timeleft}
size={size}
sizeleft={sizeleft}
showRelativeDates={showRelativeDates}
shortDateFormat={shortDateFormat}
timeFormat={timeFormat}
/>
);
}
if (name === 'progress') {
return (
<TableRowCell
key={name}
className={styles.progress}
>
{
!!progress &&
<ProgressBar
progress={progress}
title={`${progress.toFixed(1)}%`}
/>
}
</TableRowCell>
);
}
if (name === 'actions') {
return (
<TableRowCell
key={name}
className={styles.actions}
>
{
showInteractiveImport &&
<IconButton
name={icons.INTERACTIVE}
onPress={this.onInteractiveImportPress}
/>
}
{
isPending &&
<SpinnerIconButton
name={icons.DOWNLOAD}
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
isSpinning={isGrabbing}
onPress={onGrabPress}
/>
}
<SpinnerIconButton
title="Remove from queue"
name={icons.REMOVE}
isSpinning={isRemoving}
onPress={this.onRemoveQueueItemPress}
/>
</TableRowCell>
);
}
return null;
})
}
<InteractiveImportModal
isOpen={isInteractiveImportModalOpen}
downloadId={downloadId}
title={title}
onModalClose={this.onInteractiveImportModalClose}
/>
<RemoveQueueItemModal
isOpen={isRemoveQueueItemModalOpen}
sourceTitle={title}
canIgnore={!!movie}
onRemovePress={this.onRemoveQueueItemModalConfirmed}
onModalClose={this.onRemoveQueueItemModalClose}
/>
</TableRow>
);
}
}
QueueRow.propTypes = {
id: PropTypes.number.isRequired,
downloadId: PropTypes.string,
title: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string,
trackedDownloadState: PropTypes.string,
statusMessages: PropTypes.arrayOf(PropTypes.object),
errorMessage: PropTypes.string,
movie: PropTypes.object,
quality: PropTypes.object.isRequired,
customFormats: PropTypes.arrayOf(PropTypes.object),
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
protocol: PropTypes.string.isRequired,
indexer: PropTypes.string,
outputPath: PropTypes.string,
downloadClient: PropTypes.string,
estimatedCompletionTime: PropTypes.string,
timeleft: PropTypes.string,
size: PropTypes.number,
sizeleft: PropTypes.number,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired,
isGrabbing: PropTypes.bool.isRequired,
grabError: PropTypes.object,
isRemoving: PropTypes.bool.isRequired,
isSelected: PropTypes.bool,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
onSelectedChange: PropTypes.func.isRequired,
onGrabPress: PropTypes.func.isRequired,
onRemoveQueueItemPress: PropTypes.func.isRequired
};
QueueRow.defaultProps = {
isGrabbing: false,
isRemoving: false
};
export default QueueRow;
@@ -0,0 +1,67 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { grabQueueItem, removeQueueItem } from 'Store/Actions/queueActions';
import createMovieSelector from 'Store/Selectors/createMovieSelector';
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
import QueueRow from './QueueRow';
function createMapStateToProps() {
return createSelector(
createMovieSelector(),
createUISettingsSelector(),
(movie, uiSettings) => {
const result = {
showRelativeDates: uiSettings.showRelativeDates,
shortDateFormat: uiSettings.shortDateFormat,
timeFormat: uiSettings.timeFormat
};
result.movie = movie;
return result;
}
);
}
const mapDispatchToProps = {
grabQueueItem,
removeQueueItem
};
class QueueRowConnector extends Component {
//
// Listeners
onGrabPress = () => {
this.props.grabQueueItem({ id: this.props.id });
}
onRemoveQueueItemPress = (payload) => {
this.props.removeQueueItem({ id: this.props.id, ...payload });
}
//
// Render
render() {
return (
<QueueRow
{...this.props}
onGrabPress={this.onGrabPress}
onRemoveQueueItemPress={this.onRemoveQueueItemPress}
/>
);
}
}
QueueRowConnector.propTypes = {
id: PropTypes.number.isRequired,
movie: PropTypes.object,
grabQueueItem: PropTypes.func.isRequired,
removeQueueItem: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(QueueRowConnector);
@@ -0,0 +1,5 @@
.status {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 30px;
}
@@ -0,0 +1,155 @@
import PropTypes from 'prop-types';
import React from 'react';
import Icon from 'Components/Icon';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import Popover from 'Components/Tooltip/Popover';
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
import styles from './QueueStatusCell.css';
function getDetailedPopoverBody(statusMessages) {
return (
<div>
{
statusMessages.map(({ title, messages }) => {
return (
<div key={title}>
{title}
<ul>
{
messages.map((message) => {
return (
<li key={message}>
{message}
</li>
);
})
}
</ul>
</div>
);
})
}
</div>
);
}
function QueueStatusCell(props) {
const {
sourceTitle,
status,
trackedDownloadStatus,
trackedDownloadState,
statusMessages,
errorMessage
} = props;
const hasWarning = trackedDownloadStatus === 'warning';
const hasError = trackedDownloadStatus === 'error';
// status === 'downloading'
let iconName = icons.DOWNLOADING;
let iconKind = kinds.DEFAULT;
let title = 'Downloading';
if (status === 'paused') {
iconName = icons.PAUSED;
title = 'Paused';
}
if (status === 'queued') {
iconName = icons.QUEUED;
title = 'Queued';
}
if (status === 'completed') {
iconName = icons.DOWNLOADED;
title = 'Downloaded';
if (trackedDownloadState === 'importPending') {
title += ' - Waiting to Import';
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'importing') {
title += ' - Importing';
iconKind = kinds.PURPLE;
}
if (trackedDownloadState === 'failedPending') {
title += ' - Waiting to Process';
iconKind = kinds.DANGER;
}
}
if (hasWarning) {
iconKind = kinds.WARNING;
}
if (status === 'delay') {
iconName = icons.PENDING;
title = 'Pending';
}
if (status === 'DownloadClientUnavailable') {
iconName = icons.PENDING;
iconKind = kinds.WARNING;
title = 'Pending - Download client is unavailable';
}
if (status === 'failed') {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = 'Download failed';
}
if (status === 'warning') {
iconName = icons.DOWNLOADING;
iconKind = kinds.WARNING;
title = `Download warning: ${errorMessage || 'check download client for more details'}`;
}
if (hasError) {
if (status === 'completed') {
iconName = icons.DOWNLOAD;
iconKind = kinds.DANGER;
title = `Import failed: ${sourceTitle}`;
} else {
iconName = icons.DOWNLOADING;
iconKind = kinds.DANGER;
title = 'Download failed';
}
}
return (
<TableRowCell className={styles.status}>
<Popover
anchor={
<Icon
name={iconName}
kind={iconKind}
/>
}
title={title}
body={hasWarning || hasError ? getDetailedPopoverBody(statusMessages) : sourceTitle}
position={tooltipPositions.RIGHT}
canFlip={false}
/>
</TableRowCell>
);
}
QueueStatusCell.propTypes = {
sourceTitle: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
trackedDownloadStatus: PropTypes.string.isRequired,
trackedDownloadState: PropTypes.string.isRequired,
statusMessages: PropTypes.arrayOf(PropTypes.object),
errorMessage: PropTypes.string
};
QueueStatusCell.defaultProps = {
trackedDownloadStatus: 'Ok',
trackedDownloadState: 'Downloading'
};
export default QueueStatusCell;
@@ -0,0 +1,143 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
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 { inputTypes, kinds, sizes } from 'Helpers/Props';
class RemoveQueueItemModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
remove: true,
blacklist: false
};
}
//
// Control
resetState = function() {
this.setState({
remove: true,
blacklist: false
});
}
//
// Listeners
onRemoveChange = ({ value }) => {
this.setState({ remove: value });
}
onBlacklistChange = ({ value }) => {
this.setState({ blacklist: value });
}
onRemoveConfirmed = () => {
const state = this.state;
this.resetState();
this.props.onRemovePress(state);
}
onModalClose = () => {
this.resetState();
this.props.onModalClose();
}
//
// Render
render() {
const {
isOpen,
sourceTitle,
canIgnore
} = this.props;
const { remove, blacklist } = this.state;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={this.onModalClose}
>
<ModalContent
onModalClose={this.onModalClose}
>
<ModalHeader>
Remove - {sourceTitle}
</ModalHeader>
<ModalBody>
<div>
Are you sure you want to remove '{sourceTitle}' from the queue?
</div>
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client."
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>Blacklist Release</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blacklist"
value={blacklist}
helpText="Starts a search for this movie again and prevents this release from being grabbed again"
onChange={this.onBlacklistChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={this.onModalClose}>
Close
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
Remove
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
RemoveQueueItemModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
sourceTitle: PropTypes.string.isRequired,
canIgnore: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default RemoveQueueItemModal;
@@ -0,0 +1,3 @@
.message {
margin-bottom: 30px;
}
@@ -0,0 +1,147 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import Button from 'Components/Link/Button';
import Modal from 'Components/Modal/Modal';
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 { inputTypes, kinds, sizes } from 'Helpers/Props';
import styles from './RemoveQueueItemsModal.css';
class RemoveQueueItemsModal extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
remove: true,
blacklist: false
};
}
//
// Control
resetState = function() {
this.setState({
remove: true,
blacklist: false
});
}
//
// Listeners
onRemoveChange = ({ value }) => {
this.setState({ remove: value });
}
onBlacklistChange = ({ value }) => {
this.setState({ blacklist: value });
}
onRemoveConfirmed = () => {
const state = this.state;
this.resetState();
this.props.onRemovePress(state);
}
onModalClose = () => {
this.resetState();
this.props.onModalClose();
}
//
// Render
render() {
const {
isOpen,
selectedCount,
canIgnore
} = this.props;
const { remove, blacklist } = this.state;
return (
<Modal
isOpen={isOpen}
size={sizes.MEDIUM}
onModalClose={this.onModalClose}
>
<ModalContent
onModalClose={this.onModalClose}
>
<ModalHeader>
Remove Selected Item{selectedCount > 1 ? 's' : ''}
</ModalHeader>
<ModalBody>
<div className={styles.message}>
Are you sure you want to remove {selectedCount} item{selectedCount > 1 ? 's' : ''} from the queue?
</div>
<FormGroup>
<FormLabel>Remove From Download Client</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="remove"
value={remove}
helpTextWarning="Removing will remove the download and the file(s) from the download client."
isDisabled={!canIgnore}
onChange={this.onRemoveChange}
/>
</FormGroup>
<FormGroup>
<FormLabel>
Blacklist Release{selectedCount > 1 ? 's' : ''}
</FormLabel>
<FormInputGroup
type={inputTypes.CHECK}
name="blacklist"
value={blacklist}
helpText="Prevents Radarr from automatically grabbing this movie again"
onChange={this.onBlacklistChange}
/>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button onPress={this.onModalClose}>
Close
</Button>
<Button
kind={kinds.DANGER}
onPress={this.onRemoveConfirmed}
>
Remove
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}
}
RemoveQueueItemsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
selectedCount: PropTypes.number.isRequired,
canIgnore: PropTypes.bool.isRequired,
onRemovePress: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default RemoveQueueItemsModal;
@@ -0,0 +1,76 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import PageSidebarStatus from 'Components/Page/Sidebar/PageSidebarStatus';
import { fetchQueueStatus } from 'Store/Actions/queueActions';
function createMapStateToProps() {
return createSelector(
(state) => state.app,
(state) => state.queue.status,
(state) => state.queue.options.includeUnknownMovieItems,
(app, status, includeUnknownMovieItems) => {
const {
errors,
warnings,
unknownErrors,
unknownWarnings,
count,
totalCount
} = status.item;
return {
isConnected: app.isConnected,
isReconnecting: app.isReconnecting,
isPopulated: status.isPopulated,
...status.item,
count: includeUnknownMovieItems ? totalCount : count,
errors: includeUnknownMovieItems ? errors || unknownErrors : errors,
warnings: includeUnknownMovieItems ? warnings || unknownWarnings : warnings
};
}
);
}
const mapDispatchToProps = {
fetchQueueStatus
};
class QueueStatusConnector extends Component {
//
// Lifecycle
componentDidMount() {
if (!this.props.isPopulated) {
this.props.fetchQueueStatus();
}
}
componentDidUpdate(prevProps) {
if (this.props.isConnected && prevProps.isReconnecting) {
this.props.fetchQueueStatus();
}
}
//
// Render
render() {
return (
<PageSidebarStatus
{...this.props}
/>
);
}
}
QueueStatusConnector.propTypes = {
isConnected: PropTypes.bool.isRequired,
isReconnecting: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
fetchQueueStatus: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(QueueStatusConnector);
@@ -0,0 +1,5 @@
.timeleft {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 100px;
}
@@ -0,0 +1,82 @@
import PropTypes from 'prop-types';
import React from 'react';
import TableRowCell from 'Components/Table/Cells/TableRowCell';
import formatTime from 'Utilities/Date/formatTime';
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
import getRelativeDate from 'Utilities/Date/getRelativeDate';
import formatBytes from 'Utilities/Number/formatBytes';
import styles from './TimeleftCell.css';
function TimeleftCell(props) {
const {
estimatedCompletionTime,
timeleft,
status,
size,
sizeleft,
showRelativeDates,
shortDateFormat,
timeFormat
} = props;
if (status === 'delay') {
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
return (
<TableRowCell
className={styles.timeleft}
title={`Delaying download until ${date} at ${time}`}
>
-
</TableRowCell>
);
}
if (status === 'downloadClientUnavailable') {
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
return (
<TableRowCell
className={styles.timeleft}
title={`Retrying download ${date} at ${time}`}
>
-
</TableRowCell>
);
}
if (!timeleft || status === 'completed' || status === 'failed') {
return (
<TableRowCell className={styles.timeleft}>
-
</TableRowCell>
);
}
const totalSize = formatBytes(size);
const remainingSize = formatBytes(sizeleft);
return (
<TableRowCell
className={styles.timeleft}
title={`${remainingSize} / ${totalSize}`}
>
{formatTimeSpan(timeleft)}
</TableRowCell>
);
}
TimeleftCell.propTypes = {
estimatedCompletionTime: PropTypes.string,
timeleft: PropTypes.string,
status: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
sizeleft: PropTypes.number.isRequired,
showRelativeDates: PropTypes.bool.isRequired,
shortDateFormat: PropTypes.string.isRequired,
timeFormat: PropTypes.string.isRequired
};
export default TimeleftCell;
@@ -0,0 +1,60 @@
.searchContainer {
display: flex;
margin-bottom: 10px;
}
.searchIconContainer {
width: 58px;
height: 46px;
border: 1px solid $inputBorderColor;
border-right: none;
border-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: #edf1f2;
text-align: center;
line-height: 46px;
}
.searchInput {
composes: input from '~Components/Form/TextInput.css';
height: 46px;
border-radius: 0;
font-size: 18px;
}
.clearLookupButton {
border: 1px solid $inputBorderColor;
border-left: none;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.message {
margin-top: 30px;
text-align: center;
font-weight: 300;
font-size: $largeFontSize;
}
.helpText {
margin-bottom: 10px;
font-size: 24px;
}
.noMoviesText {
margin-top: 80px;
margin-bottom: 20px;
}
.noResults {
margin-bottom: 10px;
font-weight: 300;
font-size: 30px;
}
.searchResults {
margin-top: 30px;
}
@@ -0,0 +1,213 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import TextInput from 'Components/Form/TextInput';
import Icon from 'Components/Icon';
import Button from 'Components/Link/Button';
import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { icons, kinds } from 'Helpers/Props';
import getErrorMessage from 'Utilities/Object/getErrorMessage';
import translate from 'Utilities/String/translate';
import AddNewMovieSearchResultConnector from './AddNewMovieSearchResultConnector';
import styles from './AddNewMovie.css';
class AddNewMovie extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
term: props.term || '',
isFetching: false
};
}
componentDidMount() {
const term = this.state.term;
if (term) {
this.props.onMovieLookupChange(term);
}
}
componentDidUpdate(prevProps) {
const {
term,
isFetching
} = this.props;
if (term && term !== prevProps.term) {
this.setState({
term,
isFetching: true
});
this.props.onMovieLookupChange(term);
} else if (isFetching !== prevProps.isFetching) {
this.setState({
isFetching
});
}
}
//
// Listeners
onSearchInputChange = ({ value }) => {
const hasValue = !!value.trim();
this.setState({ term: value, isFetching: hasValue }, () => {
if (hasValue) {
this.props.onMovieLookupChange(value);
} else {
this.props.onClearMovieLookup();
}
});
}
onClearMovieLookupPress = () => {
this.setState({ term: '' });
this.props.onClearMovieLookup();
}
//
// Render
render() {
const {
error,
items,
hasExistingMovies
} = this.props;
const term = this.state.term;
const isFetching = this.state.isFetching;
return (
<PageContent title="Add New Movie">
<PageContentBody>
<div className={styles.searchContainer}>
<div className={styles.searchIconContainer}>
<Icon
name={icons.SEARCH}
size={20}
/>
</div>
<TextInput
className={styles.searchInput}
name="movieLookup"
value={term}
placeholder="eg. The Dark Knight, tmdb:155, imdb:tt0468569"
autoFocus={true}
onChange={this.onSearchInputChange}
/>
<Button
className={styles.clearLookupButton}
onPress={this.onClearMovieLookupPress}
>
<Icon
name={icons.REMOVE}
size={20}
/>
</Button>
</div>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error ?
<div className={styles.message}>
<div className={styles.helpText}>
Failed to load search results, please try again.
</div>
<div>{getErrorMessage(error)}</div>
</div> : null
}
{
!isFetching && !error && !!items.length &&
<div className={styles.searchResults}>
{
items.map((item) => {
return (
<AddNewMovieSearchResultConnector
key={item.tmdbId}
{...item}
/>
);
})
}
</div>
}
{
!isFetching && !error && !items.length && !!term &&
<div className={styles.message}>
<div className={styles.noResults}>Couldn't find any results for '{term}'</div>
<div>You can also search using TMDB ID or IMDB ID of a movie. eg. tmdb:71663</div>
<div>
<Link to="https://github.com/Radarr/Radarr/wiki/FAQ#why-cant-i-add-a-new-movie-when-i-know-the-tmdb-id">
Why can't I find my movie?
</Link>
</div>
</div>
}
{
term ?
null :
<div className={styles.message}>
<div className={styles.helpText}>
{translate('AddNewMessage')}
</div>
<div>{translate('AddNewTmdbIdMessage')}</div>
</div>
}
{
!term && !hasExistingMovies ?
<div className={styles.message}>
<div className={styles.noMoviesText}>
You haven't added any movies yet, do you want to import some or all of your movies first?
</div>
<div>
<Button
to="/add/import"
kind={kinds.PRIMARY}
>
Import Existing Movies
</Button>
</div>
</div> :
null
}
<div />
</PageContentBody>
</PageContent>
);
}
}
AddNewMovie.propTypes = {
term: PropTypes.string,
isFetching: PropTypes.bool.isRequired,
error: PropTypes.object,
isAdding: PropTypes.bool.isRequired,
addError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
hasExistingMovies: PropTypes.bool.isRequired,
onMovieLookupChange: PropTypes.func.isRequired,
onClearMovieLookup: PropTypes.func.isRequired
};
export default AddNewMovie;
@@ -0,0 +1,108 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { clearAddMovie, lookupMovie } from 'Store/Actions/addMovieActions';
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
import { fetchNetImportExclusions } from 'Store/Actions/Settings/netImportExclusions';
import parseUrl from 'Utilities/String/parseUrl';
import AddNewMovie from './AddNewMovie';
function createMapStateToProps() {
return createSelector(
(state) => state.addMovie,
(state) => state.movies.items.length,
(state) => state.router.location,
(addMovie, existingMoviesCount, location) => {
const { params } = parseUrl(location.search);
return {
...addMovie,
term: params.term,
hasExistingMovies: existingMoviesCount > 0
};
}
);
}
const mapDispatchToProps = {
lookupMovie,
clearAddMovie,
fetchRootFolders,
fetchNetImportExclusions
};
class AddNewMovieConnector extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this._movieLookupTimeout = null;
}
componentDidMount() {
this.props.fetchRootFolders();
this.props.fetchNetImportExclusions();
}
componentWillUnmount() {
if (this._movieLookupTimeout) {
clearTimeout(this._movieLookupTimeout);
}
this.props.clearAddMovie();
}
//
// Listeners
onMovieLookupChange = (term) => {
if (this._movieLookupTimeout) {
clearTimeout(this._movieLookupTimeout);
}
if (term.trim() === '') {
this.props.clearAddMovie();
} else {
this._movieLookupTimeout = setTimeout(() => {
this.props.lookupMovie({ term });
}, 300);
}
}
onClearMovieLookup = () => {
this.props.clearAddMovie();
}
//
// Render
render() {
const {
term,
...otherProps
} = this.props;
return (
<AddNewMovie
term={term}
{...otherProps}
onMovieLookupChange={this.onMovieLookupChange}
onClearMovieLookup={this.onClearMovieLookup}
/>
);
}
}
AddNewMovieConnector.propTypes = {
term: PropTypes.string,
lookupMovie: PropTypes.func.isRequired,
clearAddMovie: PropTypes.func.isRequired,
fetchRootFolders: PropTypes.func.isRequired,
fetchNetImportExclusions: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieConnector);
@@ -0,0 +1,31 @@
import PropTypes from 'prop-types';
import React from 'react';
import Modal from 'Components/Modal/Modal';
import AddNewMovieModalContentConnector from './AddNewMovieModalContentConnector';
function AddNewMovieModal(props) {
const {
isOpen,
onModalClose,
...otherProps
} = props;
return (
<Modal
isOpen={isOpen}
onModalClose={onModalClose}
>
<AddNewMovieModalContentConnector
{...otherProps}
onModalClose={onModalClose}
/>
</Modal>
);
}
AddNewMovieModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default AddNewMovieModal;
@@ -0,0 +1,68 @@
.container {
display: flex;
}
.year {
margin-left: 5px;
color: $disabledColor;
}
.poster {
flex: 0 0 170px;
margin-right: 20px;
height: 250px;
}
.info {
flex-grow: 1;
}
.overview {
margin-bottom: 30px;
}
.labelIcon {
margin-left: 8px;
}
.searchForMissingMovieLabelContainer {
display: flex;
margin-top: 2px;
}
.searchForMissingMovieLabel {
margin-right: 8px;
font-weight: normal;
}
.searchForMissingMovieContainer {
composes: container from '~Components/Form/CheckInput.css';
flex: 0 1 0;
}
.searchForMissingMovieInput {
composes: input from '~Components/Form/CheckInput.css';
margin-top: 0;
}
.modalFooter {
composes: modalFooter from '~Components/Modal/ModalFooter.css';
}
.addButton {
@add-mixin truncate;
composes: button from '~Components/Link/SpinnerButton.css';
}
@media only screen and (max-width: $breakpointSmall) {
.modalFooter {
display: block;
text-align: center;
}
.addButton {
margin-top: 10px;
}
}
@@ -0,0 +1,216 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import CheckInput from 'Components/Form/CheckInput';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormInputGroup from 'Components/Form/FormInputGroup';
import FormLabel from 'Components/Form/FormLabel';
import SpinnerButton from 'Components/Link/SpinnerButton';
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 { inputTypes, kinds } from 'Helpers/Props';
import MoviePoster from 'Movie/MoviePoster';
import styles from './AddNewMovieModalContent.css';
class AddNewMovieModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this.state = {
searchForMovie: false
};
}
//
// Listeners
onSearchForMissingMovieChange = ({ value }) => {
this.setState({ searchForMovie: value });
}
onQualityProfileIdChange = ({ value }) => {
this.props.onInputChange({ name: 'qualityProfileId', value: parseInt(value) });
}
onAddMoviePress = () => {
this.props.onAddMoviePress(this.state.searchForMovie);
}
//
// Render
render() {
const {
title,
year,
overview,
images,
isAdding,
rootFolderPath,
monitor,
qualityProfileId,
minimumAvailability,
folder,
tags,
isSmallScreen,
isWindows,
onModalClose,
onInputChange
} = this.props;
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
{title}
{
!title.contains(year) && !!year &&
<span className={styles.year}>({year})</span>
}
</ModalHeader>
<ModalBody>
<div className={styles.container}>
{
!isSmallScreen &&
<div className={styles.poster}>
<MoviePoster
className={styles.poster}
images={images}
size={250}
/>
</div>
}
<div className={styles.info}>
<div className={styles.overview}>
{overview}
</div>
<Form>
<FormGroup>
<FormLabel>Root Folder</FormLabel>
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}
name="rootFolderPath"
valueOptions={{
movieFolder: folder,
isWindows
}}
selectedValueOptions={{
movieFolder: folder,
isWindows
}}
helpText={`'${folder}' subfolder will be created automatically`}
onChange={onInputChange}
{...rootFolderPath}
/>
</FormGroup>
<FormGroup>
<FormLabel>
Monitor
</FormLabel>
<FormInputGroup
type={inputTypes.MOVIE_MONITORED_SELECT}
name="monitor"
onChange={onInputChange}
{...monitor}
/>
</FormGroup>
<FormGroup>
<FormLabel>Minimum Availability</FormLabel>
<FormInputGroup
type={inputTypes.AVAILABILITY_SELECT}
name="minimumAvailability"
onChange={onInputChange}
{...minimumAvailability}
/>
</FormGroup>
<FormGroup>
<FormLabel>Quality Profile</FormLabel>
<FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT}
name="qualityProfileId"
onChange={this.onQualityProfileIdChange}
{...qualityProfileId}
/>
</FormGroup>
<FormGroup>
<FormLabel>Tags</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
name="tags"
onChange={onInputChange}
{...tags}
/>
</FormGroup>
</Form>
</div>
</div>
</ModalBody>
<ModalFooter className={styles.modalFooter}>
<label className={styles.searchForMissingMovieLabelContainer}>
<span className={styles.searchForMissingMovieLabel}>
Start search for missing movie
</span>
<CheckInput
containerClassName={styles.searchForMissingMovieContainer}
className={styles.searchForMissingMovieInput}
name="searchForMovie"
value={this.state.searchForMovie}
onChange={this.onSearchForMissingMovieChange}
/>
</label>
<SpinnerButton
className={styles.addButton}
kind={kinds.SUCCESS}
isSpinning={isAdding}
onPress={this.onAddMoviePress}
>
Add {title}
</SpinnerButton>
</ModalFooter>
</ModalContent>
);
}
}
AddNewMovieModalContent.propTypes = {
title: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
overview: PropTypes.string,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
isAdding: PropTypes.bool.isRequired,
addError: PropTypes.object,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,
tags: PropTypes.object.isRequired,
isSmallScreen: PropTypes.bool.isRequired,
isWindows: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired,
onInputChange: PropTypes.func.isRequired,
onAddMoviePress: PropTypes.func.isRequired
};
export default AddNewMovieModalContent;
@@ -0,0 +1,103 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { addMovie, setAddMovieDefault } from 'Store/Actions/addMovieActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import selectSettings from 'Store/Selectors/selectSettings';
import AddNewMovieModalContent from './AddNewMovieModalContent';
function createMapStateToProps() {
return createSelector(
(state) => state.addMovie,
createDimensionsSelector(),
createSystemStatusSelector(),
(addMovieState, dimensions, systemStatus) => {
const {
isAdding,
addError,
defaults
} = addMovieState;
const {
settings,
validationErrors,
validationWarnings
} = selectSettings(defaults, {}, addError);
return {
isAdding,
addError,
isSmallScreen: dimensions.isSmallScreen,
validationErrors,
validationWarnings,
isWindows: systemStatus.isWindows,
...settings
};
}
);
}
const mapDispatchToProps = {
setAddMovieDefault,
addMovie
};
class AddNewMovieModalContentConnector extends Component {
//
// Listeners
onInputChange = ({ name, value }) => {
this.props.setAddMovieDefault({ [name]: value });
}
onAddMoviePress = (searchForMovie) => {
const {
tmdbId,
rootFolderPath,
monitor,
qualityProfileId,
minimumAvailability,
tags
} = this.props;
this.props.addMovie({
tmdbId,
rootFolderPath: rootFolderPath.value,
monitor: monitor.value,
qualityProfileId: qualityProfileId.value,
minimumAvailability: minimumAvailability.value,
tags: tags.value,
searchForMovie
});
}
//
// Render
render() {
return (
<AddNewMovieModalContent
{...this.props}
onInputChange={this.onInputChange}
onAddMoviePress={this.onAddMoviePress}
/>
);
}
}
AddNewMovieModalContentConnector.propTypes = {
tmdbId: PropTypes.number.isRequired,
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
qualityProfileId: PropTypes.object,
minimumAvailability: PropTypes.object.isRequired,
tags: PropTypes.object.isRequired,
onModalClose: PropTypes.func.isRequired,
setAddMovieDefault: PropTypes.func.isRequired,
addMovie: PropTypes.func.isRequired
};
export default connect(createMapStateToProps, mapDispatchToProps)(AddNewMovieModalContentConnector);

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