Compare commits

...

96 commits

Author SHA1 Message Date
Conventional Changelog Action
b72f47470c chore(release): 🚀 v4.49.3 [skip ci] 2025-08-17 16:01:24 +00:00
Jamie Rees
72d4115378
Merge pull request #5248 from emma-the-rock/patch-1
fix(plex-api): update Plex Watchlist URL
2025-08-17 17:58:51 +02:00
emmatherock
11fd7a5fc8
fix(plex-api): update Plex Watchlist URL 2025-08-14 21:17:10 -03:00
Conventional Changelog Action
d2be48a921 chore(release): 🚀 v4.49.2 [skip ci] 2025-07-12 21:47:40 +00:00
tidusjar
a92c76021a Merge branch 'develop' of https://github.com/tidusjar/ombi into develop 2025-07-12 22:45:50 +01:00
tidusjar
97d5167db6 perf(discover): Improve the loading performance on the discover page 2025-07-12 22:35:11 +01:00
Conventional Changelog Action
2519cca9f6 chore(release): 🚀 v4.49.1 [skip ci] 2025-07-12 21:27:34 +00:00
tidusjar
cfeee39978 Merge remote-tracking branch 'origin/develop' into develop 2025-07-12 22:25:45 +01:00
tidusjar
cee40146ee fix(auth): Fixed an issue where refreshing the page as a power user would stop the application from loading #5242 2025-07-12 22:25:31 +01:00
Conventional Changelog Action
1eff48e58e chore(release): 🚀 v4.49.0 [skip ci] 2025-07-11 21:51:32 +00:00
tidusjar
3b2a0d84be fix 2025-07-11 22:49:21 +01:00
contrib-readme-bot
ed5bc3f873 chore: 👥 Updated Contributors [skip ci] 2025-07-11 21:20:26 +00:00
tidusjar
067c029f42 feat: Added the ability for the Watchlist to automatically refresh the users token. This will reduce the need for the user to log in 2025-07-11 22:19:10 +01:00
Conventional Changelog Action
cfe2b6ac0f chore(release): 🚀 v4.48.5 [skip ci] 2025-05-14 21:18:32 +00:00
tidusjar
c9ab4f4f9f fix: filter out excluded notification agents from user preferences
The webhook notification field was inconsistently showing up for some users despite being excluded in the backend. This was happening because the frontend was displaying all notification preferences without filtering out the excluded agents.

Changes:
- Added excludedAgents array to match backend's excluded notification types
- Filter notification preferences in both edit and create user flows
- Prevents webhook, email, and mobile notification fields from appearing in user preferences

This change aligns the frontend behavior with the backend's intended design where webhook notifications are managed globally rather than per-user.

Fixes #5196
2025-05-14 22:16:34 +01:00
Conventional Changelog Action
acb679f99d chore(release): 🚀 v4.48.4 [skip ci] 2025-05-14 21:11:38 +00:00
tidusjar
f88c5ad818 fix(ui): correct timezone handling in OmbiDatePipe
- Replace native Date constructor with date-fns parseISO for proper UTC parsing
- Use date-fns format function for consistent timezone conversion
- Add null check for input value
- Fix issue where request times were showing incorrect timezone offset

This fixes GitHub issue #5102 where request times were showing different times than the host machine.
2025-05-14 22:09:46 +01:00
Jamie Rees
b3e8ca6950
Merge pull request #5192 from Ombi-app/translations
[skip ci]
2025-05-14 22:03:07 +01:00
Conventional Changelog Action
15a97794f6 chore(release): 🚀 v4.48.3 [skip ci] 2025-05-14 20:59:03 +00:00
tidusjar
ba6e708e18 fix: Correct 4K movie request existence check
When requesting a 4K movie, Ombi was incorrectly checking for existence in the base Radarr instance instead of the 4K instance. This caused "already exists" errors when trying to request 4K versions of movies that only existed in the standard instance.

Changes:
- Modified SendToRadarr to use the correct Radarr instance (4K or standard) when checking for existing movies
- Added existenceCheckSettings to properly handle instance-specific checks
- Maintains original settings for movie addition/update operations

Fixes #4798
2025-05-14 21:57:06 +01:00
Conventional Changelog Action
08c9017a2c chore(release): 🚀 v4.48.2 [skip ci] 2025-05-14 20:53:39 +00:00
tidusjar
f8658fe6d5 fix(radarr): ensure RequestedUser is loaded when creating tags
- Replace Find() with GetWithUser() in MovieRequestEngine methods to properly load RequestedUser navigation property
- Add null check in GetOrCreateTag as a safety measure
- Fix NullReferenceException when "Add the user as a tag" feature is enabled

Fixes #5045
2025-05-14 21:51:41 +01:00
Conventional Changelog Action
7303e7da3b chore(release): 🚀 v4.48.1 [skip ci] 2025-05-14 20:45:51 +00:00
tidusjar
ffb495019f bug: added additional logging to help identify #5215 2025-05-14 21:44:01 +01:00
Conventional Changelog Action
13c1544476 chore(release): 🚀 v4.48.0 [skip ci] 2025-05-14 20:36:40 +00:00
contrib-readme-bot
04ddf3d09b chore: 👥 Updated Contributors [skip ci] 2025-05-14 20:34:19 +00:00
Jamie Rees
ea0b690c18
feat: added the watchlist notification 2025-05-14 21:34:05 +01:00
tidusjar
dae0fe6be4 fixed tests 2025-05-14 21:24:50 +01:00
Jamie Rees
dbbfdd926f fix(translations): 🌐 New translations from Crowdin [skip ci] 2025-05-13 15:38:31 +01:00
tidusjar
cb6d441ccd rework the pages 2025-05-12 22:19:59 +01:00
tidusjar
6344ae98cd missed a file 2025-05-12 22:03:04 +01:00
tidusjar
0dfd4533db feat: added the watchlist notification 2025-05-12 22:02:59 +01:00
Conventional Changelog Action
6e539585f1 chore(release): 🚀 v4.47.3 [skip ci] 2025-04-13 20:20:05 +00:00
tidusjar
cf0c1614a4 fix: #5223 2025-04-13 21:17:25 +01:00
Conventional Changelog Action
2868314a34 chore(release): 🚀 v4.47.2 [skip ci] 2025-03-11 08:46:02 +00:00
Jamie Rees
3eef2fafee
Update version.json 2025-03-11 08:44:06 +00:00
Jamie Rees
9227bc0b4c
Merge pull request #5219 from Ombi-app/fix-users
fix(user-import): Do not import users that do not have access to the server
2025-03-09 22:05:59 +00:00
Jamie Rees
fe2fe24158 update cache 2025-03-09 22:02:43 +00:00
Jamie Rees
a801cfdb09 fix(user-import): Do not import users that do not have access to the server #5064 2025-03-09 22:01:10 +00:00
contrib-readme-bot
72af4f734d chore: 👥 Updated Contributors [skip ci] 2025-01-05 21:11:05 +00:00
Jamie Rees
fc94fcfe68
Merge pull request #5209 from Ombi-app/cypress-13
test: ⬆️ Update Cypress to V13
2025-01-05 21:10:54 +00:00
Conventional Changelog Action
6df9d6e1d2 chore(release): 🚀 v4.47.0 [skip ci] 2025-01-03 16:21:42 +00:00
Jamie Rees
fefc768aa5
Merge branch 'develop' into cypress-13 2025-01-03 16:21:00 +00:00
contrib-readme-bot
b00e3878a7 chore: 👥 Updated Contributors [skip ci] 2025-01-03 16:20:07 +00:00
Jamie Rees
cc98fc6aca
feat(wizard): Added the ability to start with a different database (#5208)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-01-03 16:19:53 +00:00
Jamie Rees
e8ca519ef9 test: ⬆️ Update Cypress to V13 2025-01-03 16:17:44 +00:00
Conventional Changelog Action
579d048ba1 chore(release): 🚀 v4.46.8 [skip ci] 2025-01-03 16:09:39 +00:00
contrib-readme-bot
cd260ed844 chore: 👥 Updated Contributors [skip ci] 2025-01-03 16:04:31 +00:00
Alexandre Picavet
dc2b958915
fix(radarr-settings): this.normalForm is undefined (#5207)
Fixes #4994
2025-01-03 16:04:19 +00:00
Conventional Changelog Action
3234204221 chore(release): 🚀 v4.46.7 [skip ci] 2024-12-03 21:06:38 +00:00
TidusJar
138df1eb25 fix(requests): 🐛 Power users can now set profiles and root folders when requesting 2024-12-03 21:01:16 +00:00
Jamie Rees
53a6a092b1 fix(translations): 🌐 New translations from Crowdin [skip ci] 2024-11-30 19:09:51 +00:00
Conventional Changelog Action
da6665deb6 chore(release): 🚀 v4.46.6 [skip ci] 2024-11-24 06:35:55 +00:00
Jamie Rees
fcb4082731
Update build.yml 2024-11-23 23:58:59 +00:00
Conventional Changelog Action
3fd722395a chore(release): 🚀 v4.46.5 [skip ci] 2024-11-23 23:02:20 +00:00
TidusJar
7b0db11336 fix 2024-11-23 22:56:20 +00:00
TidusJar
029ea79192 fix(Fixed the UI not applying the correct timezone settings): 🐛 2024-11-23 22:02:58 +00:00
Jamie Rees
2009fb743e
[Snyk] Security upgrade Microsoft.Extensions.Logging.Console from 6.0.0 to 8.0.1 (#5174)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-10-16 10:21:52 +01:00
Conventional Changelog Action
7b7ebf9767 chore(release): 🚀 v4.46.4 [skip ci] 2024-09-09 08:30:37 +00:00
Jamie Rees
3b1395e6f5 Merge branch 'develop' of https://github.com/Ombi-app/Ombi into develop 2024-09-09 09:28:38 +01:00
Jamie Rees
dd9892fc1f bug(watchlist): Updated the watchlist to take into account the actual user.
This means that it's per user now not global #5170
2024-09-09 09:28:23 +01:00
Conventional Changelog Action
b24f6084e0 chore(release): 🚀 v4.46.3 [skip ci] 2024-09-07 20:06:48 +00:00
TidusJar
4eef0e6e21 bug: fixed compile errors 2024-09-07 21:05:15 +01:00
TidusJar
0fb29a0b16 fix(radarr-4k): 🐛 Fixed an issue where the overrides wouldn't work for 4k Requests 2024-09-07 21:04:24 +01:00
Conventional Changelog Action
938a0b74aa chore(release): 🚀 v4.46.2 [skip ci] 2024-09-03 15:30:07 +00:00
Jamie Rees
0af3511e81 fix(radarr): 🐛 Enable validation on the radarr settings page 2024-09-03 16:14:16 +01:00
Conventional Changelog Action
fc1ad67246 chore(release): 🚀 v4.46.1 [skip ci] 2024-08-27 06:56:55 +00:00
Jamie Rees
e1f2a84806
fix: src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities (#5167)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-BOUNCYCASTLECRYPTOGRAPHY-7831268
- https://snyk.io/vuln/SNYK-DOTNET-BOUNCYCASTLECRYPTOGRAPHY-7831266

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

[skip-ci]
2024-08-27 06:54:56 +00:00
Conventional Changelog Action
a84d26e8a3 chore(release): 🚀 v4.46.0 [skip ci] 2024-08-20 20:57:25 +00:00
Jamie Rees
48d3dec26d
feat: upgrade @ngx-translate/core from 14.0.0 to 15.0.0 (#5158)
Snyk has created this PR to upgrade @ngx-translate/core from 14.0.0 to 15.0.0.

See this package in yarn:
@ngx-translate/core

See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2024-08-20 21:52:02 +01:00
Jamie Rees
3bd98c1d71
feat: upgrade @ngx-translate/http-loader from 7.0.0 to 8.0.0 (#5159)
[skip ci]
2024-08-20 21:50:46 +01:00
Jamie Rees
9c21074189
fix: src/Ombi.Store/Ombi.Store.csproj to reduce vulnerabilities (#5160)
[skip ci]
2024-08-20 21:49:43 +01:00
TidusJar
7654fba9b5 Merge branch 'develop' of https://github.com/ombi-app/ombi into develop 2024-08-20 21:48:46 +01:00
TidusJar
ea00d6c12f fix(discover): Improved rendering on the discover page 2024-08-20 21:48:04 +01:00
Conventional Changelog Action
43b55d9940 chore(release): 🚀 v4.45.1 [skip ci] 2024-08-20 19:52:32 +00:00
TidusJar
d9787dc32a fix(plex): Fixed some errors around the scanner that was causing the scan to fail 2024-08-20 20:47:28 +01:00
Conventional Changelog Action
be886eed3c chore(release): 🚀 v4.45.0 [skip ci] 2024-08-07 15:19:56 +00:00
Jamie Rees
061132f897 Merge branch 'master' into develop 2024-08-07 16:14:28 +01:00
Jamie Rees
a7e0c8cb1d
[Snyk] Upgrade @fortawesome/fontawesome-free from 6.5.2 to 6.6.0 (#5150)
[skip ci]
2024-08-07 16:03:59 +01:00
Jamie Rees
ae5844c27e
[Snyk] Upgrade @types/jquery from 3.5.29 to 3.5.30 (#5136)
[skip ci]
2024-07-30 13:58:45 +01:00
Jamie Rees
9975fa42de
[Snyk] Upgrade @types/jquery from 3.5.29 to 3.5.30 (#5141)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-30 13:58:30 +01:00
Jamie Rees
51721bed2b
[Snyk] Upgrade primeng from 17.9.0 to 17.11.0 (#5108)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2024-07-30 13:58:06 +01:00
Conventional Changelog Action
9e8171a794 chore(release): 🚀 v4.44.1 [skip ci] 2024-07-22 11:31:52 +00:00
Jamie Rees
1e3bd6377c
dev - master (#5143)
Co-authored-by: tidusjar <tidusjar@gmail.com>
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: snyk-bot <snyk-bot@snyk.io> [skip ci]
Co-authored-by: Jamie <tidusjar@gmail.com>
Co-authored-by: Drew <drewm727@hotmail.com> [skip ci]
Co-authored-by: ryan-c44 <54028283+ryan-c44@users.noreply.github.com>
Co-authored-by: Alexander Russell <ajex94@gmail.com>
Co-authored-by: Grygon <647846+Grygon@users.noreply.github.com>
Co-authored-by: phildups7 <60622768+phildups7@users.noreply.github.com>
Co-authored-by: Avi <357984+Unimatrix0@users.noreply.github.com>
Co-authored-by: Antonin <82907030+Antonin-Bruzard@users.noreply.github.com>
Co-authored-by: Drew <mcminn.drew@gmail.com>
Co-authored-by: Joel Samson <joel.samson@hotmail.com>
Co-authored-by: David Benson <dben@users.noreply.github.com>
fix: Consistently reset loading flag when requesting movies on discover page. (#4777)
fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782
fix: Only log error messages from Microsoft (#4787)
fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)
fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)
fix(sonarr): 🐛 Sonarr V4 should work now (#4810)
fixed (#4833)
fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)
fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)
fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845
fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)
fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835
fix: Some minor tweaks to the movie info panel (#4883)
fix(sonarr): 🐛 Stop the sonarr version endpoint from breaking when Sonarr is down #4895
fix: Support duplicates in Emby/JF collections (#4902)
fix(discover): Fix denied requests displayed as approved (#4901)
fix: Fix denied movie shown as 'processing request' in details view (#4900)
fix(#4906): 🐛 Fixed an issue with power users and permissions
fix: Cron Validation (#4842)
fix(lidarr): Change monitor to Existing to properly add artist #3597
fix(API): Allow RequestOnBehalf rights if requested from the API (#4919)
Fixes #4610
fix: added media type tag to media type text (#4638)
fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)
fix: Missing Poster broken link fix (#4637)
fix: Improve Swagger documentation (#4652)
fix(API): Fix pagination in some edge cases (#4649)
fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)
fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)
fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685
fix: Landing and Login page improvements (#4690)
fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)
fix: Override Sonarr V3 Profiles endpoint (#4678)
fix(4K) :4K request fixes (#4702)
fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)
fix: fixed trakt image not loading when base url present (#4711)
fix: 🐛 Fixed missing externals (#4712)
fix: Log Microsoft warnings to log file (#4723)
fix: Localize recently requested on discover page (#4729)
Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)
fix: Fix conflicting property name for Swagger (#4733)
fix: fixed stats controller (#4742)
fix(webhook): Remove added trailing slash from webhook URL #4710
fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)
fix: Fixes default image for recently requested items. (#4767)
Fixes build warnings. (#4769)
fix: Reworked the version check (#4719) (#4781)
fix: Unable to Delete Jellyfin Server (#4705) (#4780)
fix: Partially Available prevents further TV requests (#4768) (#4779)
fix(emby): Fix Emby played sync running a full sync during recently added sync (#4932)
Fixes #4947
fix: Fix various styling issues (#4935)
fix(translations): 🌐 New translations from Crowdin [skip ci] (#4926)
fix: upgrade @microsoft/signalr from 6.0.11 to 6.0.16 (#4964) [skip ci]
fix: upgrade primeng from 15.0.0-rc.1 to 15.4.1 (#4962) [skip ci]
fix: src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities (#4969) [skip ci]
fix: upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0 (#4965) [skip ci]
fix: upgrade multiple dependencies with Snyk (#4963) [skip ci]
fix flaky tests (#4970) [skip ci]
fix: More automation tests mainly around the Plex Settings page (#4821)
fix: upgrade cypress-real-events from 1.7.4 to 1.8.1 (#4968) [skip ci]
fix: Remove Angular TSLint (#4973)
fix: upgrade zone.js from 0.11.8 to 0.13.0 (#4975)
fix: upgrade jquery from 3.6.1 to 3.7.0 (#4974)
fix: upgrade multiple dependencies with Snyk (#4961)
fixes (#4978)
fix(user-importer): 🐛 Fixed an issue where the cleanup wouldn't delete users #4812
fix(user-importer): Do not delete the Plex Admin as part of the user Importer cleanup #4870 (#4981)
Fixes #4957
fix: Remove old trending source (#4987)
fix(plex-api): Switch over to the new API to avoid deprecation & save… (#4986)
fix: switch back to the old plex friends API #4989
fix(user-importer): Fixed not importing all correct users #4989
fix: upgrade cypress-real-events from 1.8.1 to 1.9.1 (#5000) [skip ci]
fix: upgrade @microsoft/signalr from 6.0.18 to 6.0.20 (#4999) [skip ci]
fix: upgrade @fortawesome/fontawesome-free from 6.4.0 to 6.4.2 (#5005) [skip ci]
fix: upgrade @types/jquery from 3.5.16 to 3.5.17 (#5011) [skip ci]
fix: src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities (#5010) [skip ci]
fix: upgrade @microsoft/signalr from 6.0.21 to 6.0.22 (#5020)
fix: upgrade @types/jquery from 3.5.18 to 3.5.19 (#5022)
fix: upgrade zone.js from 0.13.1 to 0.13.2 (#5019)
fix: upgrade multiple dependencies with Snyk (#5030)
fix: upgrade cypress-real-events from 1.10.0 to 1.10.1 (#5014)
fix: upgrade jquery from 3.7.0 to 3.7.1 (#5015)
Fix: Linkify logo in newsletter (#5036)
fix: upgrade @types/jquery from 3.5.22 to 3.5.23 (#5034) [skip ci]
fix: upgrade @microsoft/signalr from 6.0.22 to 6.0.23 (#5032) [skip ci]
Fix(mutliple servers): attempting to cache the Plex Content in server (#5018)
fix(radarr-4k): 🐛 Fixed an issue when using Radarr 4k with user set quality profiles. There are now user quality profiles for 4k profile #5025
fix: src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities (#5072) [skip ci]
fix: src/Ombi/Ombi.csproj to reduce vulnerabilities (#5066) [skip ci]
fix: upgrade @fortawesome/fontawesome-free from 6.4.2 to 6.5.0 (#5053)  [skip ci]
fix: upgrade @types/jquery from 3.5.27 to 3.5.28 (#5049)  [skip ci]
fix: src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities (#5040)  [skip ci]
fix: upgrade moment from 2.29.4 to 2.30.1 (#5075) [skip ci]
fix: upgrade multiple dependencies with Snyk (#5073) [skip ci]
fix: upgrade multiple dependencies with Snyk (#5104) [skip ci]
2024-07-22 11:41:03 +01:00
Jamie Rees
69929f1a8c
[Snyk] Upgrade cypress-real-events from 1.10.3 to 1.13.0 (#5138)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-17 16:07:23 +01:00
Jamie Rees
3608322d60
[Snyk] Upgrade ngx-infinite-scroll from 17.0.0 to 17.0.1 (#5137)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-17 16:06:29 +01:00
Jamie Rees
ff85d54800
[Snyk] Upgrade zone.js from 0.14.4 to 0.14.7 (#5133)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-16 10:21:31 +01:00
Jamie Rees
a1c20e951b
[Snyk] Upgrade @fortawesome/fontawesome-free from 6.5.1 to 6.5.2 (#5134)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-16 10:20:52 +01:00
Jamie Rees
d43c72f35a
[Snyk] Upgrade: , , , , , , , , (#5131)
Co-authored-by: snyk-bot <snyk-bot@snyk.io> [skip ci]
2024-07-15 10:21:17 +01:00
Jamie Rees
c792e614c1
[Snyk] Upgrade zone.js from 0.14.3 to 0.14.4 (#5086)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-12 11:00:45 +01:00
Jamie Rees
100bfe3384
[Snyk] Upgrade: @ngxs/devtools-plugin, @ngxs/store (#5085)
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
[skip ci]
2024-07-12 11:00:17 +01:00
Jamie Rees
5dce9abb11
chore: 🌐 Translations Update (#5105) [skip ci] 2024-07-11 12:35:06 +01:00
Conventional Changelog Action
f4d0bf1dae chore(release): 🚀 v4.43.5 [skip ci] 2023-08-24 20:28:56 +00:00
Jamie
8845e48baf
Dev merge master (#5006)
* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

* fix(sonarr): 🐛 Improved the error handling in the sonarr settings page in the UI

This should hopefully prevent some odd situations where the settings are in a odd state #4877

* chore: update deps

* chore: more deps

* bump

* chore(release): 🚀 v4.35.12 [skip ci]

* fix(sonarr): 🐛 Added some more error handling and information around testing sonarr

#4877

* chore(release): 🚀 v4.35.13 [skip ci]

* fix: Some minor tweaks to the movie info panel (#4883)

* fix: Hide denied reason label if there is no value

* fix: Movie would show as pending approval when denied

* chore(release): 🚀 v4.35.14 [skip ci]

* fix(sonarr): 🐛 Stop the sonarr version endpoint from breaking when Sonarr is down #4895

* chore(release): 🚀 v4.35.15 [skip ci]

* fix: Support duplicates in Emby/JF collections (#4902)

Support same movie that belongs in different collections
in Emby or Jellyfin

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.16 [skip ci]

* fix(discover): Fix denied requests displayed as approved (#4901)

* fix: Fix denied movie shown as 'processing request' in details view (#4900)

* chore(release): 🚀 v4.35.17 [skip ci]

* fix(#4906): 🐛 Fixed an issue with power users and permissions

* chore(release): 🚀 v4.35.18 [skip ci]

* fix(radarr): Fixed an issue where the radarr sync would break

* chore(release): 🚀 v4.35.19 [skip ci]

* feat(discover): Add deny option to recently requested (#4907)

* chore(release): 🚀 v4.36.0 [skip ci]

* fix(healthchecks): Removed redundant ping check

* chore(release): 🚀 v4.36.1 [skip ci]

* feat: Search by genre

[skip-ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.0 [skip ci]

* fix(discover): 🚸 Improved the new Genre buttons, it now includes TV results

* chore: 👥 Updated Contributors [skip ci]

* fix: Cron Validation (#4842)

* Add Cron Next Time Validation

The cron job can't be created if the year is more than 100 years in the future.
Getting the next valid time will return null if this is the case.

* add next cron validation to api
* add next cron validation to job settings page

* Add Missing Import

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4806)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(lidarr): Change monitor to Existing to properly add artist #3597

Discussed and tested manually in https://github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.1 [skip ci]

* fix(jellyfin): Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails

* chore(release): 🚀 v4.37.2 [skip ci]

* fix: Show the ApiAlias in the requests-list

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.3 [skip ci]

* feat(emby): Show watched status for Movie requests

* First step towards played sync

* Change TMDB id format to integer

This will better integrate with TMDB id type in the request model

* Display played state in the requests list

* Fix played status filter

* Run played sync job after content sync instead of on its own

* Add a toggle to activate played sync

* Hoovering

* FIx played sync job not being triggered

* Expose played state according to hide requests setting

* Fix tests

* Fix tests for real

* Add MySql migrations

[skip ci]

* fix: remove sort header

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.0 [skip ci]

* fix(notificaitons): Add the RequestedByAlias field to the Notification Message

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4921)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(API): Allow RequestOnBehalf rights if requested from the API (#4919)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.1 [skip ci]

* Merge pull request from GHSA-28j3-84m7-gpjp

* chore(release): 🚀 v4.38.2 [skip ci]

* Develop master (#4930)

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* Develop master (#4931) [skip ci]

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* fix(emby): Fix Emby played sync running a full sync during recently added sync (#4932)

* feat: Hide watched status when request is not available (#4934)

* chore(release): 🚀 v4.39.0 [skip ci]

* chore(release): 🚀 v4.40.0 [skip ci]

* feat(emby): Show end-user external IP address to Emby when logging in as an Emby user (#4949)

Fixes #4947

* chore: 👥 Updated Contributors [skip ci]

* fix: Fix various styling issues (#4935)

* chore(release): 🚀 v4.41.0 [skip ci]

* chore(release): 🚀 v4.41.1 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4926)

* feat(emby): Show watched status for TV requests

* feat(emby): Show watched status for TV requests

* Consider only requested episodes in  played progress

* Clarify tv watched progress tooltip

* Fix unrespected code guidelines

* chore(release): 🚀 v4.42.0 [skip ci]

* 🌐 Translations Update (#4952)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore: 🔥 Remove unused dependency (#4959) [skip ci]

* chore: 🔥 Remove unused dependency

* chore: 🔥 Remove Angular Localize

* Remove unused dep (#4960) [skip ci]

* chore: 🔥 Remove unused dependency

* chore: 🔥 Remove Angular Localize

* deps update

* fix: upgrade @microsoft/signalr from 6.0.11 to 6.0.16 (#4964) [skip ci]

Snyk has created this PR to upgrade @microsoft/signalr from 6.0.11 to 6.0.16.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade primeng from 15.0.0-rc.1 to 15.4.1 (#4962) [skip ci]

Snyk has created this PR to upgrade primeng from 15.0.0-rc.1 to 15.4.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities (#4969) [skip ci]

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-SYSTEMSECURITYCRYPTOGRAPHYPKCS-5708426

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0 (#4965) [skip ci]

Snyk has created this PR to upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade multiple dependencies with Snyk (#4963) [skip ci]

Snyk has created this PR to upgrade:
  - @angular/animations from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/common from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/compiler from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/core from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/forms from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-browser from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-browser-dynamic from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-server from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/router from 15.2.4 to 15.2.9.
    See this package in npm: 

See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* small improvements to fix flaky tests (#4970) [skip ci]

* fix: More automation tests mainly around the Plex Settings page (#4821)

* updates

* test coverage on the plex settings page

* features

* Update cypress.yml

* Update cypress.yml

* Update cypress.yml

* Update cypress.config.ts

* fixes

* stuff

* put it back

* a

* always kill docker

* Run the wizard as part of the feature files

* fix?

* slow the tests down

* subby

* Update user-preferences-profile.spec.ts

* Update user-preferences-profile.spec.ts

* fix: upgrade cypress-real-events from 1.7.4 to 1.8.1 (#4968) [skip ci]

Snyk has created this PR to upgrade cypress-real-events from 1.7.4 to 1.8.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/750c1ef4-1459-4f30-a181-009a5f1ea1dc?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* chore(release): 🚀 v4.42.1 [skip ci]

* Remove unused dependancy [skip ci]

* chore: updates (#4971) [skip ci]

* docs: Update README.md (#4972)

[skip ci]

* fix: Remove Angular TSLint (#4973)

* Update tslint.json

* Update package.json

[skip ci]

* fix: upgrade zone.js from 0.11.8 to 0.13.0 (#4975)

[skip ci]

Snyk has created this PR to upgrade zone.js from 0.11.8 to 0.13.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade jquery from 3.6.1 to 3.7.0 (#4974)

[skip ci]

Snyk has created this PR to upgrade jquery from 3.6.1 to 3.7.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade multiple dependencies with Snyk (#4961)

Snyk has created this PR to upgrade:
  - @ngxs/devtools-plugin from 3.7.3 to 3.8.1.
    See this package in npm: 
  - @ngxs/store from 3.7.3 to 3.8.1.
    See this package in npm: 

See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* Small fixes (#4978)

* fix(tv-requests): 🐛 Fixed a small bug where an exception can get thrown when attempting to view TV Requests

* fix(user-importer): 🐛 Fixed an issue where the cleanup wouldn't delete users #4812

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.42.2 [skip ci]

* Bug improvements [skip ci]  (#4979)

* Update config.yml

* stuff

* Update bug_report.yml

* fix(user-importer): Do not delete the Plex Admin as part of the user Importer cleanup #4870 (#4981)

* chore(release): 🚀 v4.42.3 [skip ci]

* feat: Add Auto Approve 4K role (#4982) (#4983)

Legacy "Auto Approve" role now only applies to non-4K requests
Fixes #4957

Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>

* chore(release): 🚀 v4.43.0 [skip ci]

* fix(user-importer): don't delete admins in the cleanup

* chore(release): 🚀 v4.43.1 [skip ci]

* fix: Remove old trending source (#4987)

[skip ci]

* fix(plex-api): Switch over to the new API to avoid deprecation & save… (#4986)

* fix(plex-api): Switch over to the new API to avoid deprecation & save the plex settings when modifying the servers

* Delete Ombi.sln

* chore(release): 🚀 v4.43.2 [skip ci]

* fix: switch back to the old plex friends API #4989

* chore(release): 🚀 v4.43.3 [skip ci]

* fix(user-importer): Fixed not importing all correct users #4989

* test: Add a unit test to cover the Unmanaged Home user scenario [skip ci]

* chore(release): 🚀 v4.43.4 [skip ci]

* fix: upgrade cypress-real-events from 1.8.1 to 1.9.1 (#5000) [skip ci]

Snyk has created this PR to upgrade cypress-real-events from 1.8.1 to 1.9.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/750c1ef4-1459-4f30-a181-009a5f1ea1dc?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade @microsoft/signalr from 6.0.18 to 6.0.20 (#4999) [skip ci]

Snyk has created this PR to upgrade @microsoft/signalr from 6.0.18 to 6.0.20.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>
Co-authored-by: ryan-c44 <54028283+ryan-c44@users.noreply.github.com>
Co-authored-by: Alexander Russell <ajex94@gmail.com>
Co-authored-by: Grygon <647846+Grygon@users.noreply.github.com>
Co-authored-by: phildups7 <60622768+phildups7@users.noreply.github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2023-08-24 22:22:54 +02:00
Jamie
dc410a5a3b
Dev to master (#5002)
* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

* fix(sonarr): 🐛 Improved the error handling in the sonarr settings page in the UI

This should hopefully prevent some odd situations where the settings are in a odd state #4877

* chore: update deps

* chore: more deps

* bump

* chore(release): 🚀 v4.35.12 [skip ci]

* fix(sonarr): 🐛 Added some more error handling and information around testing sonarr

#4877

* chore(release): 🚀 v4.35.13 [skip ci]

* fix: Some minor tweaks to the movie info panel (#4883)

* fix: Hide denied reason label if there is no value

* fix: Movie would show as pending approval when denied

* chore(release): 🚀 v4.35.14 [skip ci]

* fix(sonarr): 🐛 Stop the sonarr version endpoint from breaking when Sonarr is down #4895

* chore(release): 🚀 v4.35.15 [skip ci]

* fix: Support duplicates in Emby/JF collections (#4902)

Support same movie that belongs in different collections
in Emby or Jellyfin

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.16 [skip ci]

* fix(discover): Fix denied requests displayed as approved (#4901)

* fix: Fix denied movie shown as 'processing request' in details view (#4900)

* chore(release): 🚀 v4.35.17 [skip ci]

* fix(#4906): 🐛 Fixed an issue with power users and permissions

* chore(release): 🚀 v4.35.18 [skip ci]

* fix(radarr): Fixed an issue where the radarr sync would break

* chore(release): 🚀 v4.35.19 [skip ci]

* feat(discover): Add deny option to recently requested (#4907)

* chore(release): 🚀 v4.36.0 [skip ci]

* fix(healthchecks): Removed redundant ping check

* chore(release): 🚀 v4.36.1 [skip ci]

* feat: Search by genre

[skip-ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.0 [skip ci]

* fix(discover): 🚸 Improved the new Genre buttons, it now includes TV results

* chore: 👥 Updated Contributors [skip ci]

* fix: Cron Validation (#4842)

* Add Cron Next Time Validation

The cron job can't be created if the year is more than 100 years in the future.
Getting the next valid time will return null if this is the case.

* add next cron validation to api
* add next cron validation to job settings page

* Add Missing Import

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4806)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(lidarr): Change monitor to Existing to properly add artist #3597

Discussed and tested manually in https://github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.1 [skip ci]

* fix(jellyfin): Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails

* chore(release): 🚀 v4.37.2 [skip ci]

* fix: Show the ApiAlias in the requests-list

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.3 [skip ci]

* feat(emby): Show watched status for Movie requests

* First step towards played sync

* Change TMDB id format to integer

This will better integrate with TMDB id type in the request model

* Display played state in the requests list

* Fix played status filter

* Run played sync job after content sync instead of on its own

* Add a toggle to activate played sync

* Hoovering

* FIx played sync job not being triggered

* Expose played state according to hide requests setting

* Fix tests

* Fix tests for real

* Add MySql migrations

[skip ci]

* fix: remove sort header

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.0 [skip ci]

* fix(notificaitons): Add the RequestedByAlias field to the Notification Message

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4921)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(API): Allow RequestOnBehalf rights if requested from the API (#4919)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.1 [skip ci]

* Merge pull request from GHSA-28j3-84m7-gpjp

* chore(release): 🚀 v4.38.2 [skip ci]

* Develop master (#4930)

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* Develop master (#4931) [skip ci]

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* fix(emby): Fix Emby played sync running a full sync during recently added sync (#4932)

* feat: Hide watched status when request is not available (#4934)

* chore(release): 🚀 v4.39.0 [skip ci]

* chore(release): 🚀 v4.40.0 [skip ci]

* feat(emby): Show end-user external IP address to Emby when logging in as an Emby user (#4949)

Fixes #4947

* chore: 👥 Updated Contributors [skip ci]

* fix: Fix various styling issues (#4935)

* chore(release): 🚀 v4.41.0 [skip ci]

* chore(release): 🚀 v4.41.1 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4926)

* feat(emby): Show watched status for TV requests

* feat(emby): Show watched status for TV requests

* Consider only requested episodes in  played progress

* Clarify tv watched progress tooltip

* Fix unrespected code guidelines

* chore(release): 🚀 v4.42.0 [skip ci]

* 🌐 Translations Update (#4952)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore: 🔥 Remove unused dependency (#4959) [skip ci]

* chore: 🔥 Remove unused dependency

* chore: 🔥 Remove Angular Localize

* Remove unused dep (#4960) [skip ci]

* chore: 🔥 Remove unused dependency

* chore: 🔥 Remove Angular Localize

* deps update

* fix: upgrade @microsoft/signalr from 6.0.11 to 6.0.16 (#4964) [skip ci]

Snyk has created this PR to upgrade @microsoft/signalr from 6.0.11 to 6.0.16.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade primeng from 15.0.0-rc.1 to 15.4.1 (#4962) [skip ci]

Snyk has created this PR to upgrade primeng from 15.0.0-rc.1 to 15.4.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities (#4969) [skip ci]

The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-DOTNET-SYSTEMSECURITYCRYPTOGRAPHYPKCS-5708426

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0 (#4965) [skip ci]

Snyk has created this PR to upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade multiple dependencies with Snyk (#4963) [skip ci]

Snyk has created this PR to upgrade:
  - @angular/animations from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/common from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/compiler from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/core from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/forms from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-browser from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-browser-dynamic from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/platform-server from 15.2.4 to 15.2.9.
    See this package in npm: 
  - @angular/router from 15.2.4 to 15.2.9.
    See this package in npm: 

See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* small improvements to fix flaky tests (#4970) [skip ci]

* fix: More automation tests mainly around the Plex Settings page (#4821)

* updates

* test coverage on the plex settings page

* features

* Update cypress.yml

* Update cypress.yml

* Update cypress.yml

* Update cypress.config.ts

* fixes

* stuff

* put it back

* a

* always kill docker

* Run the wizard as part of the feature files

* fix?

* slow the tests down

* subby

* Update user-preferences-profile.spec.ts

* Update user-preferences-profile.spec.ts

* fix: upgrade cypress-real-events from 1.7.4 to 1.8.1 (#4968) [skip ci]

Snyk has created this PR to upgrade cypress-real-events from 1.7.4 to 1.8.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/750c1ef4-1459-4f30-a181-009a5f1ea1dc?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* chore(release): 🚀 v4.42.1 [skip ci]

* Remove unused dependancy [skip ci]

* chore: updates (#4971) [skip ci]

* docs: Update README.md (#4972)

[skip ci]

* fix: Remove Angular TSLint (#4973)

* Update tslint.json

* Update package.json

[skip ci]

* fix: upgrade zone.js from 0.11.8 to 0.13.0 (#4975)

[skip ci]

Snyk has created this PR to upgrade zone.js from 0.11.8 to 0.13.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade jquery from 3.6.1 to 3.7.0 (#4974)

[skip ci]

Snyk has created this PR to upgrade jquery from 3.6.1 to 3.7.0.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade multiple dependencies with Snyk (#4961)

Snyk has created this PR to upgrade:
  - @ngxs/devtools-plugin from 3.7.3 to 3.8.1.
    See this package in npm: 
  - @ngxs/store from 3.7.3 to 3.8.1.
    See this package in npm: 

See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* Small fixes (#4978)

* fix(tv-requests): 🐛 Fixed a small bug where an exception can get thrown when attempting to view TV Requests

* fix(user-importer): 🐛 Fixed an issue where the cleanup wouldn't delete users #4812

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.42.2 [skip ci]

* Bug improvements [skip ci]  (#4979)

* Update config.yml

* stuff

* Update bug_report.yml

* fix(user-importer): Do not delete the Plex Admin as part of the user Importer cleanup #4870 (#4981)

* chore(release): 🚀 v4.42.3 [skip ci]

* feat: Add Auto Approve 4K role (#4982) (#4983)

Legacy "Auto Approve" role now only applies to non-4K requests
Fixes #4957

Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>

* chore(release): 🚀 v4.43.0 [skip ci]

* fix(user-importer): don't delete admins in the cleanup

* chore(release): 🚀 v4.43.1 [skip ci]

* fix: Remove old trending source (#4987)

[skip ci]

* fix(plex-api): Switch over to the new API to avoid deprecation & save… (#4986)

* fix(plex-api): Switch over to the new API to avoid deprecation & save the plex settings when modifying the servers

* Delete Ombi.sln

* chore(release): 🚀 v4.43.2 [skip ci]

* fix: switch back to the old plex friends API #4989

* chore(release): 🚀 v4.43.3 [skip ci]

* fix(user-importer): Fixed not importing all correct users #4989

* test: Add a unit test to cover the Unmanaged Home user scenario [skip ci]

* chore(release): 🚀 v4.43.4 [skip ci]

* fix: upgrade cypress-real-events from 1.8.1 to 1.9.1 (#5000) [skip ci]

Snyk has created this PR to upgrade cypress-real-events from 1.8.1 to 1.9.1.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/750c1ef4-1459-4f30-a181-009a5f1ea1dc?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

* fix: upgrade @microsoft/signalr from 6.0.18 to 6.0.20 (#4999) [skip ci]

Snyk has created this PR to upgrade @microsoft/signalr from 6.0.18 to 6.0.20.

See this package in npm:


See this project in Snyk:
https://app.snyk.io/org/ombi-shared/project/7a4dc3b5-498d-41a0-82fb-9781f30ae243?utm_source=github&utm_medium=referral&page=upgrade-pr

Co-authored-by: snyk-bot <snyk-bot@snyk.io>

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>
Co-authored-by: ryan-c44 <54028283+ryan-c44@users.noreply.github.com>
Co-authored-by: Alexander Russell <ajex94@gmail.com>
Co-authored-by: Grygon <647846+Grygon@users.noreply.github.com>
Co-authored-by: phildups7 <60622768+phildups7@users.noreply.github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: snyk-bot <snyk-bot@snyk.io>
2023-08-18 08:22:34 +01:00
Jamie
c96b5a31df
test (#4938)
* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

* fix(sonarr): 🐛 Improved the error handling in the sonarr settings page in the UI

This should hopefully prevent some odd situations where the settings are in a odd state #4877

* chore: update deps

* chore: more deps

* bump

* chore(release): 🚀 v4.35.12 [skip ci]

* fix(sonarr): 🐛 Added some more error handling and information around testing sonarr

#4877

* chore(release): 🚀 v4.35.13 [skip ci]

* fix: Some minor tweaks to the movie info panel (#4883)

* fix: Hide denied reason label if there is no value

* fix: Movie would show as pending approval when denied

* chore(release): 🚀 v4.35.14 [skip ci]

* fix(sonarr): 🐛 Stop the sonarr version endpoint from breaking when Sonarr is down #4895

* chore(release): 🚀 v4.35.15 [skip ci]

* fix: Support duplicates in Emby/JF collections (#4902)

Support same movie that belongs in different collections
in Emby or Jellyfin

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.16 [skip ci]

* fix(discover): Fix denied requests displayed as approved (#4901)

* fix: Fix denied movie shown as 'processing request' in details view (#4900)

* chore(release): 🚀 v4.35.17 [skip ci]

* fix(#4906): 🐛 Fixed an issue with power users and permissions

* chore(release): 🚀 v4.35.18 [skip ci]

* fix(radarr): Fixed an issue where the radarr sync would break

* chore(release): 🚀 v4.35.19 [skip ci]

* feat(discover): Add deny option to recently requested (#4907)

* chore(release): 🚀 v4.36.0 [skip ci]

* fix(healthchecks): Removed redundant ping check

* chore(release): 🚀 v4.36.1 [skip ci]

* feat: Search by genre

[skip-ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.0 [skip ci]

* fix(discover): 🚸 Improved the new Genre buttons, it now includes TV results

* chore: 👥 Updated Contributors [skip ci]

* fix: Cron Validation (#4842)

* Add Cron Next Time Validation

The cron job can't be created if the year is more than 100 years in the future.
Getting the next valid time will return null if this is the case.

* add next cron validation to api
* add next cron validation to job settings page

* Add Missing Import

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4806)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(lidarr): Change monitor to Existing to properly add artist #3597

Discussed and tested manually in https://github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.1 [skip ci]

* fix(jellyfin): Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails

* chore(release): 🚀 v4.37.2 [skip ci]

* fix: Show the ApiAlias in the requests-list

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.37.3 [skip ci]

* feat(emby): Show watched status for Movie requests

* First step towards played sync

* Change TMDB id format to integer

This will better integrate with TMDB id type in the request model

* Display played state in the requests list

* Fix played status filter

* Run played sync job after content sync instead of on its own

* Add a toggle to activate played sync

* Hoovering

* FIx played sync job not being triggered

* Expose played state according to hide requests setting

* Fix tests

* Fix tests for real

* Add MySql migrations

[skip ci]

* fix: remove sort header

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.0 [skip ci]

* fix(notificaitons): Add the RequestedByAlias field to the Notification Message

* chore: 👥 Updated Contributors [skip ci]

* 🌐 Translations Update (#4921)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(API): Allow RequestOnBehalf rights if requested from the API (#4919)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.38.1 [skip ci]

* Merge pull request from GHSA-28j3-84m7-gpjp

* chore(release): 🚀 v4.38.2 [skip ci]

* Develop master (#4930)

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* Develop master (#4931) [skip ci]

* Update (#4871)

* Remove dead code

* Localize TV requests messages on TV details page

* Transform buttons with link into anchors

* Sonarr sync: stop using seasonpass API

* chore(release): 🚀 v4.16.13

* Fix requests when 4k available and 4k disabled

Fixes #4610

* chore(release): 🚀 v4.16.14

* Hide subscribe button when request is available

* Hide subscribe button when request is denied

* Add Title to Partially Available Message

If the Title of the show is not menitoned it can be unclear what Episodes are now available.

* Better error message when test email fails due to missing recipient

* feat(discover): Add original language filter

* chore(release): 🚀 v4.16.15

* fix(4616): 🐛 fixed mandatory fields

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.16.16

* added test results into the PR pipeline

* chore(release): 🚀 v4.16.17

* Add information about cache refresh

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* Update pr.yml

[skip ci]

* chore(release): 🚀 v4.17.0

* feat(discover): Add new trending source experimental feature

* fix(settings): Allow toggling features when there are more than one

* fix(discover): Fix new trending feature detection

* fix(discover): Fix cache mix up

* refactor(discover): Move movie trending feature toggle to backend

* feat(discover): Default trending source to new logic

* chore(release): 🚀 v4.18.0

* feat(sync): Detect reidentified movies in Emby and Jellyfin

* feat(sync): Detect reidentified series in Emby and Jellyfin

* Fix sync log criticity

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* Fix formatting

* Update pr.yml

[skip ci]

* Update label.yml

[skip ci]

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.19.0

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish a series

* refactor(newsletter): Clarify very rare cases where newsletter doesn't publish movie

* chore(release): 🚀 v4.19.1

* feat(discover): Show more relevant shows in upcoming TV

* chore(release): 🚀 v4.20.0

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes

* perf(sync): Emby+Jellyfin - use a more reliable filter to missing items

* fix(sync): Emby+Jellyfin - sync multi-episode files of 3+ episodes [skip ci]

* fix: added media type tag to media type text (#4638)

[skip ci]

* fix(sickrage): Fixed issue with incorrect handling of SiCKRAGE episode results returned during episode status changes, now expects array of objects from data path if present (#4648)

[skip ci]

* fix: Missing Poster broken link fix (#4637)

[skip ci]

* 🌐 Translations Update (#4622)

[skip ci]

* Update launch.json (#4650)

[skip ci]

* fix: Improve Swagger documentation (#4652)

* Upgrade Swashbuckle dependency

* Document /token response

* Add support for Newtonsoft annotations in Swagger

* Remove unecessary ActionResult [skip ci]

* fix(API): Fix pagination in some edge cases (#4649)

[skip ci]

* 🌐 Translations Update (#4655)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): Carousel touch not working when scrolling page and recommendations and similar movie navigation (#4633)

* fixed touch not working on carousels

* fixed touch not working

* Movie details component fixes

Fixed recommendations and similar not changing the data on the component by calling the init function again on param change

Moved the ngif results > 0 to the mat-expansion panel to avoid rendering  the entire element if it doesn't have any results instead of having an empty panel.

* removed unused line, added scroll to top on init

* updated recommendation refresh implementation

Changed the implementation to use the router instead in order to reload the component instead of just reloading the data.

This implementation makes sure the component gets destroyed on navigation eliminating any memory leaks, reloading CSS in case of having animations on page load and generally a continuation of the experience you get when you browse into a movie from the discover page.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.20.1 [skip ci]

* fix: 🐛 Fixed the Request on Behalf of having blanks (#4667)

* chore(release): 🚀 v4.20.2 [skip ci]

* fix(plex): 🐛 Fixed an issue with the Plex Sync

* chore(release): 🚀 v4.20.3 [skip ci]

* fix (technical): Improved some of the date time parsing handling

* fix: fixed build

* chore(release): 🚀 v4.20.4 [skip ci]

* feat: Upgrade to Angular14 (#4668)

* refactor: 🔥 removed angular-bootstrap-md dependancy

* chore: update tsconfig

* yeah

* ng14 upgrade

* refactor: migration changes

* fix: fixed CLI

* test: Fixed automation

* chore: 👥 Updated Contributors [skip ci]

* perf: stop populating obsolete subscribe fields (#4625)

* chore(release): 🚀 v4.21.0 [skip ci]

* fix(images): Retry images with a backoff when we get a Too Many requests from TheMovieDb #4685

* chore(release): 🚀 v4.21.1 [skip ci]

* 🌐 Translations Update (#4683)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Landing and Login page improvements (#4690)

* chore(release): 🚀 v4.21.2 [skip ci]

* feat(discover):  Added infinite scroll on advanced search results

* feat(discover):  Added infinite scroll on advanced search results

* chore(release): 🚀 v4.22.0 [skip ci]

* 🌐 Translations Update (#4694)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(discover): 🐛 Created new Image component to handle 429's from TMDB (#4698) and fixed #4635 (#4699)

* chore(release): 🚀 v4.22.1 [skip ci]

* fix: fixed an issue where I broke images for some users

* chore(release): 🚀 v4.22.2 [skip ci]

* ci(Mergify): configuration update (#4701)

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]

* fix: Override Sonarr V3 Profiles endpoint (#4678)

* Override Sonarr V3 Profiles endpoint [skip ci]

* fix(4K) :4K request fixes (#4702)

* GetRequestsByStatus wasn't implementing the MovieRequests object correctly for 4K quality requests with the ProcessingRequest status.

* Fixed 4K requests not getting automatically approved if the user has the "Auto Approve Movie" role flag enabled.

* Fixed "Request Date" values for the "left-panel-details" div class. Previously when the movie was exclusively 4K (regular request was absent), then "Request Date" equaled DateTime.MinValue (January 1, 0001).

* Fixed "Request Status" evaluation in the "left-panel-details" div class. Now it shows the appropriate status instead of an empty spot. "Request Status" displays both regular and 4K statuses at the same time if needed. Added a comma to the end of the "RequestStatus" label to maintain design consistency with the other labels. Also added a "Denied Reason" element for 4K  requests.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.3 [skip ci]

* chore: Storybook (#4700)

[skip ci]

* chore: Translations

[skip ci]

* 🌐 Translations Update (#4704)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4713)

* build: Run automation tests in docker (#4715)

[skip ci]

* fix: fixed trakt image not loading when base url present (#4711)

[skip ci]

* fix: 🐛 Fixed missing externals (#4712)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.22.4 [skip ci]

* test: fixed automationt tests [skip ci]

* fix: Log Microsoft warnings to log file (#4723)

[skip ci]

* feat:  Recently Requested on Discover Page (#4387)

* chore(release): 🚀 v4.23.0 [skip ci]

* fix: Localize recently requested on discover page (#4729)

[skip ci]

* 🌐 Translations Update (#4731)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* Fix: Ombi.Api.Lidarr: Remove unused fields from ArtistAdd (#4727)

When an artist is not found in Lidarr as part of requesting an album,
MusicSender will make a POST request against the /api/v1/artist endpoint
to add such artist.

Not all fields defined in ArtistAdd are initialized, and those
uninitialized will be `null` values in the JSON body of the request, as
shown in this intercepted request:

```
{
    "addOptions": {
        "AlbumsToMonitor": [
            "e5c48b66-44ef-3685-ad53-45dbcd7294c0"
        ],
        "monitor": 6,
        "monitored": true,
        "searchForMissingAlbums": false
    },
    "added": "2022-08-10T06:49:32.4374278+00:00",
    "albumFolder": true,
    "artistName": "Manolo García",
    "cleanName": "manologarcía",
    "disambiguation": null,
    "discogsId": 0,
    "ended": false,
    "foreignArtistId": "1c8309da-9789-40bf-b9c2-e20064263820",
    "images": [],
    "links": [],
    "metadataProfileId": 1,
    "monitored": true,
    "overview": null,
    "qualityProfileId": 3,
    "ratings": null,
    "remotePoster": null,
    "rootFolderPath": "/media/music/",
    "sortName": null,
    "statistics": null,
    "status": null,
    "tadbId": 0,
    "tags": null
}
```

This request will fail and Lidarr will return a 400 BadRequest error
with the following message:

```
2022-08-10 01:45:52.458 +00:00 [Error] StatusCode: BadRequest, Reason: Bad Request, RequestUri: http://lidarr:8686/api/v1/artist
2022-08-10 01:45:52.459 +00:00 [Debug] {
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-52e01b322a05d7c6633eca2488ef2a5c-06345b3bb8c4bb6c-00",
  "errors": {
    "$.status": [
      "The JSON value could not be converted to NzbDrone.Core.Music.ArtistStatusType. Path: $.status | LineNumber: 0 | BytePositionInLine: 14."
    ]
  }
}
```

Removing all the `null` fields from the JSON body fixes the problem and
correctly adds the artist to Lidarr.

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.23.1 [skip ci]

* fix: Fix conflicting property name for Swagger (#4733)

* chore(release): 🚀 v4.23.2 [skip ci]

* feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>

* 🌐 Translations Update (#4736)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* chore(release): 🚀 v4.24.0 [skip ci]

* feat: Watchlist history errors(#4741)

[skip ci]

* fix: fixed stats controller (#4742)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.25.0 [skip ci]

* fix(webhook): Remove added trailing slash from webhook URL #4710

* chore(release): 🚀 v4.25.1 [skip ci]

* feat(notifications): Add more curly variables for partially available notification

* feat: Add more curly variables for partially available notification

* test: Fix newly added test

* chore(release): 🚀 v4.26.0 [skip ci]

* feat: Recently requested improvements (#4755)

* feat(discover):  Admins can now approve the Recently Requested list

* feat(discover):  Images for the recently requested area are now loading faster and just better all around

* test:  Added automation for the new feature

* chore(release): 🚀 v4.27.0 [skip ci]

* fix(plex): stop the plex sync from deleting episodes when we can't find the plex key

* chore(release): 🚀 v4.27.1 [skip ci]

* refactor: Encapsulate common TV availability checker logic (#4753)

[skip ci]

* fix(sonarr): 🐛 Cleaned up and removed Sonarr v3 option, sonarr v3 is now the default. This allows us to get ready for the upcoming Sonarr v4 (#4764)

* chore(release): 🚀 v4.27.2 [skip ci]

* 🌐 Translations Update (#4739)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(availability): 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area

* chore(release): 🚀 v4.27.3 [skip ci]

* test: 🧪 added full test coverage to the plex availability checker, also fixed a small few bugs in there at the same time

* chore(release): 🚀 v4.27.4 [skip ci]

* fix(importer): 🐛 Allow you to only import Plex Admins without the Plex Users

* chore(release): 🚀 v4.27.5 [skip ci]

* fix(notifications): Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid

* chore(release): 🚀 v4.27.6 [skip ci]

* fix: Fixes default image for recently requested items. (#4767)

* chore(release): 🚀 v4.27.7 [skip ci]

* refactor: Upgrades nuget packages. Removes deprecated packages. Fixes build warnings. (#4769)

* Upgrades nuget packages. Removes deprecated packages. Fixes build warnings.

* Fixes the last few build warnings.

* chore(release): 🚀 v4.27.8 [skip ci]

* refactor: Rework the Plex Settings Page (#4772)

[skip ci]

* feat(plex):  Added the ability to configure the watchlist to request the whole TV show rather than latest season (#4774)

* chore(release): 🚀 v4.28.0 [skip ci]

* 🌐 Translations Update (#4771)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* 🌐 Translations Update (#4775)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix: Reworked the version check (#4719) (#4781)

[skip ci]

* fix(plex): 🐛 Fixed not being able to enable watchlist requests in the Plex settings

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.28.1 [skip ci]

* feat: Provide a flag for missing users on Plex Server (#4688) (#4778)

[skip ci]

* fix: Unable to Delete Jellyfin Server (#4705) (#4780)

[skip ci]

* fix: Partially Available prevents further TV requests (#4768) (#4779)

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.29.0 [skip ci]

* fix: Consistently reset loading flag when requesting movies on discover page. (#4777)

[skip ci]

* fix(sonarr): 🐛 Fixed an issue where the language list didn't correctly load for power users in the advanced options #4782

* chore(release): 🚀 v4.29.1 [skip ci]

* fix(plex): Fixed an issue where sometimes the availability checker would throw an exception when checking episodes

* chore: fixed tests

* chore(release): 🚀 v4.29.2 [skip ci]

* fix: Only log error messages from Microsoft (#4787)

[skip ci]

* 🌐 Translations Update (#4784)

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci]

* fix(notifications): Fixed the Partially TV notifications going to the admin #4797 (#4799)

* chore(release): 🚀 v4.29.3 [skip ci]

* feat(sonarr):  Add the username to a Sonarr tag when sent to Sonarr (#4802)

* chore(release): 🚀 v4.30.0 [skip ci]

* feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

* chore(release): 🚀 v4.31.0 [skip ci]

* fix(translations): 🌐 New translations from Crowdin [skip ci] (#4801)

* feat(plex): Rework the Plex Settings page (#4805)

* chore(release): 🚀 v4.32.0 [skip ci]

* fix(plex): 🐛 Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework

* chore(release): 🚀 v4.32.1 [skip ci]

* fix(sonarr): 🐛 Sonarr V4 should work now (#4810)

* fix(sonarr): 🐛 Sonarr V4 should work now

Auto detect the sonarr version and adjust the UI depending on V3 or V4 (Lang profiles)

* fix: Fixed the load error

* chore(release): 🚀 v4.32.2 [skip ci]

* fix(sonarr): V4 actually works this time around

* chore(release): 🚀 v4.32.3 [skip ci]

* feat: Angular 15 and Dependency upgrades (#4818)

* chore(release): 🚀 v4.33.0 [skip ci]

* fix(plex): Added the watchlist request whole show back into the settings

* chore: undid

* fixed (#4833)

* chore(release): 🚀 v4.33.1 [skip ci]

* chore: add logo [skip ci]

* feat: Radarr tags (#4815)

* chore(release): 🚀 v4.34.0 [skip ci]

* fix(plex-watchlist): Lookup the ID from different sources when Plex doesn't contain the metadata (#4843)

* chore(release): 🚀 v4.34.1 [skip ci]

* feat: Add the option for header authentication to create users (#4841)

* feat: allow SSO to create new users automatically

* feat: apply default user settings to SSO users

* feat: add warnings to header auth toggles

* chore(release): 🚀 v4.35.0 [skip ci]

* fix(plex-watchlist): Index out of bounds error

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.1 [skip ci]

* fix(database): Just some tweaks, shouldn't notice any difference, maybe a less error in the log

* chore(release): 🚀 v4.35.2 [skip ci]

* fix(#4847): Invalid Discord request fixed, also fixed an issue where App Only users would not show as logged in on the user management page (#4848)

* chore(release): 🚀 v4.35.3 [skip ci]

* bug(#4854): 🐛 Fixed the Recently Requested showing requests when it should be hidden

* fix(discover): 🐛 Fixed the default poster not taking into account the base url in some scenarios #4845

* fix(Hide music from navbar and request list when not enabled): 🐛

* chore: 👥 Updated Contributors [skip ci]

* chore(release): 🚀 v4.35.4 [skip ci]

* fix(radarr-settings): 🐛 Fixed a typo

* chore(release): 🚀 v4.35.5 [skip ci]

* fix: Fixed the issue where the login page is still present after logging in with oauth

* chore(release): 🚀 v4.35.6 [skip ci]

* fix(wizard): 🐛 Stop access to the wizard when you have already setup ombi (#4866)

* chore(release): 🚀 v4.35.7 [skip ci]

* fix(plex-oauth): 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user #4835

* chore(release): 🚀 v4.35.8 [skip ci]

* chore: 👥 Updated Contributors [skip ci]

* fixed bad merge

* chore(release): 🚀 v4.35.9 [skip ci]

* Update .gitignore

* Fixed automation

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* chore(release): 🚀 v4.35.10 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>

* fix(emby): Fix Emby played sync running a full sync during recently added sync (#4932)

* feat: Hide watched status when request is not available (#4934)

* chore(release): 🚀 v4.39.0 [skip ci]

---------

Signed-off-by: Jamie <tidusjar@gmail.com> [skip ci]
Co-authored-by: sephrat <34862846+sephrat@users.noreply.github.com>
Co-authored-by: contrib-readme-bot <contrib-readme-action@noreply.com>
Co-authored-by: Conventional Changelog Action <conventional.changelog.action@github.com>
Co-authored-by: dr3amer <91037083+dr3am37@users.noreply.github.com>
Co-authored-by: echel0n <echel0n@sickrage.ca>
Co-authored-by: Marley <55280588+marleypowell@users.noreply.github.com>
Co-authored-by: Igor Borges <igor@borges.dev>
Co-authored-by: Lucane <Lucane@users.noreply.github.com>
Co-authored-by: mkgeeky <github@mkgeeky.xyz>
Co-authored-by: Miguel A Vico Moya <mvicomoya@gmail.com>
Co-authored-by: Hadrien <26697460+ketsapiwiq@users.noreply.github.com>
Co-authored-by: Victor Usoltsev <bernarden@users.noreply.github.com>
Co-authored-by: Wesley King <kingwe92@gmail.com>
Co-authored-by: Lea <me@janderedev.xyz>
Co-authored-by: ryan-c44 <54028283+ryan-c44@users.noreply.github.com>
Co-authored-by: Alexander Russell <ajex94@gmail.com>
Co-authored-by: Grygon <647846+Grygon@users.noreply.github.com>
Co-authored-by: phildups7 <60622768+phildups7@users.noreply.github.com>
Co-authored-by: Teifun2 <Teifun2@users.noreply.github.com>
2023-05-18 16:24:53 +01:00
110 changed files with 8441 additions and 5395 deletions

View file

@ -24,7 +24,7 @@ jobs:
with: with:
node-version: '18' node-version: '18'
- uses: actions/cache@v2 - uses: actions/cache@v4
with: with:
path: | path: |
'**/node_modules' '**/node_modules'

View file

@ -15,7 +15,7 @@ jobs:
node-version: '18' node-version: '18'
- name: NodeModules Cache - name: NodeModules Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: '**/node_modules' path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }} key: node_modules-${{ hashFiles('**/yarn.lock') }}
@ -27,7 +27,7 @@ jobs:
run: yarn --cwd ./src/Ombi/ClientApp run build run: yarn --cwd ./src/Ombi/ClientApp run build
- name: Publish UI Artifacts - name: Publish UI Artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: angular_dist name: angular_dist
path: | path: |
@ -42,7 +42,7 @@ jobs:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -112,7 +112,7 @@ jobs:
dotnet-version: '5.0.x' dotnet-version: '5.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -130,7 +130,7 @@ jobs:
working-directory: src/Ombi working-directory: src/Ombi
- name: Download Angular - name: Download Angular
uses: actions/download-artifact@v2 uses: actions/download-artifact@v4
with: with:
name: angular_dist name: angular_dist
path: ~/src/Ombi/dist path: ~/src/Ombi/dist
@ -156,7 +156,7 @@ jobs:
directory: 'src/Ombi/${{ matrix.os }}' directory: 'src/Ombi/${{ matrix.os }}'
- name: Publish Release - name: Publish Release
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.os }} name: ${{ matrix.os }}
path: | path: |
@ -170,7 +170,7 @@ jobs:
- name: Download Artifacts - name: Download Artifacts
id: download id: download
uses: actions/download-artifact@v2 uses: actions/download-artifact@v4
with: with:
path: artifacts path: artifacts

View file

@ -17,7 +17,7 @@
# fetch-depth: 0 # fetch-depth: 0
# - name: NodeModules Cache # - name: NodeModules Cache
# uses: actions/cache@v2 # uses: actions/cache@v4
# with: # with:
# path: '**/node_modules' # path: '**/node_modules'
# key: node_modules-${{ hashFiles('**/yarn.lock') }} # key: node_modules-${{ hashFiles('**/yarn.lock') }}

View file

@ -20,7 +20,7 @@ jobs:
node-version: '18' node-version: '18'
- name: NodeModules Cache - name: NodeModules Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: '**/node_modules' path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }} key: node_modules-${{ hashFiles('**/yarn.lock') }}
@ -41,7 +41,7 @@ jobs:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -101,7 +101,7 @@ jobs:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
- name: Nuget Cache - name: Nuget Cache
uses: actions/cache@v2 uses: actions/cache@v4
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}

View file

@ -1,3 +1,208 @@
## [4.49.3](https://github.com/Ombi-app/Ombi/compare/v4.49.2...v4.49.3) (2025-08-17)
### Bug Fixes
* **plex-api:** update Plex Watchlist URL ([11fd7a5](https://github.com/Ombi-app/Ombi/commit/11fd7a5fc853da75974a16bf4fdecd72a836f54b))
## [4.49.2](https://github.com/Ombi-app/Ombi/compare/v4.49.1...v4.49.2) (2025-07-12)
### Performance Improvements
* **discover:** :zap: Improve the loading performance on the discover page ([97d5167](https://github.com/Ombi-app/Ombi/commit/97d5167db6c9f915021f32b96b281d7db3741d7f))
## [4.49.1](https://github.com/Ombi-app/Ombi/compare/v4.49.0...v4.49.1) (2025-07-12)
### Bug Fixes
* **auth:** Fixed an issue where refreshing the page as a power user would stop the application from loading [#5242](https://github.com/Ombi-app/Ombi/issues/5242) ([cee4014](https://github.com/Ombi-app/Ombi/commit/cee40146ee02f7fb79e2019d6fe2f9d5c5dbdfc8))
# [4.49.0](https://github.com/Ombi-app/Ombi/compare/v4.48.5...v4.49.0) (2025-07-11)
### Features
* Added the ability for the Watchlist to automatically refresh the users token. This will reduce the need for the user to log in ([067c029](https://github.com/Ombi-app/Ombi/commit/067c029f42e9fd853d060fdb2093013b15ac14c0))
## [4.48.5](https://github.com/Ombi-app/Ombi/compare/v4.48.4...v4.48.5) (2025-05-14)
### Bug Fixes
* filter out excluded notification agents from user preferences ([c9ab4f4](https://github.com/Ombi-app/Ombi/commit/c9ab4f4f9faa66dbf263da693db1eefcf68beeec)), closes [#5196](https://github.com/Ombi-app/Ombi/issues/5196)
## [4.48.4](https://github.com/Ombi-app/Ombi/compare/v4.48.3...v4.48.4) (2025-05-14)
### Bug Fixes
* **translations:** 🌐 New translations from Crowdin [skip ci] ([dbbfdd9](https://github.com/Ombi-app/Ombi/commit/dbbfdd926f0808f6d16f0b2cd8b5406e6b610c82))
* **translations:** 🌐 New translations from Crowdin [skip ci] ([53a6a09](https://github.com/Ombi-app/Ombi/commit/53a6a092b14b8b8bdbff95d066926d3dbe6951f4))
* **ui:** correct timezone handling in OmbiDatePipe ([f88c5ad](https://github.com/Ombi-app/Ombi/commit/f88c5ad818fadea7064e7dfbe46f07eae855109a)), closes [#5102](https://github.com/Ombi-app/Ombi/issues/5102)
## [4.48.3](https://github.com/Ombi-app/Ombi/compare/v4.48.2...v4.48.3) (2025-05-14)
### Bug Fixes
* Correct 4K movie request existence check ([ba6e708](https://github.com/Ombi-app/Ombi/commit/ba6e708e189f52f2ff4ebc073fa38a4f53f1061c)), closes [#4798](https://github.com/Ombi-app/Ombi/issues/4798)
## [4.48.2](https://github.com/Ombi-app/Ombi/compare/v4.48.1...v4.48.2) (2025-05-14)
### Bug Fixes
* **radarr:** ensure RequestedUser is loaded when creating tags ([f8658fe](https://github.com/Ombi-app/Ombi/commit/f8658fe6d56488aa5caa68093245cbf021a31810)), closes [#5045](https://github.com/Ombi-app/Ombi/issues/5045)
## [4.48.1](https://github.com/Ombi-app/Ombi/compare/v4.48.0...v4.48.1) (2025-05-14)
# [4.48.0](https://github.com/Ombi-app/Ombi/compare/v4.47.3...v4.48.0) (2025-05-14)
### Features
* added the watchlist notification ([0dfd453](https://github.com/Ombi-app/Ombi/commit/0dfd4533dba01cb04ea2217c020de8833ddf39c6))
## [4.47.3](https://github.com/Ombi-app/Ombi/compare/v4.47.2...v4.47.3) (2025-04-13)
### Bug Fixes
* [#5223](https://github.com/Ombi-app/Ombi/issues/5223) ([cf0c161](https://github.com/Ombi-app/Ombi/commit/cf0c1614a496b4f7cf19d78e885c3e37dae5cf0f))
## [4.47.2](https://github.com/Ombi-app/Ombi/compare/v4.47.0...v4.47.2) (2025-03-11)
### Bug Fixes
* **user-import:** Do not import users that do not have access to the server [#5064](https://github.com/Ombi-app/Ombi/issues/5064) ([a801cfd](https://github.com/Ombi-app/Ombi/commit/a801cfdb0946cbee3c35b7e917a240f69020f221))
# [4.47.0](https://github.com/Ombi-app/Ombi/compare/v4.46.8...v4.47.0) (2025-01-03)
### Features
* **wizard:** :sparkles: Added the ability to start with a different database ([#5208](https://github.com/Ombi-app/Ombi/issues/5208)) ([cc98fc6](https://github.com/Ombi-app/Ombi/commit/cc98fc6aca27111a8afc3b7b5b8e53207b73fe15))
## [4.46.8](https://github.com/Ombi-app/Ombi/compare/v4.46.7...v4.46.8) (2025-01-03)
### Bug Fixes
* **radarr-settings:** this.normalForm is undefined ([#5207](https://github.com/Ombi-app/Ombi/issues/5207)) ([dc2b958](https://github.com/Ombi-app/Ombi/commit/dc2b958915bf6cb77e093ada843ef6d9f62a3755)), closes [#4994](https://github.com/Ombi-app/Ombi/issues/4994)
## [4.46.7](https://github.com/Ombi-app/Ombi/compare/v4.46.6...v4.46.7) (2024-12-03)
### Bug Fixes
* **requests:** :bug: Power users can now set profiles and root folders when requesting ([138df1e](https://github.com/Ombi-app/Ombi/commit/138df1eb25c709c1939d01d4c9f9ece63f8e0fde))
## [4.46.6](https://github.com/Ombi-app/Ombi/compare/v4.46.5...v4.46.6) (2024-11-24)
## [4.46.5](https://github.com/Ombi-app/Ombi/compare/v4.46.4...v4.46.5) (2024-11-23)
### Bug Fixes
* **Fixed the UI not applying the correct timezone settings:** :bug: ([029ea79](https://github.com/Ombi-app/Ombi/commit/029ea7919220fbc506898733caeb4370053051a7))
## [4.46.4](https://github.com/Ombi-app/Ombi/compare/v4.46.3...v4.46.4) (2024-09-09)
## [4.46.3](https://github.com/Ombi-app/Ombi/compare/v4.46.2...v4.46.3) (2024-09-07)
### Bug Fixes
* **radarr-4k:** :bug: Fixed an issue where the overrides wouldn't work for 4k Requests ([0fb29a0](https://github.com/Ombi-app/Ombi/commit/0fb29a0b16b1fc87f71df1a589f6141324cf2f1b))
## [4.46.2](https://github.com/Ombi-app/Ombi/compare/v4.46.1...v4.46.2) (2024-09-03)
### Bug Fixes
* **radarr:** :bug: Enable validation on the radarr settings page ([0af3511](https://github.com/Ombi-app/Ombi/commit/0af3511e819d24e0f4edf6f33931e61bba743224))
## [4.46.1](https://github.com/Ombi-app/Ombi/compare/v4.46.0...v4.46.1) (2024-08-27)
### Bug Fixes
* src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities ([#5167](https://github.com/Ombi-app/Ombi/issues/5167)) ([e1f2a84](https://github.com/Ombi-app/Ombi/commit/e1f2a848065d79c8bba9eafff4f1f5db4a994b53))
# [4.46.0](https://github.com/Ombi-app/Ombi/compare/v4.45.1...v4.46.0) (2024-08-20)
### Bug Fixes
* **discover:** Improved rendering on the discover page ([ea00d6c](https://github.com/Ombi-app/Ombi/commit/ea00d6c12f4441da243287d0fbc706d66c0afd82))
* src/Ombi.Store/Ombi.Store.csproj to reduce vulnerabilities ([#5160](https://github.com/Ombi-app/Ombi/issues/5160)) ([9c21074](https://github.com/Ombi-app/Ombi/commit/9c2107418939ee92e50c59765481f30efac12eff))
### Features
* upgrade @ngx-translate/core from 14.0.0 to 15.0.0 ([#5158](https://github.com/Ombi-app/Ombi/issues/5158)) ([48d3dec](https://github.com/Ombi-app/Ombi/commit/48d3dec26d36002a9d613432fb7f9232d8801cba))
* upgrade @ngx-translate/http-loader from 7.0.0 to 8.0.0 ([#5159](https://github.com/Ombi-app/Ombi/issues/5159)) ([3bd98c1](https://github.com/Ombi-app/Ombi/commit/3bd98c1d711786bff66f1528dcdddcafe256abd2))
## [4.45.1](https://github.com/Ombi-app/Ombi/compare/v4.45.0...v4.45.1) (2024-08-20)
### Bug Fixes
* **plex:** Fixed some errors around the scanner that was causing the scan to fail ([d9787dc](https://github.com/Ombi-app/Ombi/commit/d9787dc32aace808d196f6f87456ef45de3d7bbf))
# [4.45.0](https://github.com/Ombi-app/Ombi/compare/v4.44.1...v4.45.0) (2024-08-07)
# [4.44.0](https://github.com/Ombi-app/Ombi/compare/v4.43.22...v4.44.0) (2024-07-11) # [4.44.0](https://github.com/Ombi-app/Ombi/compare/v4.43.22...v4.44.0) (2024-07-11)
@ -106,7 +311,7 @@
## [4.43.6](https://github.com/Ombi-app/Ombi/compare/v4.43.4...v4.43.6) (2023-11-01) ## [4.43.6](https://github.com/Ombi-app/Ombi/compare/v4.43.5...v4.43.6) (2023-11-01)
### Bug Fixes ### Bug Fixes
@ -998,15 +1203,257 @@
## [4.39.1](https://github.com/Ombi-app/Ombi/compare/v4.39.0...v4.39.1) (2023-05-18) ## [4.44.1](https://github.com/Ombi-app/Ombi/compare/v4.44.0...v4.44.1) (2024-07-22)
## [4.35.11](https://github.com/Ombi-app/Ombi/compare/v4.38.2...v4.35.11) (2023-05-17) ## [4.43.5](https://github.com/Ombi-app/Ombi/compare/v4.43.4...v4.43.5) (2023-08-24)
## [4.35.10](https://github.com/Ombi-app/Ombi/compare/v4.35.9...v4.35.10) (2023-02-25) # [4.44.0](https://github.com/Ombi-app/Ombi/compare/v4.43.22...v4.44.0) (2024-07-11)
### Features
* Adding postgres support to ombi (beta) ([#5050](https://github.com/Ombi-app/Ombi/issues/5050)) ([f8c6102](https://github.com/Ombi-app/Ombi/commit/f8c61027bf53d657d7955a98b69d7ab90b66a75a))
## [4.43.22](https://github.com/Ombi-app/Ombi/compare/v4.43.21...v4.43.22) (2024-07-08)
## [4.43.21](https://github.com/Ombi-app/Ombi/compare/v4.43.20...v4.43.21) (2024-06-26)
## [4.43.20](https://github.com/Ombi-app/Ombi/compare/v4.43.16...v4.43.20) (2024-05-14)
### Bug Fixes
* upgrade multiple dependencies with Snyk ([#5104](https://github.com/Ombi-app/Ombi/issues/5104)) [skip ci] ([a1083f6](https://github.com/Ombi-app/Ombi/commit/a1083f67c73c968b9ff0a0feebe5a9aac6a7c7c9))
## [4.43.16](https://github.com/Ombi-app/Ombi/compare/v4.43.15...v4.43.16) (2024-04-01)
## [4.43.15](https://github.com/Ombi-app/Ombi/compare/v4.43.14...v4.43.15) (2024-03-30)
### Bug Fixes
* fixed emails not being able to load the template ([6b49d9b](https://github.com/Ombi-app/Ombi/commit/6b49d9bc7108a0b663ca05de19dbf4841c9c43c1))
## [4.43.14](https://github.com/Ombi-app/Ombi/compare/v4.43.13...v4.43.14) (2024-03-06)
### Performance Improvements
* ⚡ Improve render performance on the discover, movie and tv pages ([#5084](https://github.com/Ombi-app/Ombi/issues/5084)) ([71c86a8](https://github.com/Ombi-app/Ombi/commit/71c86a8db9e63bf0ab779f9a8b5d62a42c246392))
## [4.43.13](https://github.com/Ombi-app/Ombi/compare/v4.43.12...v4.43.13) (2024-03-05)
## [4.43.12](https://github.com/Ombi-app/Ombi/compare/v4.43.11...v4.43.12) (2024-03-05)
### Bug Fixes
* src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities ([#5040](https://github.com/Ombi-app/Ombi/issues/5040)) [skip ci] ([955a742](https://github.com/Ombi-app/Ombi/commit/955a742fae1d0a3983c59cf77eb1a2d222f18b48))
* src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities ([#5072](https://github.com/Ombi-app/Ombi/issues/5072)) [skip ci] ([af6a986](https://github.com/Ombi-app/Ombi/commit/af6a9867719deb7b651a6a78352a8ce0df7a0cf0))
* src/Ombi/Ombi.csproj to reduce vulnerabilities ([#5066](https://github.com/Ombi-app/Ombi/issues/5066)) [skip ci] ([71df058](https://github.com/Ombi-app/Ombi/commit/71df05886512b8589f193a5cda0166c694438fc0))
* upgrade @fortawesome/fontawesome-free from 6.4.2 to 6.5.0 ([#5053](https://github.com/Ombi-app/Ombi/issues/5053)) [skip ci] ([5017e38](https://github.com/Ombi-app/Ombi/commit/5017e38f87e32821adb744935fffcb2d76927e2c))
* upgrade @types/jquery from 3.5.27 to 3.5.28 ([#5049](https://github.com/Ombi-app/Ombi/issues/5049)) [skip ci] ([2c8fe80](https://github.com/Ombi-app/Ombi/commit/2c8fe8087aea227e7425e82392ad9ccb3f8261b4))
* upgrade moment from 2.29.4 to 2.30.1 ([#5075](https://github.com/Ombi-app/Ombi/issues/5075)) [skip ci] ([460fa39](https://github.com/Ombi-app/Ombi/commit/460fa39bb95c73bafcd65fcc394fecca04d3ac49))
* upgrade multiple dependencies with Snyk ([#5073](https://github.com/Ombi-app/Ombi/issues/5073)) [skip ci] ([a27b459](https://github.com/Ombi-app/Ombi/commit/a27b4592471c58fca9ad5193b979171fa7c5e66d))
## [4.43.11](https://github.com/Ombi-app/Ombi/compare/v4.43.10...v4.43.11) (2024-01-11)
### Bug Fixes
* **emby:** Add more logging on the PlaySync to check for Tv Shows without a valid TMDB ([08eb13b](https://github.com/Ombi-app/Ombi/commit/08eb13b788582d576a0e1befdb8e84ef7ff0d2f3))
## [4.43.10](https://github.com/Ombi-app/Ombi/compare/v4.43.9...v4.43.10) (2023-11-20)
### Bug Fixes
* **radarr-4k:** 🐛 Fixed an issue when using Radarr 4k with user set quality profiles. There are now user quality profiles for 4k profile [#5025](https://github.com/Ombi-app/Ombi/issues/5025) ([62b9a1f](https://github.com/Ombi-app/Ombi/commit/62b9a1f65fc4ee5f1eaf9dfabf37742a9007433e))
## [4.43.9](https://github.com/Ombi-app/Ombi/compare/v4.43.8...v4.43.9) (2023-11-18)
### Bug Fixes
* **sonarr:** 🐛 Correctly monitor episodes ([57e7830](https://github.com/Ombi-app/Ombi/commit/57e7830f8e54b65ce3c1f0b122cbcc517d1af926))
## [4.43.8](https://github.com/Ombi-app/Ombi/compare/v4.43.7...v4.43.8) (2023-11-09)
## [4.43.7](https://github.com/Ombi-app/Ombi/compare/v4.43.6...v4.43.7) (2023-11-09)
### Bug Fixes
* upgrade @microsoft/signalr from 6.0.22 to 6.0.23 ([#5032](https://github.com/Ombi-app/Ombi/issues/5032)) [skip ci] ([11edac9](https://github.com/Ombi-app/Ombi/commit/11edac961b0b828658be83a9c4e67079345d3b0b))
* upgrade @types/jquery from 3.5.22 to 3.5.23 ([#5034](https://github.com/Ombi-app/Ombi/issues/5034)) [skip ci] ([9e28879](https://github.com/Ombi-app/Ombi/commit/9e28879fc1a479e1bd77ca003e3e748fec27f081))
## [4.43.6](https://github.com/Ombi-app/Ombi/compare/v4.43.5...v4.43.6) (2023-11-01)
### Bug Fixes
* **discord:** 🐛 Fixed an issue where the Icon in the discord notifications wouldn't apply ([32da949](https://github.com/Ombi-app/Ombi/commit/32da949a9547f68c57eb4338f749228b7de167c2))
* src/Ombi/ClientApp/package.json & src/Ombi/ClientApp/yarn.lock to reduce vulnerabilities ([#5010](https://github.com/Ombi-app/Ombi/issues/5010)) [skip ci] ([9c2e1b4](https://github.com/Ombi-app/Ombi/commit/9c2e1b435305d51cc9ab7f5d6932ccd3fa723e6c))
* upgrade @fortawesome/fontawesome-free from 6.4.0 to 6.4.2 ([#5005](https://github.com/Ombi-app/Ombi/issues/5005)) [skip ci] ([f703ff2](https://github.com/Ombi-app/Ombi/commit/f703ff255cf389a60bdece824214d38f57f03f90))
* upgrade @microsoft/signalr from 6.0.18 to 6.0.20 ([#4999](https://github.com/Ombi-app/Ombi/issues/4999)) [skip ci] ([563a044](https://github.com/Ombi-app/Ombi/commit/563a0443ea09ff71c5aa740173b4f3c5627cb543))
* upgrade @microsoft/signalr from 6.0.21 to 6.0.22 ([#5020](https://github.com/Ombi-app/Ombi/issues/5020)) ([1261a44](https://github.com/Ombi-app/Ombi/commit/1261a446e7ffa4d7540623f76ecc9d7e643ca4ce))
* upgrade @types/jquery from 3.5.16 to 3.5.17 ([#5011](https://github.com/Ombi-app/Ombi/issues/5011)) [skip ci] ([40ee175](https://github.com/Ombi-app/Ombi/commit/40ee175ccd6e53b1254833163954b6a1be0d5251))
* upgrade @types/jquery from 3.5.18 to 3.5.19 ([#5022](https://github.com/Ombi-app/Ombi/issues/5022)) ([291425e](https://github.com/Ombi-app/Ombi/commit/291425e6091d90ac07010ff65cc3a53309965546))
* upgrade cypress-real-events from 1.10.0 to 1.10.1 ([#5014](https://github.com/Ombi-app/Ombi/issues/5014)) ([ed06c22](https://github.com/Ombi-app/Ombi/commit/ed06c22fb26fb605b857f3f8e1b26b2dbab15710))
* upgrade cypress-real-events from 1.8.1 to 1.9.1 ([#5000](https://github.com/Ombi-app/Ombi/issues/5000)) [skip ci] ([19e0a88](https://github.com/Ombi-app/Ombi/commit/19e0a886ced344bfe6284f3916fba12826b7de08))
* upgrade jquery from 3.7.0 to 3.7.1 ([#5015](https://github.com/Ombi-app/Ombi/issues/5015)) ([7bc915c](https://github.com/Ombi-app/Ombi/commit/7bc915cc14ed27ad0f35142279f9362e5b7b27b8))
* upgrade multiple dependencies with Snyk ([#5030](https://github.com/Ombi-app/Ombi/issues/5030)) ([7e1e254](https://github.com/Ombi-app/Ombi/commit/7e1e254cfe6c84c1f143388f436d63efd4686e55))
* upgrade zone.js from 0.13.1 to 0.13.2 ([#5019](https://github.com/Ombi-app/Ombi/issues/5019)) ([c5c8dda](https://github.com/Ombi-app/Ombi/commit/c5c8dda7e4f744fa47841efba9f0e8fee9ef67c6))
## [4.43.4](https://github.com/Ombi-app/Ombi/compare/v4.43.3...v4.43.4) (2023-07-28)
### Bug Fixes
* **user-importer:** Fixed not importing all correct users [#4989](https://github.com/Ombi-app/Ombi/issues/4989) ([34c32f8](https://github.com/Ombi-app/Ombi/commit/34c32f8338705ea3f790d95b91c9ada21a41b9f2))
## [4.43.3](https://github.com/Ombi-app/Ombi/compare/v4.43.2...v4.43.3) (2023-07-28)
### Bug Fixes
* switch back to the old plex friends API [#4989](https://github.com/Ombi-app/Ombi/issues/4989) ([c8ad12e](https://github.com/Ombi-app/Ombi/commit/c8ad12eb5f53889609d1793ae907afd33ba6ef38))
## [4.43.2](https://github.com/Ombi-app/Ombi/compare/v4.43.1...v4.43.2) (2023-07-19)
### Bug Fixes
* **plex-api:** Switch over to the new API to avoid deprecation & save… ([#4986](https://github.com/Ombi-app/Ombi/issues/4986)) ([2f2d35e](https://github.com/Ombi-app/Ombi/commit/2f2d35ec867a8e5488e368db294bd37bcf92d843))
* Remove old trending source ([#4987](https://github.com/Ombi-app/Ombi/issues/4987)) ([aacaa3e](https://github.com/Ombi-app/Ombi/commit/aacaa3e140b43f5d196da612f785cc4451717752))
## [4.43.1](https://github.com/Ombi-app/Ombi/compare/v4.43.0...v4.43.1) (2023-07-16)
### Bug Fixes
* **user-importer:** don't delete admins in the cleanup ([895b9bf](https://github.com/Ombi-app/Ombi/commit/895b9bf6a060a678d4b0cca8083aa96c38e47b95))
# [4.43.0](https://github.com/Ombi-app/Ombi/compare/v4.42.3...v4.43.0) (2023-07-14)
### Features
* Add Auto Approve 4K role ([#4982](https://github.com/Ombi-app/Ombi/issues/4982)) ([#4983](https://github.com/Ombi-app/Ombi/issues/4983)) ([ac05495](https://github.com/Ombi-app/Ombi/commit/ac054954254b9d77a42e057f1065570c7fdc1093)), closes [#4957](https://github.com/Ombi-app/Ombi/issues/4957)
## [4.42.3](https://github.com/Ombi-app/Ombi/compare/v4.42.2...v4.42.3) (2023-07-13)
### Bug Fixes
* **user-importer:** Do not delete the Plex Admin as part of the user Importer cleanup [#4870](https://github.com/Ombi-app/Ombi/issues/4870) ([#4981](https://github.com/Ombi-app/Ombi/issues/4981)) ([4e80e7b](https://github.com/Ombi-app/Ombi/commit/4e80e7b7c3239a46a645ab6d1054993734ad4dd6))
## [4.42.2](https://github.com/Ombi-app/Ombi/compare/v4.42.1...v4.42.2) (2023-07-03)
### Bug Fixes
* Remove Angular TSLint ([#4973](https://github.com/Ombi-app/Ombi/issues/4973)) ([93969b5](https://github.com/Ombi-app/Ombi/commit/93969b5a2d82f442299bee418fae43cb590d7743))
* upgrade jquery from 3.6.1 to 3.7.0 ([#4974](https://github.com/Ombi-app/Ombi/issues/4974)) ([f2552ef](https://github.com/Ombi-app/Ombi/commit/f2552ef6ede011080a8d5499e11930c4d41d04c2))
* upgrade multiple dependencies with Snyk ([#4961](https://github.com/Ombi-app/Ombi/issues/4961)) ([3c3edf6](https://github.com/Ombi-app/Ombi/commit/3c3edf6273fa98c420989ebcebfee52b2545e402))
* upgrade zone.js from 0.11.8 to 0.13.0 ([#4975](https://github.com/Ombi-app/Ombi/issues/4975)) ([37f6564](https://github.com/Ombi-app/Ombi/commit/37f65648a2f8742020b0954acec4168aee048942))
## [4.42.1](https://github.com/Ombi-app/Ombi/compare/v4.42.0...v4.42.1) (2023-06-20)
### Bug Fixes
* More automation tests mainly around the Plex Settings page ([#4821](https://github.com/Ombi-app/Ombi/issues/4821)) ([21bfc5a](https://github.com/Ombi-app/Ombi/commit/21bfc5a45adf6da6a80854e19494a8ffdc9c0761))
* src/Ombi.Notifications/Ombi.Notifications.csproj to reduce vulnerabilities ([#4969](https://github.com/Ombi-app/Ombi/issues/4969)) [skip ci] ([8584ad4](https://github.com/Ombi-app/Ombi/commit/8584ad46053c51f5da40b24f3efd1b9e5a031ddd))
* upgrade @fortawesome/fontawesome-free from 6.1.2 to 6.4.0 ([#4965](https://github.com/Ombi-app/Ombi/issues/4965)) [skip ci] ([84454e5](https://github.com/Ombi-app/Ombi/commit/84454e53c00c808e8a393c7750bdc418a7593e91))
* upgrade @microsoft/signalr from 6.0.11 to 6.0.16 ([#4964](https://github.com/Ombi-app/Ombi/issues/4964)) [skip ci] ([a0201e3](https://github.com/Ombi-app/Ombi/commit/a0201e3f585dc52f717e33c46ede35a4eccac736))
* upgrade cypress-real-events from 1.7.4 to 1.8.1 ([#4968](https://github.com/Ombi-app/Ombi/issues/4968)) [skip ci] ([8a24b56](https://github.com/Ombi-app/Ombi/commit/8a24b56299c3bc98bf0d719ba448972aaa7f7461))
* upgrade multiple dependencies with Snyk ([#4963](https://github.com/Ombi-app/Ombi/issues/4963)) [skip ci] ([6025c5e](https://github.com/Ombi-app/Ombi/commit/6025c5ed757438d3a5d79bd36fd789ef0297ce70))
* upgrade primeng from 15.0.0-rc.1 to 15.4.1 ([#4962](https://github.com/Ombi-app/Ombi/issues/4962)) [skip ci] ([23a4fed](https://github.com/Ombi-app/Ombi/commit/23a4fede69898a25b342aed78a8cda553c1fd18d))
# [4.42.0](https://github.com/Ombi-app/Ombi/compare/v4.41.1...v4.42.0) (2023-06-02)
### Bug Fixes
* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4926](https://github.com/Ombi-app/Ombi/issues/4926)) ([151efe1](https://github.com/Ombi-app/Ombi/commit/151efe19d0012b85f317c175da5ab4802ea14e20))
### Features
* **emby:** Show watched status for TV requests ([1f37de0](https://github.com/Ombi-app/Ombi/commit/1f37de08888812b6d130d92bb664a89e89149105))
## [4.41.1](https://github.com/Ombi-app/Ombi/compare/v4.41.0...v4.41.1) (2023-05-27)
# [4.41.0](https://github.com/Ombi-app/Ombi/compare/v4.40.0...v4.41.0) (2023-05-27)
### Bug Fixes
* Fix various styling issues ([#4935](https://github.com/Ombi-app/Ombi/issues/4935)) ([90b934a](https://github.com/Ombi-app/Ombi/commit/90b934a36996c0f489096f3641350a1c0d3b7c89))
### Features
* **emby:** Show end-user external IP address to Emby when logging in as an Emby user ([#4949](https://github.com/Ombi-app/Ombi/issues/4949)) ([79cef7e](https://github.com/Ombi-app/Ombi/commit/79cef7e0f8643e36536a9ea84dd1a07c232403a9)), closes [#4947](https://github.com/Ombi-app/Ombi/issues/4947)
# [4.40.0](https://github.com/Ombi-app/Ombi/compare/v4.39.1...v4.40.0) (2023-05-18)
@ -1764,104 +2211,3 @@
## [4.35.11](https://github.com/Ombi-app/Ombi/compare/v4.38.2...v4.35.11) (2023-05-17)
## [4.35.10](https://github.com/Ombi-app/Ombi/compare/v4.35.9...v4.35.10) (2023-02-25)
## [4.38.2](https://github.com/Ombi-app/Ombi/compare/v4.38.1...v4.38.2) (2023-05-17)
## [4.38.1](https://github.com/Ombi-app/Ombi/compare/v4.38.0...v4.38.1) (2023-05-09)
### Bug Fixes
* **API:** Allow RequestOnBehalf rights if requested from the API ([#4919](https://github.com/Ombi-app/Ombi/issues/4919)) ([bb6dedd](https://github.com/Ombi-app/Ombi/commit/bb6deddfaecb3d6c7c3c6970414444b619bb9106))
* **notificaitons:** Add the RequestedByAlias field to the Notification Message ([7e9c8be](https://github.com/Ombi-app/Ombi/commit/7e9c8bec6b02bb4e11f8db50394e493d4dd07723))
# [4.38.0](https://github.com/Ombi-app/Ombi/compare/v4.37.3...v4.38.0) (2023-05-07)
### Bug Fixes
* remove sort header ([969bc7b](https://github.com/Ombi-app/Ombi/commit/969bc7bb25ea900ab9199509b079b36843e5bd6f))
### Features
* **emby:** Show watched status for Movie requests ([9cfb10b](https://github.com/Ombi-app/Ombi/commit/9cfb10bb1ee69067a6f47bd2c8a72d4e6834350e))
## [4.37.3](https://github.com/Ombi-app/Ombi/compare/v4.37.2...v4.37.3) (2023-05-07)
### Bug Fixes
* Show the ApiAlias in the requests-list ([9ff624c](https://github.com/Ombi-app/Ombi/commit/9ff624ce4646815b239fbb8327117947f0a90e4b))
## [4.37.2](https://github.com/Ombi-app/Ombi/compare/v4.37.1...v4.37.2) (2023-05-03)
### Bug Fixes
* **jellyfin:** Fixed an issue where the sync could stop working. Removed unused properties so the deseralization no longer fails ([0e5e0ad](https://github.com/Ombi-app/Ombi/commit/0e5e0adf862701d0f672beff14ec0aa75e4b5220))
## [4.37.1](https://github.com/Ombi-app/Ombi/compare/v4.37.0...v4.37.1) (2023-05-02)
### Bug Fixes
* Cron Validation ([#4842](https://github.com/Ombi-app/Ombi/issues/4842)) ([97cc42f](https://github.com/Ombi-app/Ombi/commit/97cc42ffa8672e7d0d0996b5fbda7f7fe699da2d))
* **discover:** :children_crossing: Improved the new Genre buttons, it now includes TV results ([b087d60](https://github.com/Ombi-app/Ombi/commit/b087d606ff36565208e564f8856903f2a4098db5))
* **lidarr:** Change monitor to Existing to properly add artist [#3597](https://github.com/Ombi-app/Ombi/issues/3597) ([506f607](https://github.com/Ombi-app/Ombi/commit/506f60773bf1031d0be51ccd34289b855a04ea40)), closes [/github.com/Lidarr/Lidarr/issues/3597#issuecomment-1530804055](https://github.com//github.com/Lidarr/Lidarr/issues/3597/issues/issuecomment-1530804055)
# [4.37.0](https://github.com/Ombi-app/Ombi/compare/v4.36.1...v4.37.0) (2023-04-24)
### Features
* Search by genre ([1837419](https://github.com/Ombi-app/Ombi/commit/18374198f9f2462ba85c5781b0fcc05892728b21))
## [4.36.1](https://github.com/Ombi-app/Ombi/compare/v4.36.0...v4.36.1) (2023-04-20)
### Bug Fixes
* **healthchecks:** Removed redundant ping check ([1751305](https://github.com/Ombi-app/Ombi/commit/1751305064176d2c0135f867773ccc46b03915ec))
# [4.36.0](https://github.com/Ombi-app/Ombi/compare/v4.35.19...v4.36.0) (2023-04-20)
### Features
* **discover:** Add deny option to recently requested ([#4907](https://github.com/Ombi-app/Ombi/issues/4907)) ([78f340e](https://github.com/Ombi-app/Ombi/commit/78f340ee5f309c55690497170897533801957668))
## [4.35.19](https://github.com/Ombi-app/Ombi/compare/v4.35.18...v4.35.19) (2023-04-20)
### Bug Fixes
* **radarr:** Fixed an issue where the radarr sync would break ([de4baad](https://github.com/Ombi-app/Ombi/commit/de4baade9f87248d77106ff1a313a498870f4fb3))

View file

@ -122,10 +122,10 @@ Here are some of the features Ombi has:
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/MattJeanes"> <a href="https://github.com/AmyJeanes">
<img src="https://avatars.githubusercontent.com/u/2363642?v=4" width="50;" alt="MattJeanes"/> <img src="https://avatars.githubusercontent.com/u/2363642?v=4" width="50;" alt="AmyJeanes"/>
<br /> <br />
<sub><b>Matt Jeanes</b></sub> <sub><b>Amy Jeanes</b></sub>
</a> </a>
</td> </td>
<td align="center"> <td align="center">
@ -301,8 +301,8 @@ Here are some of the features Ombi has:
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/deepwather"> <a href="https://github.com/tuxmi">
<img src="https://avatars.githubusercontent.com/u/12274612?v=4" width="50;" alt="deepwather"/> <img src="https://avatars.githubusercontent.com/u/12274612?v=4" width="50;" alt="tuxmi"/>
<br /> <br />
<sub><b>Michael Reber</b></sub> <sub><b>Michael Reber</b></sub>
</a> </a>
@ -659,10 +659,10 @@ Here are some of the features Ombi has:
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/Drewster727"> <a href="https://github.com/sussycatgirl">
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/> <img src="https://avatars.githubusercontent.com/u/26145882?v=4" width="50;" alt="sussycatgirl"/>
<br /> <br />
<sub><b>Drew</b></sub> <sub><b>Lea</b></sub>
</a> </a>
</td> </td>
<td align="center"> <td align="center">
@ -752,13 +752,20 @@ Here are some of the features Ombi has:
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/Alasano"> <a href="https://github.com/alasano">
<img src="https://avatars.githubusercontent.com/u/14372930?v=4" width="50;" alt="Alasano"/> <img src="https://avatars.githubusercontent.com/u/14372930?v=4" width="50;" alt="alasano"/>
<br /> <br />
<sub><b>Aljosa Asanovic</b></sub> <sub><b>Aljosa Asanovic</b></sub>
</a> </a>
</td></tr> </td></tr>
<tr> <tr>
<td align="center">
<a href="https://github.com/AlexandrePicavet">
<img src="https://avatars.githubusercontent.com/u/25816980?v=4" width="50;" alt="AlexandrePicavet"/>
<br />
<sub><b>Alexandre Picavet</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/XanderStrike"> <a href="https://github.com/XanderStrike">
<img src="https://avatars.githubusercontent.com/u/1565303?v=4" width="50;" alt="XanderStrike"/> <img src="https://avatars.githubusercontent.com/u/1565303?v=4" width="50;" alt="XanderStrike"/>
@ -780,13 +787,6 @@ Here are some of the features Ombi has:
<sub><b>Abe Kline</b></sub> <sub><b>Abe Kline</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/sussycatgirl">
<img src="https://avatars.githubusercontent.com/u/26145882?v=4" width="50;" alt="sussycatgirl"/>
<br />
<sub><b>Lea</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/kmlucy"> <a href="https://github.com/kmlucy">
<img src="https://avatars.githubusercontent.com/u/13952475?v=4" width="50;" alt="kmlucy"/> <img src="https://avatars.githubusercontent.com/u/13952475?v=4" width="50;" alt="kmlucy"/>
@ -901,6 +901,13 @@ Here are some of the features Ombi has:
<br /> <br />
<sub><b>Eli</b></sub> <sub><b>Eli</b></sub>
</a> </a>
</td>
<td align="center">
<a href="https://github.com/Drewster727">
<img src="https://avatars.githubusercontent.com/u/4528753?v=4" width="50;" alt="Drewster727"/>
<br />
<sub><b>Drew</b></sub>
</a>
</td></tr> </td></tr>
</table> </table>
<!-- readme: collaborators,contributors -end --> <!-- readme: collaborators,contributors -end -->

File diff suppressed because it is too large Load diff

440
src/.idea/.idea.Ombi/.idea/dbnavigator.xml generated Normal file
View file

@ -0,0 +1,440 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DBNavigator.Project.DDLFileAttachmentManager">
<mappings />
<preferences />
</component>
<component name="DBNavigator.Project.DatabaseAssistantManager">
<assistants />
</component>
<component name="DBNavigator.Project.DatabaseBrowserManager">
<autoscroll-to-editor value="false" />
<autoscroll-from-editor value="true" />
<show-object-properties value="true" />
<loaded-nodes />
</component>
<component name="DBNavigator.Project.DatabaseFileManager">
<open-files />
</component>
<component name="DBNavigator.Project.ExecutionManager">
<retain-sticky-names value="false" />
</component>
<component name="DBNavigator.Project.ParserDiagnosticsManager">
<diagnostics-history />
</component>
<component name="DBNavigator.Project.Settings">
<connections />
<browser-settings>
<general>
<display-mode value="TABBED" />
<navigation-history-size value="100" />
<show-object-details value="false" />
<enable-sticky-paths value="true" />
</general>
<filters>
<object-type-filter>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="true" />
<object-type name="ROLE" enabled="true" />
<object-type name="PRIVILEGE" enabled="true" />
<object-type name="CHARSET" enabled="true" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED_VIEW" enabled="true" />
<object-type name="NESTED_TABLE" enabled="true" />
<object-type name="COLUMN" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET_TRIGGER" enabled="true" />
<object-type name="DATABASE_TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="true" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="TYPE_ATTRIBUTE" enabled="true" />
<object-type name="ARGUMENT" enabled="true" />
<object-type name="JAVA_CLASS" enabled="true" />
<object-type name="JAVA_INNER_CLASS" enabled="true" />
<object-type name="JAVA_FIELD" enabled="true" />
<object-type name="JAVA_METHOD" enabled="true" />
<object-type name="DIMENSION" enabled="true" />
<object-type name="CLUSTER" enabled="true" />
<object-type name="DBLINK" enabled="true" />
<object-type name="CREDENTIAL" enabled="true" />
<object-type name="AI_PROFILE" enabled="true" />
</object-type-filter>
</filters>
<sorting>
<object-type name="COLUMN" sorting-type="NAME" />
<object-type name="FUNCTION" sorting-type="NAME" />
<object-type name="PROCEDURE" sorting-type="NAME" />
<object-type name="ARGUMENT" sorting-type="POSITION" />
<object-type name="TYPE ATTRIBUTE" sorting-type="POSITION" />
</sorting>
<default-editors>
<object-type name="VIEW" editor-type="SELECTION" />
<object-type name="PACKAGE" editor-type="SELECTION" />
<object-type name="TYPE" editor-type="SELECTION" />
</default-editors>
</browser-settings>
<navigation-settings>
<lookup-filters>
<lookup-objects>
<object-type name="SCHEMA" enabled="true" />
<object-type name="USER" enabled="false" />
<object-type name="ROLE" enabled="false" />
<object-type name="PRIVILEGE" enabled="false" />
<object-type name="CHARSET" enabled="false" />
<object-type name="TABLE" enabled="true" />
<object-type name="VIEW" enabled="true" />
<object-type name="MATERIALIZED VIEW" enabled="true" />
<object-type name="INDEX" enabled="true" />
<object-type name="CONSTRAINT" enabled="true" />
<object-type name="DATASET TRIGGER" enabled="true" />
<object-type name="DATABASE TRIGGER" enabled="true" />
<object-type name="SYNONYM" enabled="false" />
<object-type name="SEQUENCE" enabled="true" />
<object-type name="PROCEDURE" enabled="true" />
<object-type name="FUNCTION" enabled="true" />
<object-type name="PACKAGE" enabled="true" />
<object-type name="TYPE" enabled="true" />
<object-type name="JAVA CLASS" enabled="true" />
<object-type name="INNER CLASS" enabled="true" />
<object-type name="JAVA FIELD" enabled="true" />
<object-type name="JAVA METHOD" enabled="true" />
<object-type name="JAVA PARAMETER" enabled="true" />
<object-type name="DIMENSION" enabled="false" />
<object-type name="CLUSTER" enabled="false" />
<object-type name="DBLINK" enabled="false" />
<object-type name="CREDENTIAL" enabled="false" />
</lookup-objects>
<force-database-load value="false" />
<prompt-connection-selection value="true" />
<prompt-schema-selection value="true" />
</lookup-filters>
</navigation-settings>
<dataset-grid-settings>
<general>
<enable-zooming value="true" />
<enable-column-tooltip value="true" />
</general>
<sorting>
<nulls-first value="true" />
<max-sorting-columns value="4" />
</sorting>
<audit-columns>
<column-names value="" />
<visible value="true" />
<editable value="false" />
</audit-columns>
</dataset-grid-settings>
<dataset-editor-settings>
<text-editor-popup>
<active value="false" />
<active-if-empty value="false" />
<data-length-threshold value="100" />
<popup-delay value="1000" />
</text-editor-popup>
<values-actions-popup>
<show-popup-button value="true" />
<element-count-threshold value="1000" />
<data-length-threshold value="250" />
</values-actions-popup>
<general>
<fetch-block-size value="100" />
<fetch-timeout value="30" />
<trim-whitespaces value="true" />
<convert-empty-strings-to-null value="true" />
<select-content-on-cell-edit value="true" />
<large-value-preview-active value="true" />
</general>
<filters>
<prompt-filter-dialog value="true" />
<default-filter-type value="BASIC" />
</filters>
<qualified-text-editor text-length-threshold="300">
<content-types>
<content-type name="Text" enabled="true" />
<content-type name="Properties" enabled="true" />
<content-type name="XML" enabled="true" />
<content-type name="DTD" enabled="true" />
<content-type name="HTML" enabled="true" />
<content-type name="XHTML" enabled="true" />
<content-type name="CSS" enabled="true" />
<content-type name="SQL" enabled="true" />
<content-type name="PL/SQL" enabled="true" />
<content-type name="JavaScript" enabled="true" />
<content-type name="JSON" enabled="true" />
<content-type name="JSON5" enabled="true" />
<content-type name="YAML" enabled="true" />
<content-type name="C#" enabled="true" />
</content-types>
</qualified-text-editor>
<record-navigation>
<navigation-target value="VIEWER" />
</record-navigation>
</dataset-editor-settings>
<code-editor-settings>
<general>
<show-object-navigation-gutter value="false" />
<show-spec-declaration-navigation-gutter value="true" />
<enable-spellchecking value="true" />
<enable-reference-spellchecking value="false" />
</general>
<confirmations>
<save-changes value="false" />
<revert-changes value="true" />
<exit-on-changes value="ASK" />
</confirmations>
</code-editor-settings>
<code-completion-settings>
<filters>
<basic-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="false" />
<filter-element type="OBJECT" id="view" selected="false" />
<filter-element type="OBJECT" id="materialized view" selected="false" />
<filter-element type="OBJECT" id="index" selected="false" />
<filter-element type="OBJECT" id="constraint" selected="false" />
<filter-element type="OBJECT" id="trigger" selected="false" />
<filter-element type="OBJECT" id="synonym" selected="false" />
<filter-element type="OBJECT" id="sequence" selected="false" />
<filter-element type="OBJECT" id="procedure" selected="false" />
<filter-element type="OBJECT" id="function" selected="false" />
<filter-element type="OBJECT" id="package" selected="false" />
<filter-element type="OBJECT" id="type" selected="false" />
<filter-element type="OBJECT" id="dimension" selected="false" />
<filter-element type="OBJECT" id="cluster" selected="false" />
<filter-element type="OBJECT" id="dblink" selected="false" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</basic-filter>
<extended-filter>
<filter-element type="RESERVED_WORD" id="keyword" selected="true" />
<filter-element type="RESERVED_WORD" id="function" selected="true" />
<filter-element type="RESERVED_WORD" id="parameter" selected="true" />
<filter-element type="RESERVED_WORD" id="datatype" selected="true" />
<filter-element type="RESERVED_WORD" id="exception" selected="true" />
<filter-element type="OBJECT" id="schema" selected="true" />
<filter-element type="OBJECT" id="user" selected="true" />
<filter-element type="OBJECT" id="role" selected="true" />
<filter-element type="OBJECT" id="privilege" selected="true" />
<user-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</user-schema>
<public-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</public-schema>
<any-schema>
<filter-element type="OBJECT" id="table" selected="true" />
<filter-element type="OBJECT" id="view" selected="true" />
<filter-element type="OBJECT" id="materialized view" selected="true" />
<filter-element type="OBJECT" id="index" selected="true" />
<filter-element type="OBJECT" id="constraint" selected="true" />
<filter-element type="OBJECT" id="trigger" selected="true" />
<filter-element type="OBJECT" id="synonym" selected="true" />
<filter-element type="OBJECT" id="sequence" selected="true" />
<filter-element type="OBJECT" id="procedure" selected="true" />
<filter-element type="OBJECT" id="function" selected="true" />
<filter-element type="OBJECT" id="package" selected="true" />
<filter-element type="OBJECT" id="type" selected="true" />
<filter-element type="OBJECT" id="dimension" selected="true" />
<filter-element type="OBJECT" id="cluster" selected="true" />
<filter-element type="OBJECT" id="dblink" selected="true" />
</any-schema>
</extended-filter>
</filters>
<sorting enabled="true">
<sorting-element type="RESERVED_WORD" id="keyword" />
<sorting-element type="RESERVED_WORD" id="datatype" />
<sorting-element type="OBJECT" id="column" />
<sorting-element type="OBJECT" id="table" />
<sorting-element type="OBJECT" id="view" />
<sorting-element type="OBJECT" id="materialized view" />
<sorting-element type="OBJECT" id="index" />
<sorting-element type="OBJECT" id="constraint" />
<sorting-element type="OBJECT" id="trigger" />
<sorting-element type="OBJECT" id="synonym" />
<sorting-element type="OBJECT" id="sequence" />
<sorting-element type="OBJECT" id="procedure" />
<sorting-element type="OBJECT" id="function" />
<sorting-element type="OBJECT" id="package" />
<sorting-element type="OBJECT" id="type" />
<sorting-element type="OBJECT" id="dimension" />
<sorting-element type="OBJECT" id="cluster" />
<sorting-element type="OBJECT" id="dblink" />
<sorting-element type="OBJECT" id="schema" />
<sorting-element type="OBJECT" id="role" />
<sorting-element type="OBJECT" id="user" />
<sorting-element type="RESERVED_WORD" id="function" />
<sorting-element type="RESERVED_WORD" id="parameter" />
</sorting>
<format>
<enforce-code-style-case value="true" />
</format>
</code-completion-settings>
<execution-engine-settings>
<statement-execution>
<fetch-block-size value="100" />
<execution-timeout value="20" />
<debug-execution-timeout value="600" />
<focus-result value="false" />
<prompt-execution value="false" />
</statement-execution>
<script-execution>
<command-line-interfaces />
<execution-timeout value="300" />
</script-execution>
<method-execution>
<execution-timeout value="30" />
<debug-execution-timeout value="600" />
<parameter-history-size value="10" />
</method-execution>
</execution-engine-settings>
<operation-settings>
<transactions>
<uncommitted-changes>
<on-project-close value="ASK" />
<on-disconnect value="ASK" />
<on-autocommit-toggle value="ASK" />
</uncommitted-changes>
<multiple-uncommitted-changes>
<on-commit value="ASK" />
<on-rollback value="ASK" />
</multiple-uncommitted-changes>
</transactions>
<session-browser>
<disconnect-session value="ASK" />
<kill-session value="ASK" />
<reload-on-filter-change value="false" />
</session-browser>
<compiler>
<compile-type value="KEEP" />
<compile-dependencies value="ASK" />
<always-show-controls value="false" />
</compiler>
</operation-settings>
<ddl-file-settings>
<extensions>
<mapping file-type-id="VIEW" extensions="vw" />
<mapping file-type-id="TRIGGER" extensions="trg" />
<mapping file-type-id="PROCEDURE" extensions="prc" />
<mapping file-type-id="FUNCTION" extensions="fnc" />
<mapping file-type-id="PACKAGE" extensions="pkg" />
<mapping file-type-id="PACKAGE_SPEC" extensions="pks" />
<mapping file-type-id="PACKAGE_BODY" extensions="pkb" />
<mapping file-type-id="TYPE" extensions="tpe" />
<mapping file-type-id="TYPE_SPEC" extensions="tps" />
<mapping file-type-id="TYPE_BODY" extensions="tpb" />
<mapping file-type-id="JAVA_SOURCE" extensions="sql" />
</extensions>
<general>
<lookup-ddl-files value="true" />
<create-ddl-files value="false" />
<synchronize-ddl-files value="true" />
<use-qualified-names value="false" />
<make-scripts-rerunnable value="true" />
</general>
</ddl-file-settings>
<assistant-settings>
<credential-settings>
<credentials />
</credential-settings>
</assistant-settings>
<general-settings>
<regional-settings>
<date-format value="MEDIUM" />
<number-format value="UNGROUPED" />
<locale value="SYSTEM_DEFAULT" />
<use-custom-formats value="false" />
</regional-settings>
<environment>
<environment-types>
<environment-type id="development" name="Development" description="Development environment" color="-2430209/-12296320" readonly-code="false" readonly-data="false" />
<environment-type id="integration" name="Integration" description="Integration environment" color="-2621494/-12163514" readonly-code="true" readonly-data="false" />
<environment-type id="production" name="Production" description="Productive environment" color="-11574/-10271420" readonly-code="true" readonly-data="true" />
<environment-type id="other" name="Other" description="" color="-1576/-10724543" readonly-code="false" readonly-data="false" />
</environment-types>
<visibility-settings>
<connection-tabs value="true" />
<dialog-headers value="true" />
<object-editor-tabs value="true" />
<script-editor-tabs value="false" />
<execution-result-tabs value="true" />
</visibility-settings>
</environment>
</general-settings>
</component>
</project>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ContentModelUserStore"> <component name="UserContentModel">
<attachedFolders /> <attachedFolders />
<explicitIncludes /> <explicitIncludes />
<explicitExcludes /> <explicitExcludes />

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.Ombi/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.Ombi/riderModule.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderProjectSettingsUpdater">
<option name="singleClickDiffPreview" value="1" />
<option name="vcsConfiguration" value="3" />
</component>
</project>

View file

@ -1,25 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment=""> <list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/config/applicationhost.config" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/config/applicationhost.config" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/media-details/components/artist/artist-details.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/services/searchV2.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" afterDir="false" />
</list> </list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </component>
<component name="DpaMonitoringSettings">
<option name="firstShow" value="false" />
</component>
<component name="FileEditorManager"> <component name="FileEditorManager">
<leaf> <leaf>
<file pinned="false" current-in-tab="false"> <file pinned="false" current-in-tab="false">
@ -237,27 +232,75 @@
<component name="Git.Settings"> <component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component> </component>
<component name="GitHubPullRequestSearchHistory">{
&quot;lastFilter&quot;: {
&quot;state&quot;: &quot;OPEN&quot;,
&quot;assignee&quot;: &quot;tidusjar&quot;
}
}</component>
<component name="GitToolBoxStore">
<option name="recentBranches">
<RecentBranches>
<option name="branchesForRepo">
<list>
<RecentBranchesForRepo>
<option name="branches">
<list>
<RecentBranch>
<option name="branchName" value="wizard-database" />
<option name="lastUsedInstant" value="1735917525" />
</RecentBranch>
<RecentBranch>
<option name="branchName" value="develop" />
<option name="lastUsedInstant" value="1735917524" />
</RecentBranch>
</list>
</option>
<option name="repositoryRootUrl" value="file://$PROJECT_DIR$/.." />
</RecentBranchesForRepo>
</list>
</option>
</RecentBranches>
</option>
</component>
<component name="GithubProjectSettings">
<option name="branchProtectionPatterns">
<list>
<option value="master" />
<option value="develop" />
</list>
</option>
</component>
<component name="GithubPullRequestsUISettings">{
&quot;selectedUrlAndAccountId&quot;: {
&quot;url&quot;: &quot;https://github.com/ombi-app/ombi&quot;,
&quot;accountId&quot;: &quot;22dd09fe-fb9e-48a4-bfcc-3c152edf3f25&quot;
}
}</component>
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/990126b794024fe2bd16aebdd37eba1d7b600/93/25662f04/ServerVersion.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7/DbContext.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/449b441523c469ed34ff5a5e14f0bafcd8f097aa463655303dc19048fa44ac3/EntityFrameworkServiceCollectionExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/Models/Artist/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7d81b2d4f22bee75e5438c707251ae43cb0974c207db91ffc159118c84b4eb9/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a424e6912048b4cd25715f158e789aae24db5c2911d9e622d39bc6ac3246c6/MySqlConnectionStringBuilder.cs" root0="SKIP_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bd1d5c50194fea68ff3559c160230b0ab50f5acf4ce3061bffd6d62958e2182/ExceptionDispatchInfo.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e9881a453a581134c1a18331ac1f8f1201a5382a685bf2a40777fa22619/DbContextOptions`.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/IMusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/Interfaces/IMusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/MusicSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/RecentlyAddedEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/UserStatsEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="mock:///Dummy.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/IMultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MusicSearchEngineV2.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Core/Models/Search/V2/Music/ArtistInformation.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.DependencyInjection/IocExtensions.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Helpers.Tests/EmbyHelperTests.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi.Schedule.Tests/OmbiQuartzTests.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Controllers/V2/SearchController.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$PROJECT_DIR$/Ombi/Program.cs" root0="FORCE_HIGHLIGHTING" />
</component> </component>
<component name="IdeDocumentHistory"> <component name="IdeDocumentHistory">
<option name="CHANGED_PATHS"> <option name="CHANGED_PATHS">
@ -275,12 +318,17 @@
<component name="PackageJsonUpdateNotifier"> <component name="PackageJsonUpdateNotifier">
<dismissed value="$PROJECT_DIR$/Ombi/ClientApp/package.json" /> <dismissed value="$PROJECT_DIR$/Ombi/ClientApp/package.json" />
</component> </component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;&quot;,
&quot;associatedIndex&quot;: 0
}</component>
<component name="ProjectFrameBounds" extendedState="6"> <component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="1087" /> <option name="x" value="1087" />
<option name="y" value="-1113" /> <option name="y" value="-1113" />
<option name="width" value="1400" /> <option name="width" value="1400" />
<option name="height" value="1000" /> <option name="height" value="1000" />
</component> </component>
<component name="ProjectId" id="2wGwbN5gDqLwyiO1WJdlwJzZ5M9" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true"> <component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="2" id="Add" /> <ConfirmationsSetting value="2" id="Add" />
</component> </component>
@ -343,27 +391,26 @@
<pane id="FileSystemExplorer" /> <pane id="FileSystemExplorer" />
</panes> </panes>
</component> </component>
<component name="PropertiesComponent"> <component name="ProjectViewState">
<property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<property name="Rider.DefaultBreakpoints.AreToggled" value="true" /> <option name="showLibraryContents" value="true" />
<property name="Rider.ProjectViewActivator.IsNotFirstRun" value="true" />
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="nodejs_package_manager_path" value="npm" />
</component> </component>
<component name="RunDashboard"> <component name="PropertiesComponent"><![CDATA[{
<option name="ruleStates"> "keyToString": {
<list> ".NET Launch Settings Profile.Ombi.Schedule.Tests.executor": "Run",
<RuleState> ".NET Launch Settings Profile.Ombi.executor": "Debug",
<option name="name" value="ConfigurationTypeDashboardGroupingRule" /> "RunOnceActivity.ShowReadmeOnStart": "true",
</RuleState> "RunOnceActivity.git.unshallow": "true",
<RuleState> "fb34c741-04ca-4b4f-8ea1-651a011b42c8.executor": "Debug",
<option name="name" value="StatusDashboardGroupingRule" /> "git-widget-placeholder": "watchlist-expired-notification",
</RuleState> "node.js.detected.package.eslint": "true",
</list> "node.js.selected.package.eslint": "(autodetect)",
</option> "node.js.selected.package.tslint": "(autodetect)",
</component> "nodejs_package_manager_path": "yarn",
<component name="RunManager" selected=".NET Launch Settings Profile.Ombi: IIS Express"> "vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager" selected=".NET Launch Settings Profile.Ombi">
<configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" /> <option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/Ombi/Ombi.csproj" />
<option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" /> <option name="LAUNCH_PROFILE_TFM" value=".NETCoreApp,Version=v2.2" />
@ -376,7 +423,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -391,7 +438,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Schedule.Tests" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -406,7 +453,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Schedule.Tests: IIS Express" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -421,7 +468,7 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" />
</method> </method>
</configuration> </configuration>
<configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile"> <configuration name="Ombi.Updater" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
@ -436,10 +483,11 @@
<option name="SEND_DEBUG_REQUEST" value="1" /> <option name="SEND_DEBUG_REQUEST" value="1" />
<option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" /> <option name="ADDITIONAL_IIS_EXPRESS_ARGUMENTS" value="" />
<method v="2"> <method v="2">
<option name="Build" enabled="true" /> <option name="Build" />
</method> </method>
</configuration> </configuration>
</component> </component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager"> <component name="TaskManager">
<task active="true" id="Default" summary="Default task"> <task active="true" id="Default" summary="Default task">
<changelist id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="" /> <changelist id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="" />
@ -448,6 +496,9 @@
<option name="presentableId" value="Default" /> <option name="presentableId" value="Default" />
<updated>1563957157468</updated> <updated>1563957157468</updated>
<workItem from="1563957162999" duration="5401000" /> <workItem from="1563957162999" duration="5401000" />
<workItem from="1745681294313" duration="1814000" />
<workItem from="1747080279165" duration="838000" />
<workItem from="1747082180432" duration="1994000" />
</task> </task>
<servers /> <servers />
</component> </component>
@ -493,7 +544,11 @@
</layout> </layout>
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" /> <option name="version" value="3" />
</component>
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
<component name="VcsManagerConfiguration">
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>
@ -505,7 +560,7 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url> <url>file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs</url>
<line>48</line> <line>48</line>
<properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs" initialLine="48"> <properties documentPath="$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs">
<startOffsets> <startOffsets>
<option value="1518" /> <option value="1518" />
</startOffsets> </startOffsets>
@ -518,12 +573,12 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url> <url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
<line>59</line> <line>59</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="59"> <properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'">
<startOffsets> <startOffsets>
<option value="2276" /> <option value="2369" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="2316" /> <option value="2576" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="4" /> <option name="timeStamp" value="4" />
@ -531,12 +586,12 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url> <url>file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs</url>
<line>49</line> <line>49</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" initialLine="49"> <properties documentPath="$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs" containingFunctionPresentation="Method 'MultiSearch'">
<startOffsets> <startOffsets>
<option value="2001" /> <option value="1903" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="2002" /> <option value="1945" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="5" /> <option name="timeStamp" value="5" />
@ -544,16 +599,55 @@
<line-breakpoint enabled="true" type="DotNet Breakpoints"> <line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url> <url>file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs</url>
<line>30</line> <line>30</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" initialLine="30"> <properties documentPath="$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs" containingFunctionPresentation="Method 'SearchArtist'">
<startOffsets> <startOffsets>
<option value="917" /> <option value="833" />
</startOffsets> </startOffsets>
<endOffsets> <endOffsets>
<option value="1016" /> <option value="834" />
</endOffsets> </endOffsets>
</properties> </properties>
<option name="timeStamp" value="7" /> <option name="timeStamp" value="7" />
</line-breakpoint> </line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>110</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="5123" />
</startOffsets>
<endOffsets>
<option value="5206" />
</endOffsets>
</properties>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>77</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="3324" />
</startOffsets>
<endOffsets>
<option value="3365" />
</endOffsets>
</properties>
<option name="timeStamp" value="11" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DotNet Breakpoints">
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
<line>100</line>
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
<startOffsets>
<option value="4602" />
</startOffsets>
<endOffsets>
<option value="4636" />
</endOffsets>
</properties>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints> </breakpoints>
</breakpoint-manager> </breakpoint-manager>
<watches-manager> <watches-manager>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RIDER_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$USER_HOME$/.nuget/packages/microsoft.net.test.sdk/16.0.1/build/netcoreapp1.0" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/NUnit3.TestAdapter.dll" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/NUnit3.TestAdapter.pdb" />
<content url="file://$USER_HOME$/.nuget/packages/nunit3testadapter/3.13.0/build/netcoreapp1.0/nunit.engine.netstandard.dll" />
<content url="file://$MODULE_DIR$/../../../CHANGELOG.md" />
<content url="file://$MODULE_DIR$/../../../appveyor.yml" />
<content url="file://$MODULE_DIR$/../../../build.cake" />
<content url="file://$MODULE_DIR$/../.." />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,22 +1,31 @@
{ {
"version": "0.1.0", "version": "2.0.0",
"command": "dotnet", "command": "dotnet",
"isShellCommand": true,
"args": [], "args": [],
"tasks": [ "tasks": [
{ {
"taskName": "build", "label": "build",
"type": "shell",
"command": "dotnet",
"args": [ "args": [
"build",
"${workspaceRoot}/Ombi/Ombi.csproj" "${workspaceRoot}/Ombi/Ombi.csproj"
], ],
"isBuildCommand": true, "problemMatcher": "$msCompile",
"problemMatcher": "$msCompile" "group": {
"_id": "build",
"isDefault": false
}
}, },
{ {
"taskName": "lint", "label": "lint",
"type": "shell",
"command": "npm", "command": "npm",
"isShellCommand": true, "args": [
"args": ["run", "lint"] "run",
"lint"
],
"problemMatcher": []
} }
] ]
} }

View file

@ -29,5 +29,6 @@ namespace Ombi.Api.Plex
Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs); Task<PlexAddWrapper> AddUser(string emailAddress, string serverId, string authToken, int[] libs);
Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken); Task<PlexWatchlistContainer> GetWatchlist(string plexToken, CancellationToken cancellationToken);
Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken); Task<PlexWatchlistMetadataContainer> GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken);
Task<bool> Ping(string authToken, CancellationToken cancellationToken = default);
} }
} }

View file

@ -22,6 +22,18 @@ namespace Ombi.Api.Plex.Models.Friends
/// </summary> /// </summary>
[XmlAttribute(AttributeName = "home")] [XmlAttribute(AttributeName = "home")]
public bool HomeUser { get; set; } public bool HomeUser { get; set; }
[XmlElement(ElementName = "Server")]
public PlexUserServer[] Server { get; set; }
}
public class PlexUserServer
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "serverId")]
public string ServerId { get; set; }
} }
[XmlRoot(ElementName = "MediaContainer")] [XmlRoot(ElementName = "MediaContainer")]

View file

@ -68,7 +68,7 @@ namespace Ombi.Api.Plex
private const string FriendsUri = "https://plex.tv/api/users"; private const string FriendsUri = "https://plex.tv/api/users";
private const string GetAccountUri = "https://plex.tv/users/account.json"; private const string GetAccountUri = "https://plex.tv/users/account.json";
private const string ServerUri = "https://plex.tv/pms/servers.xml"; private const string ServerUri = "https://plex.tv/pms/servers.xml";
private const string WatchlistUri = "https://metadata.provider.plex.tv/"; private const string WatchlistUri = "https://discover.provider.plex.tv/";
/// <summary> /// <summary>
/// Sign into the Plex API /// Sign into the Plex API
@ -320,6 +320,30 @@ namespace Ombi.Api.Plex
return result; return result;
} }
/// <summary>
/// Pings the Plex API to validate if a token is still valid
/// </summary>
/// <param name="authToken">The authentication token to validate</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if the token is valid, false otherwise</returns>
public async Task<bool> Ping(string authToken, CancellationToken cancellationToken = default)
{
try
{
var request = new Request("api/v2/ping", "https://plex.tv/", HttpMethod.Get);
await AddHeaders(request, authToken);
// We don't need to parse the response, just check if the request succeeds
await Api.Request(request, cancellationToken);
return true;
}
catch
{
// If the request fails (401, 403, etc.), the token is invalid
return false;
}
}
/// <summary> /// <summary>
/// Adds the required headers and also the authorization header /// Adds the required headers and also the authorization header

View file

@ -0,0 +1,52 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Plex;
namespace Ombi.Core.Authentication
{
public interface IPlexTokenKeepAliveService
{
Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken);
}
public class PlexTokenKeepAliveService : IPlexTokenKeepAliveService
{
private readonly IPlexApi _plexApi;
private readonly ILogger<PlexTokenKeepAliveService> _logger;
public PlexTokenKeepAliveService(IPlexApi plexApi, ILogger<PlexTokenKeepAliveService> logger)
{
_plexApi = plexApi;
_logger = logger;
}
public async Task<bool> KeepTokenAliveAsync(string token, CancellationToken cancellationToken)
{
try
{
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("Token is null or empty");
return false;
}
// Use the Ping method to validate the token
var isValid = await _plexApi.Ping(token, cancellationToken);
if (!isValid)
{
_logger.LogWarning("Token validation failed - token may be expired or invalid");
}
return isValid;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while keeping token alive");
return false;
}
}
}
}

View file

@ -598,13 +598,13 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K) public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K)
{ {
var request = await MovieRepository.Find(requestId); var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId);
return await ApproveMovie(request, is4K); return await ApproveMovie(request, is4K);
} }
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K) public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K)
{ {
var request = await MovieRepository.Find(modelId); var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -790,7 +790,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken) public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{ {
var request = await MovieRepository.Find(requestId); var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == requestId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -805,7 +805,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K) public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{ {
var request = await MovieRepository.Find(modelId); var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -834,7 +834,7 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K) public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{ {
var request = await MovieRepository.Find(modelId); var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null) if (request == null)
{ {
return new RequestEngineResult return new RequestEngineResult

View file

@ -0,0 +1,67 @@
using System;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
using Ombi.Core.Models;
using Polly;
using Pomelo.EntityFrameworkCore.MySql.Storage.Internal;
namespace Ombi.Core.Helpers;
public static class DatabaseConfigurationSetup
{
public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
options.UseNpgsql(config.ConnectionString, b =>
{
b.EnableRetryOnFailure();
}).ReplaceService<ISqlGenerationHelper, NpgsqlCaseInsensitiveSqlGenerationHelper>();
}
public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
if (string.IsNullOrEmpty(config.ConnectionString))
{
throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
}
options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
{
//b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
b.EnableRetryOnFailure();
});
}
private static ServerVersion GetServerVersion(string connectionString)
{
// Workaround Windows bug, that can lead to the following exception:
//
// MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
// ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
// ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
//
// See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
//
// Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
ServerVersion serverVersion = null;
#pragma warning disable EF1001
var retryPolicy = Policy.Handle<Exception>(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
#pragma warning restore EF1001
.WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
return serverVersion;
}
public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
{
const string EFMigrationsHisory = "__EFMigrationsHistory";
public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
: base(dependencies) { }
public override string DelimitIdentifier(string identifier) =>
base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
public override void DelimitIdentifier(StringBuilder builder, string identifier)
=> base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
}
}

View file

@ -0,0 +1,10 @@
namespace Ombi.Core.Helpers;
public class FileSystem : IFileSystem
{
public bool FileExists(string path)
{
return System.IO.File.Exists(path);
}
// Implement other file system operations as needed
}

View file

@ -0,0 +1,7 @@
namespace Ombi.Core.Helpers;
public interface IFileSystem
{
bool FileExists(string path);
// Add other file system operations as needed
}

View file

@ -0,0 +1,40 @@
using System.IO;
namespace Ombi.Core.Models;
public class DatabaseConfiguration
{
public const string SqliteDatabase = "Sqlite";
public DatabaseConfiguration()
{
}
public DatabaseConfiguration(string defaultSqlitePath)
{
OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
}
public PerDatabaseConfiguration OmbiDatabase { get; set; }
public PerDatabaseConfiguration SettingsDatabase { get; set; }
public PerDatabaseConfiguration ExternalDatabase { get; set; }
}
public class PerDatabaseConfiguration
{
public PerDatabaseConfiguration(string type, string connectionString)
{
Type = type;
ConnectionString = connectionString;
}
// Used in Deserialization
public PerDatabaseConfiguration()
{
}
public string Type { get; set; }
public string ConnectionString { get; set; }
}

View file

@ -182,8 +182,11 @@ namespace Ombi.Core.Senders
if (settings.SendUserTags) if (settings.SendUserTags)
{ {
var userTag = await GetOrCreateTag(model, settings); var userTag = await GetOrCreateTag(model, settings);
if (userTag != null)
{
tags.Add(userTag.id); tags.Add(userTag.id);
} }
}
// Overrides on the request take priority // Overrides on the request take priority
if (model.QualityOverride > 0) if (model.QualityOverride > 0)
@ -198,7 +201,9 @@ namespace Ombi.Core.Senders
List<MovieResponse> movies; List<MovieResponse> movies;
// Check if the movie already exists? Since it could be unmonitored // Check if the movie already exists? Since it could be unmonitored
movies = await _radarrV3Api.GetMovies(settings.ApiKey, settings.FullUri); // Get the appropriate Radarr instance settings for existence check
var existenceCheckSettings = is4k ? await _radarr4KSettings.GetSettingsAsync() : settings;
movies = await _radarrV3Api.GetMovies(existenceCheckSettings.ApiKey, existenceCheckSettings.FullUri);
var existingMovie = movies.FirstOrDefault(x => x.tmdbId == model.TheMovieDbId); var existingMovie = movies.FirstOrDefault(x => x.tmdbId == model.TheMovieDbId);
if (existingMovie == null) if (existingMovie == null)
@ -246,6 +251,12 @@ namespace Ombi.Core.Senders
private async Task<Tag> GetOrCreateTag(MovieRequests model, RadarrSettings s) private async Task<Tag> GetOrCreateTag(MovieRequests model, RadarrSettings s)
{ {
if (model.RequestedUser == null)
{
_log.LogWarning("Cannot create tag - RequestedUser is null for movie request {MovieTitle}", model.Title);
return null;
}
var tagName = model.RequestedUser.UserName; var tagName = model.RequestedUser.UserName;
// Does tag exist? // Does tag exist?

View file

@ -133,7 +133,14 @@ namespace Ombi.Core.Senders
string seriesType; string seriesType;
int? tagToUse = null; int? tagToUse = null;
Logger.LogInformation("Starting SendToSonarr for series {Title} (TvDbId: {TvDbId})", model.ParentRequest.Title, model.ParentRequest.TvDbId);
Logger.LogInformation("Series type: {SeriesType}", model.SeriesType);
var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); var profiles = await UserQualityProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId);
if (profiles != null)
{
Logger.LogInformation("Found user quality profile for user {UserId}", model.RequestedUserId);
}
if (model.SeriesType == SeriesType.Anime) if (model.SeriesType == SeriesType.Anime)
{ {
@ -141,8 +148,10 @@ namespace Ombi.Core.Senders
// For some reason, if we haven't got one use the first root folder in Sonarr // For some reason, if we haven't got one use the first root folder in Sonarr
if (!int.TryParse(s.RootPathAnime, out int animePath)) if (!int.TryParse(s.RootPathAnime, out int animePath))
{ {
Logger.LogWarning("Failed to parse RootPathAnime: {RootPathAnime}, falling back to main root path", s.RootPathAnime);
animePath = int.Parse(s.RootPath); // Set it to the main root folder if we have no anime folder. animePath = int.Parse(s.RootPath); // Set it to the main root folder if we have no anime folder.
} }
Logger.LogInformation("Using anime path ID: {AnimePath}", animePath);
rootFolderPath = await GetSonarrRootPath(animePath, s); rootFolderPath = await GetSonarrRootPath(animePath, s);
languageProfileId = s.LanguageProfileAnime > 0 ? s.LanguageProfileAnime : s.LanguageProfile; languageProfileId = s.LanguageProfileAnime > 0 ? s.LanguageProfileAnime : s.LanguageProfile;
@ -154,6 +163,7 @@ namespace Ombi.Core.Senders
{ {
if (profiles.SonarrRootPathAnime > 0) if (profiles.SonarrRootPathAnime > 0)
{ {
Logger.LogInformation("Using user's anime root path override: {RootPath}", profiles.SonarrRootPathAnime);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s); rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s);
} }
if (profiles.SonarrQualityProfileAnime > 0) if (profiles.SonarrQualityProfileAnime > 0)
@ -169,11 +179,13 @@ namespace Ombi.Core.Senders
int.TryParse(s.QualityProfile, out qualityToUse); int.TryParse(s.QualityProfile, out qualityToUse);
// Get the root path from the rootfolder selected. // Get the root path from the rootfolder selected.
// For some reason, if we haven't got one use the first root folder in Sonarr // For some reason, if we haven't got one use the first root folder in Sonarr
Logger.LogInformation("Using standard path ID: {RootPath}", s.RootPath);
rootFolderPath = await GetSonarrRootPath(int.Parse(s.RootPath), s); rootFolderPath = await GetSonarrRootPath(int.Parse(s.RootPath), s);
if (profiles != null) if (profiles != null)
{ {
if (profiles.SonarrRootPath > 0) if (profiles.SonarrRootPath > 0)
{ {
Logger.LogInformation("Using user's standard root path override: {RootPath}", profiles.SonarrRootPath);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s); rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s);
} }
if (profiles.SonarrQualityProfile > 0) if (profiles.SonarrQualityProfile > 0)
@ -193,6 +205,7 @@ namespace Ombi.Core.Senders
if (model.ParentRequest.RootFolder.HasValue && model.ParentRequest.RootFolder.Value > 0) if (model.ParentRequest.RootFolder.HasValue && model.ParentRequest.RootFolder.Value > 0)
{ {
Logger.LogInformation("Using request root folder override: {RootFolder}", model.ParentRequest.RootFolder.Value);
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s); rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s);
} }
@ -201,6 +214,8 @@ namespace Ombi.Core.Senders
languageProfileId = model.ParentRequest.LanguageProfile.Value; languageProfileId = model.ParentRequest.LanguageProfile.Value;
} }
Logger.LogInformation("Final root folder path: {RootFolderPath}", rootFolderPath);
try try
{ {
if (tagToUse.HasValue) if (tagToUse.HasValue)
@ -520,17 +535,36 @@ namespace Ombi.Core.Senders
private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings) private async Task<string> GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
{ {
Logger.LogInformation("Getting Sonarr root path for ID: {PathId}", pathId);
var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri); var rootFoldersResult = await SonarrApi.GetRootFolders(sonarrSettings.ApiKey, sonarrSettings.FullUri);
if (rootFoldersResult == null || !rootFoldersResult.Any())
{
Logger.LogError("No root folders returned from Sonarr API");
return string.Empty;
}
Logger.LogInformation("Found {Count} root folders in Sonarr", rootFoldersResult.Count());
foreach (var folder in rootFoldersResult)
{
Logger.LogDebug("Root folder - ID: {Id}, Path: {Path}", folder.id, folder.path);
}
if (pathId == 0) if (pathId == 0)
{ {
return rootFoldersResult.FirstOrDefault().path; var defaultPath = rootFoldersResult.FirstOrDefault()?.path;
Logger.LogInformation("Using first root folder as default: {Path}", defaultPath);
return defaultPath;
} }
foreach (var r in rootFoldersResult?.Where(r => r.id == pathId)) var matchingFolder = rootFoldersResult.FirstOrDefault(r => r.id == pathId);
if (matchingFolder != null)
{ {
return r.path; Logger.LogInformation("Found matching root folder for ID {PathId}: {Path}", pathId, matchingFolder.path);
return matchingFolder.path;
} }
Logger.LogError("No matching root folder found for ID: {PathId}", pathId);
return string.Empty; return string.Empty;
} }
} }

View file

@ -0,0 +1,69 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Helpers;
namespace Ombi.Core.Services;
public class DatabaseConfigurationService : IDatabaseConfigurationService
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
public DatabaseConfigurationService(
ILogger<DatabaseConfigurationService> logger,
IFileSystem fileSystem)
{
_logger = logger;
_fileSystem = fileSystem;
}
public async Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token)
{
var i = StartupSingleton.Instance;
if (string.IsNullOrEmpty(i.StoragePath))
{
i.StoragePath = string.Empty;
}
var databaseFileLocation = Path.Combine(i.StoragePath, "database.json");
if (_fileSystem.FileExists(databaseFileLocation))
{
var error = $"The database file at '{databaseFileLocation}' already exists";
_logger.LogError(error);
return false;
}
var configuration = new DatabaseConfiguration
{
ExternalDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
OmbiDatabase = new PerDatabaseConfiguration(databaseType, connectionString),
SettingsDatabase = new PerDatabaseConfiguration(databaseType, connectionString)
};
var json = JsonConvert.SerializeObject(configuration, Formatting.Indented);
_logger.LogInformation("Writing database configuration to file");
try
{
await File.WriteAllTextAsync(databaseFileLocation, json, token);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to write database configuration to file");
return false;
}
_logger.LogInformation("Database configuration written to file");
return true;
}
}

View file

@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
namespace Ombi.Core.Services;
public interface IDatabaseConfigurationService
{
const string MySqlDatabase = "MySQL";
const string PostgresDatabase = "Postgres";
Task<bool> ConfigureDatabase(string databaseType, string connectionString, CancellationToken token);
}

View file

@ -107,6 +107,7 @@ namespace Ombi.DependencyInjection
services.AddTransient<IMusicSender, MusicSender>(); services.AddTransient<IMusicSender, MusicSender>();
services.AddTransient<IMassEmailSender, MassEmailSender>(); services.AddTransient<IMassEmailSender, MassEmailSender>();
services.AddTransient<IPlexOAuthManager, PlexOAuthManager>(); services.AddTransient<IPlexOAuthManager, PlexOAuthManager>();
services.AddTransient<IPlexTokenKeepAliveService, PlexTokenKeepAliveService>();
services.AddTransient<IVoteEngine, VoteEngine>(); services.AddTransient<IVoteEngine, VoteEngine>();
services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>(); services.AddTransient<IDemoMovieSearchEngine, DemoMovieSearchEngine>();
services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>(); services.AddTransient<IDemoTvSearchEngine, DemoTvSearchEngine>();
@ -236,6 +237,8 @@ namespace Ombi.DependencyInjection
services.AddScoped<IFeatureService, FeatureService>(); services.AddScoped<IFeatureService, FeatureService>();
services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>(); services.AddTransient<IRecentlyRequestedService, RecentlyRequestedService>();
services.AddTransient<IPlexService, PlexService>(); services.AddTransient<IPlexService, PlexService>();
services.AddSingleton<IFileSystem, FileSystem>();
services.AddSingleton<IDatabaseConfigurationService, DatabaseConfigurationService>();
} }
public static void RegisterJobs(this IServiceCollection services) public static void RegisterJobs(this IServiceCollection services)

View file

@ -14,6 +14,7 @@
IssueResolved = 9, IssueResolved = 9,
IssueComment = 10, IssueComment = 10,
Newsletter = 11, Newsletter = 11,
PartiallyAvailable = 12 PartiallyAvailable = 12,
PlexWatchlistTokenExpired = 13
} }
} }

View file

@ -12,7 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Ensure.That" Version="10.1.0" /> <PackageReference Include="Ensure.That" Version="10.1.0" />
<PackageReference Include="MailKit" Version="4.1.0" /> <PackageReference Include="MailKit" Version="4.6.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -2,6 +2,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<Configurations>Debug;Release;NonUiBuild</Configurations> <Configurations>Debug;Release;NonUiBuild</Configurations>
</PropertyGroup> </PropertyGroup>
@ -13,7 +16,7 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" /> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" /> <PackageReference Include="NUnit.ConsoleRunner" Version="3.15.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<packagereference Include="Microsoft.NET.Test.Sdk" Version="17.6.2"></packagereference> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -182,8 +182,6 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<OmbiUserManager>(x => x.UpdateAsync(It.IsAny<OmbiUser>()), Times.Never); _mocker.Verify<OmbiUserManager>(x => x.UpdateAsync(It.IsAny<OmbiUser>()), Times.Never);
} }
[Test] [Test]
public async Task Import_Doesnt_Import_Banned_Users() public async Task Import_Doesnt_Import_Banned_Users()
{ {
@ -247,7 +245,15 @@ namespace Ombi.Schedule.Tests
Id = "id", Id = "id",
Title = "title", Title = "title",
Username = "username", Username = "username",
HomeUser = true HomeUser = true,
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -257,7 +263,6 @@ namespace Ombi.Schedule.Tests
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "plex"), OmbiRoles.RequestMovie)) _mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.AddToRoleAsync(It.Is<OmbiUser>(x => x.UserName == "plex"), OmbiRoles.RequestMovie))
.ReturnsAsync(IdentityResult.Success); .ReturnsAsync(IdentityResult.Success);
await _subject.Execute(null); await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Once); _mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Once);
@ -306,7 +311,15 @@ namespace Ombi.Schedule.Tests
{ {
Email = "email", Email = "email",
Id = "id", Id = "id",
Username = "plex" Username = "plex",
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -343,7 +356,15 @@ namespace Ombi.Schedule.Tests
{ {
Email = "email", Email = "email",
Id = "PLEX_ID", Id = "PLEX_ID",
Username = "user" Username = "user",
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
} }
} }
}); });
@ -440,5 +461,98 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<IUserDeletionEngine>(x => x.DeleteUser(It.Is<OmbiUser>(x => x.ProviderUserId == "ADMIN_ID" && x.Email == "ADMIN@ADMIN.CO" && x.UserName == "Admin")), Times.Never); _mocker.Verify<IUserDeletionEngine>(x => x.DeleteUser(It.Is<OmbiUser>(x => x.ProviderUserId == "ADMIN_ID" && x.Email == "ADMIN@ADMIN.CO" && x.UserName == "Admin")), Times.Never);
} }
[Test]
public async Task Import_Skips_Users_Without_Server_Access()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "NoServer",
Title = "title",
Username = "username",
Server = null
}
}
});
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Never);
}
[Test]
public async Task Import_Skips_Users_With_Empty_Server_Array()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "EmptyServer",
Title = "title",
Username = "username",
Server = new PlexUserServer[0]
}
}
});
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.IsAny<OmbiUser>()), Times.Never);
}
[Test]
public async Task Import_Creates_User_With_Server_Access()
{
_mocker.Setup<ISettingsService<UserManagementSettings>, Task<UserManagementSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
_mocker.Setup<IPlexApi, Task<PlexUsers>>(x => x.GetUsers(It.IsAny<string>())).ReturnsAsync(new PlexUsers
{
User = new UserFriends[]
{
new UserFriends
{
Email = "email",
Id = "HasServer",
Title = "title",
Username = "username",
Server = new PlexUserServer[]
{
new PlexUserServer
{
Id = "1",
ServerId = "123"
}
}
}
}
});
_mocker.Setup<OmbiUserManager, Task<IdentityResult>>(x => x.CreateAsync(It.Is<OmbiUser>(x =>
x.UserName == "username" &&
x.Email == "email" &&
x.ProviderUserId == "HasServer" &&
x.UserType == UserType.PlexUser)))
.ReturnsAsync(IdentityResult.Success);
await _subject.Execute(null);
_mocker.Verify<OmbiUserManager>(x => x.CreateAsync(It.Is<OmbiUser>(x =>
x.UserName == "username" &&
x.Email == "email" &&
x.ProviderUserId == "HasServer" &&
x.UserType == UserType.PlexUser)), Times.Once);
}
} }
} }

View file

@ -20,6 +20,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Notifications.Models;
using Ombi.Core.Notifications;
using Ombi.Helpers;
using Ombi.Core;
using Ombi.Core.Authentication;
namespace Ombi.Schedule.Tests namespace Ombi.Schedule.Tests
{ {
@ -35,12 +40,15 @@ namespace Ombi.Schedule.Tests
public void Setup() public void Setup()
{ {
_mocker = new AutoMocker(); _mocker = new AutoMocker();
var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } }); var um = MockHelper.MockUserManager(new List<OmbiUser> { new OmbiUser { Id = "abc", Email = "email@email.com", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } });
_mocker.Use(um); _mocker.Use(um);
_context = _mocker.GetMock<IJobExecutionContext>(); _context = _mocker.GetMock<IJobExecutionContext>();
_context.Setup(x => x.CancellationToken).Returns(CancellationToken.None); _context.Setup(x => x.CancellationToken).Returns(CancellationToken.None);
// Mock the keep-alive service to return true by default
_mocker.Use<IPlexTokenKeepAliveService>(Mock.Of<IPlexTokenKeepAliveService>(s => s.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()) == Task.FromResult(true)));
_subject = _mocker.CreateInstance<PlexWatchlistImport>(); _subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<IRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>().AsQueryable().BuildMock()); _mocker.Setup<IRepository<PlexWatchlistUserError>, IQueryable<PlexWatchlistUserError>>(x => x.GetAll()).Returns(new List<PlexWatchlistUserError>().AsQueryable().BuildMock());
_mocker.Setup<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()));
} }
[Test] [Test]
@ -682,7 +690,6 @@ namespace Ombi.Schedule.Tests
[Test] [Test]
public async Task MovieRequestFromWatchList_AlreadyImported() public async Task MovieRequestFromWatchList_AlreadyImported()
{ {
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true }); _mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer _mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(new PlexWatchlistContainer
{ {
@ -719,7 +726,7 @@ namespace Ombi.Schedule.Tests
} }
}); });
_mocker.Setup<IExternalRepository<PlexWatchlistHistory>, IQueryable<PlexWatchlistHistory>>(x => x.GetAll()).Returns(new List<PlexWatchlistHistory> { new PlexWatchlistHistory { Id = 1, TmdbId = "123" } }.AsQueryable()); _mocker.Setup<IExternalRepository<PlexWatchlistHistory>, IQueryable<PlexWatchlistHistory>>(x => x.GetAll()).Returns(new List<PlexWatchlistHistory> { new PlexWatchlistHistory { Id = 1, TmdbId = "123", UserId = "abc" } }.AsQueryable());
await _subject.Execute(_context.Object); await _subject.Execute(_context.Object);
_mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never); _mocker.Verify<IMovieRequestEngine>(x => x.RequestMovie(It.IsAny<MovieRequestViewModel>()), Times.Never);
_mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once); _mocker.Verify<IPlexApi>(x => x.GetWatchlistMetadata("abc", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
@ -778,5 +785,99 @@ namespace Ombi.Schedule.Tests
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once); _mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.GetAll(), Times.Once);
_mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once); _mocker.Verify<IExternalRepository<PlexWatchlistHistory>>(x => x.Add(It.IsAny<PlexWatchlistHistory>()), Times.Once);
} }
[Test]
public async Task AuthenticationError_NotificationsEnabled_WithEmail_SendsNotification()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.Is<NotificationOptions>(n =>
n.NotificationType == NotificationType.PlexWatchlistTokenExpired &&
n.Recipient == "email@email.com" &&
n.Substitutes["UserName"] == "abc"
)), Times.Once);
}
[Test]
public async Task AuthenticationError_NotificationsDisabled_WithEmail_DoesNotSendNotification()
{
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = false });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never);
}
[Test]
public async Task AuthenticationError_NotificationsEnabled_NoEmail_DoesNotSendNotification()
{
// Arrange
var user = new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" };
var um = MockHelper.MockUserManager(new List<OmbiUser> { user });
_mocker.Use(um);
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync())
.ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
_mocker.Setup<IPlexApi, Task<PlexWatchlistContainer>>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
// Act
await _subject.Execute(_context.Object);
// Assert
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never);
}
[Test]
public async Task SkipsUserIfTokenKeepAliveFails()
{
// Arrange: Set up the keep-alive service to return false (token invalid/expired)
var keepAliveMock = new Mock<IPlexTokenKeepAliveService>();
keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(false);
_mocker.Use(keepAliveMock.Object);
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
// Act
await _subject.Execute(_context.Object);
// Assert: Should not attempt to import watchlist if keep-alive fails
keepAliveMock.Verify(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
_mocker.Verify<IPlexApi>(x => x.GetWatchlist(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
_mocker.Verify<INotificationHelper>(x => x.Notify(It.IsAny<NotificationOptions>()), Times.Never); // or Times.Once if notification is expected
}
[Test]
public async Task CallsKeepAliveForEachPlexUser()
{
// Arrange: Multiple Plex users
var users = new List<OmbiUser>
{
new OmbiUser { Id = "abc1", UserType = UserType.PlexUser, MediaServerToken = "abc1", UserName = "abc1", NormalizedUserName = "ABC1" },
new OmbiUser { Id = "abc2", UserType = UserType.PlexUser, MediaServerToken = "abc2", UserName = "abc2", NormalizedUserName = "ABC2" },
};
var um = MockHelper.MockUserManager(users);
_mocker.Use(um);
var keepAliveMock = new Mock<IPlexTokenKeepAliveService>();
keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(true);
_mocker.Use(keepAliveMock.Object);
_subject = _mocker.CreateInstance<PlexWatchlistImport>();
_mocker.Setup<ISettingsService<PlexSettings>, Task<PlexSettings>>(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true });
// Act
await _subject.Execute(_context.Object);
// Assert: KeepAlive should be called for each user
keepAliveMock.Verify(x => x.KeepTokenAliveAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(users.Count));
}
} }
} }

View file

@ -1,27 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62604/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Ombi.Schedule.Tests": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:62605/"
}
}
}

View file

@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging;
using Ombi.Api.Emby; using Ombi.Api.Emby;
using Ombi.Api.Jellyfin; using Ombi.Api.Jellyfin;
using Ombi.Api.Plex; using Ombi.Api.Plex;
using Ombi.Api.Plex.Models;
using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb;
using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TheMovieDb.Models;
using Ombi.Api.TvMaze; using Ombi.Api.TvMaze;
@ -286,7 +287,18 @@ namespace Ombi.Schedule.Jobs.Ombi
continue; continue;
} }
var servers = settings.Servers[0]; var servers = settings.Servers[0];
var metaData = await _plexApi.GetMetadata(servers.PlexAuthToken, settings.Servers[0].FullUri, movie.Key); PlexMetadata metaData = null;
try
{
metaData = await _plexApi.GetMetadata(servers.PlexAuthToken, settings.Servers[0].FullUri, movie.Key);
}
catch (Exception e)
{
_log.LogError($"Could not find the metadata for title: '{movie.Title}', skipping");
_log.LogDebug(e, $"Could not find the metadata for title: '{movie.Title}', skipping");
continue;
}
var guids = new List<string>(); var guids = new List<string>();
var meta = metaData.MediaContainer.Metadata.FirstOrDefault(); var meta = metaData.MediaContainer.Metadata.FirstOrDefault();

View file

@ -304,6 +304,13 @@ namespace Ombi.Schedule.Jobs.Plex
var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title var existing = await Repo.GetFirstContentByCustom(x => x.Title == movie.title
&& x.ReleaseYear == movie.year.ToString() && x.ReleaseYear == movie.year.ToString()
&& x.Type == MediaType.Movie); && x.Type == MediaType.Movie);
if (existing == null)
{
// Let's just check the key
existing = await Repo.GetByKey(movie.ratingKey);
}
if (existing != null) if (existing != null)
{ {
// We need to see if this is a different quality, // We need to see if this is a different quality,
@ -342,12 +349,6 @@ namespace Ombi.Schedule.Jobs.Plex
continue; continue;
} }
//var hasSameKey = await Repo.GetByKey(movie.ratingKey);
//if (hasSameKey != null)
//{
// await Repo.Delete(hasSameKey);
//}
Logger.LogDebug("Adding movie {0}", movie.title); Logger.LogDebug("Adding movie {0}", movie.title);
var guids = new List<string>(); var guids = new List<string>();
if (!movie.Guid.Any()) if (!movie.Guid.Any())

View file

@ -120,6 +120,13 @@ namespace Ombi.Schedule.Jobs.Plex
foreach (var plexUser in users.User) foreach (var plexUser in users.User)
{ {
// Skip users without server access
if (plexUser.Server == null || !plexUser.Server.Any())
{
_log.LogInformation($"Skipping user {plexUser.Username ?? plexUser.Id} as they have no server access");
continue;
}
// Check if we should import this user // Check if we should import this user
if (userManagementSettings.BannedPlexUserIds.Contains(plexUser.Id)) if (userManagementSettings.BannedPlexUserIds.Contains(plexUser.Id))
{ {

View file

@ -22,6 +22,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Notifications.Models;
using Ombi.Core.Notifications;
using Microsoft.AspNetCore.Identity;
using Ombi.Store.Repository.Requests;
using Ombi.Core;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
@ -37,11 +42,13 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo; private readonly IExternalRepository<PlexWatchlistHistory> _watchlistRepo;
private readonly IRepository<PlexWatchlistUserError> _userError; private readonly IRepository<PlexWatchlistUserError> _userError;
private readonly IMovieDbApi _movieDbApi; private readonly IMovieDbApi _movieDbApi;
private readonly INotificationHelper _notificationHelper;
private readonly IPlexTokenKeepAliveService _tokenKeepAliveService;
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager, public PlexWatchlistImport(IPlexApi plexApi, ISettingsService<PlexSettings> settings, OmbiUserManager ombiUserManager,
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService, IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService,
ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError, ILogger<PlexWatchlistImport> logger, IExternalRepository<PlexWatchlistHistory> watchlistRepo, IRepository<PlexWatchlistUserError> userError,
IMovieDbApi movieDbApi) IMovieDbApi movieDbApi, INotificationHelper notificationHelper, IPlexTokenKeepAliveService tokenKeepAliveService)
{ {
_plexApi = plexApi; _plexApi = plexApi;
_settings = settings; _settings = settings;
@ -53,6 +60,8 @@ namespace Ombi.Schedule.Jobs.Plex
_watchlistRepo = watchlistRepo; _watchlistRepo = watchlistRepo;
_userError = userError; _userError = userError;
_movieDbApi = movieDbApi; _movieDbApi = movieDbApi;
_notificationHelper = notificationHelper;
_tokenKeepAliveService = tokenKeepAliveService;
} }
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
@ -90,6 +99,36 @@ namespace Ombi.Schedule.Jobs.Plex
} }
_logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}"); _logger.LogDebug($"Starting Watchlist Import for {user.UserName} with token {user.MediaServerToken}");
// Keep the token alive before attempting watchlist import
var keepAliveSuccess = await _tokenKeepAliveService.KeepTokenAliveAsync(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (!keepAliveSuccess)
{
_logger.LogWarning($"Token for user '{user.UserName}' is invalid or expired (keep-alive failed). Recording error and skipping.");
await _userError.Add(new PlexWatchlistUserError
{
UserId = user.Id,
MediaServerToken = user.MediaServerToken,
});
// Send notification to user about token expiration
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
{
var notificationModel = new NotificationOptions
{
NotificationType = NotificationType.PlexWatchlistTokenExpired,
Recipient = user.Email,
DateTime = DateTime.Now,
Substitutes = new Dictionary<string, string>
{
{ "UserName", user.UserName }
}
};
await _notificationHelper.Notify(notificationModel);
}
continue;
}
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None); var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (watchlist?.AuthError ?? false) if (watchlist?.AuthError ?? false)
{ {
@ -99,6 +138,22 @@ namespace Ombi.Schedule.Jobs.Plex
UserId = user.Id, UserId = user.Id,
MediaServerToken = user.MediaServerToken, MediaServerToken = user.MediaServerToken,
}); });
// Send notification to user about token expiration
if (settings.NotifyOnWatchlistTokenExpiration && !string.IsNullOrEmpty(user.Email))
{
var notificationModel = new NotificationOptions
{
NotificationType = NotificationType.PlexWatchlistTokenExpired,
Recipient = user.Email,
DateTime = DateTime.Now,
Substitutes = new Dictionary<string, string>
{
{ "UserName", user.UserName }
}
};
await _notificationHelper.Notify(notificationModel);
}
continue; continue;
} }
if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false)) if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false))
@ -128,7 +183,7 @@ namespace Ombi.Schedule.Jobs.Plex
} }
// Check to see if we have already imported this item // Check to see if we have already imported this item
var alreadyImported = _watchlistRepo.GetAll().Any(x => x.TmdbId == providerIds.TheMovieDb); var alreadyImported = _watchlistRepo.GetAll().Any(x => x.TmdbId == providerIds.TheMovieDb && x.UserId == user.Id);
if (alreadyImported) if (alreadyImported)
{ {
_logger.LogDebug($"{item.title} already imported via Plex WatchList, skipping"); _logger.LogDebug($"{item.title} already imported via Plex WatchList, skipping");
@ -202,14 +257,14 @@ namespace Ombi.Schedule.Jobs.Plex
if (response.ErrorCode == ErrorCode.AlreadyRequested) if (response.ErrorCode == ErrorCode.AlreadyRequested)
{ {
_logger.LogDebug($"Movie already requested for user '{user.UserName}'"); _logger.LogDebug($"Movie already requested for user '{user.UserName}'");
await AddToHistory(theMovieDbId); await AddToHistory(theMovieDbId, user.Id);
return; return;
} }
_logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'"); _logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'");
} }
else else
{ {
await AddToHistory(theMovieDbId); await AddToHistory(theMovieDbId, user.Id);
_logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}");
} }
@ -230,24 +285,26 @@ namespace Ombi.Schedule.Jobs.Plex
if (response.ErrorCode == ErrorCode.AlreadyRequested) if (response.ErrorCode == ErrorCode.AlreadyRequested)
{ {
_logger.LogDebug($"Show already requested for user '{user.UserName}'"); _logger.LogDebug($"Show already requested for user '{user.UserName}'");
await AddToHistory(theMovieDbId); await AddToHistory(theMovieDbId, user.Id);
return; return;
} }
_logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'"); _logger.LogInformation($"Error adding title from PlexWatchlist for user '{user.UserName}'. Message: '{response.ErrorMessage}'");
} }
else else
{ {
await AddToHistory(theMovieDbId); await AddToHistory(theMovieDbId, user.Id);
_logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}"); _logger.LogInformation($"Added title from PlexWatchlist for user '{user.UserName}'. {response.Message}");
} }
} }
private async Task AddToHistory(int theMovieDbId) private async Task AddToHistory(int theMovieDbId, string userId)
{ {
// Add to the watchlist history // Add to the watchlist history
var history = new PlexWatchlistHistory var history = new PlexWatchlistHistory
{ {
TmdbId = theMovieDbId.ToString() TmdbId = theMovieDbId.ToString(),
AddedAt = DateTime.UtcNow,
UserId = userId
}; };
await _watchlistRepo.Add(history); await _watchlistRepo.Add(history);
} }

View file

@ -9,6 +9,7 @@ namespace Ombi.Core.Settings.Models.External
public bool Enable { get; set; } public bool Enable { get; set; }
public bool EnableWatchlistImport { get; set; } public bool EnableWatchlistImport { get; set; }
public bool MonitorAll { get; set; } public bool MonitorAll { get; set; }
public bool NotifyOnWatchlistTokenExpiration { get; set; }
/// <summary> /// <summary>
/// This is the ClientId for OAuth /// This is the ClientId for OAuth
/// </summary> /// </summary>

View file

@ -217,6 +217,16 @@ namespace Ombi.Store.Context
Enabled = true, Enabled = true,
}; };
break; break;
case NotificationType.PlexWatchlistTokenExpired:
notificationToAdd = new NotificationTemplates
{
NotificationType = notificationType,
Message = "Hello {UserName}! Your Plex watchlist token has expired. Please re-authenticate with Ombi to continue using the watchlist feature.",
Subject = "Plex Watchlist Token Expired",
Agent = agent,
Enabled = true,
};
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

View file

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema; using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Ombi.Store.Entities namespace Ombi.Store.Entities
{ {
@ -6,5 +7,7 @@ namespace Ombi.Store.Entities
public class PlexWatchlistHistory : Entity public class PlexWatchlistHistory : Entity
{ {
public string TmdbId { get; set; } public string TmdbId { get; set; }
public string UserId { get; set; }
public DateTime AddedAt { get; set; }
} }
} }

View file

@ -44,6 +44,21 @@ namespace Ombi.Store.Entities.Requests
public DateTime MarkedAsDenied4K { get; set; } public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; } public string DeniedReason4K { get; set; }
[NotMapped]
public RequestCombination RequestCombination
{
get
{
if (Has4KRequest && RequestedDate != default)
{
return RequestCombination.Both;
}
if (Has4KRequest) { return RequestCombination.FourK; }
return RequestCombination.Normal;
}
}
/// <summary> /// <summary>
/// Only Use for setting the Language Code, Use the LanguageCode property for reading /// Only Use for setting the Language Code, Use the LanguageCode property for reading

View file

@ -0,0 +1,9 @@
namespace Ombi.Store.Entities.Requests
{
public enum RequestCombination
{
Normal,
FourK,
Both
}
}

View file

@ -27,3 +27,48 @@ If running migrations for any db provider other than Sqlite, then ensure the dat
cd src/Ombi.Store cd src/Ombi.Store
dotnet ef migrations add <name> --context <context> --startup-project ../Ombi/Ombi.csproj dotnet ef migrations add <name> --context <context> --startup-project ../Ombi/Ombi.csproj
``` ```
docker run -d \
--name some-postgres \
-e POSTGRES_PASSWORD=ombi \
-e POSTGRES_USER=ombi \
-e POSTGRES_DB=ombi \
postgres
### MySql example
```
{
"OmbiDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=ombiNew;User=ombi"
},
"SettingsDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=ombiNew;User=ombi"
},
"ExternalDatabase": {
"Type": "MySQL",
"ConnectionString": "Server=192.168.68.118;Port=3306;Database=ombiNew;User=ombi"
}
}
```
### Postgres Example
```
{
"OmbiDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=localhost;Port=5432;Database=ombi;Username=ombi;Password=ombi"
},
"SettingsDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=localhost;Port=5432;Database=ombi;Username=ombi;Password=ombi"
},
"ExternalDatabase": {
"Type": "Postgres",
"ConnectionString": "Host=localhost;Port=5432;Database=ombi;Username=ombi;Password=ombi"
}
}
```

View file

@ -0,0 +1,635 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context.MySql;
#nullable disable
namespace Ombi.Store.Migrations.ExternalMySql
{
[DbContext(typeof(ExternalMySqlContext))]
[Migration("20240909082427_WatchListUserId")]
partial class WatchListUserId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("EmbyId")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("EmbyId")
.HasColumnType("longtext");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("ParentId")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("JellyfinId")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("JellyfinContent");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("JellyfinId")
.HasColumnType("longtext");
b.Property<string>("ParentId")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderId")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("JellyfinEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<int>("ArtistId")
.HasColumnType("int");
b.Property<string>("ForeignAlbumId")
.HasColumnType("longtext");
b.Property<bool>("Monitored")
.HasColumnType("tinyint(1)");
b.Property<decimal>("PercentOfTracks")
.HasColumnType("decimal(65,30)");
b.Property<DateTime>("ReleaseDate")
.HasColumnType("datetime(6)");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<int>("TrackCount")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("LidarrAlbumCache");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("ArtistId")
.HasColumnType("int");
b.Property<string>("ArtistName")
.HasColumnType("longtext");
b.Property<string>("ForeignArtistId")
.HasColumnType("longtext");
b.Property<bool>("Monitored")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("LidarrArtistCache");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<string>("GrandparentKey")
.HasColumnType("varchar(255)");
b.Property<string>("Key")
.HasColumnType("longtext");
b.Property<string>("ParentKey")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<string>("Title")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ParentKey")
.HasColumnType("longtext");
b.Property<string>("PlexContentId")
.HasColumnType("longtext");
b.Property<int?>("PlexServerContentId")
.HasColumnType("int");
b.Property<string>("SeasonKey")
.HasColumnType("longtext");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("varchar(255)");
b.Property<string>("Quality")
.HasColumnType("longtext");
b.Property<string>("ReleaseYear")
.HasColumnType("longtext");
b.Property<int?>("RequestId")
.HasColumnType("int");
b.Property<string>("TheMovieDbId")
.HasColumnType("longtext");
b.Property<string>("Title")
.HasColumnType("longtext");
b.Property<string>("TvDbId")
.HasColumnType("longtext");
b.Property<int>("Type")
.HasColumnType("int");
b.Property<string>("Url")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("TmdbId")
.HasColumnType("longtext");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PlexWatchlistHistory");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)");
b.Property<bool>("HasFile")
.HasColumnType("tinyint(1)");
b.Property<bool>("HasRegular")
.HasColumnType("tinyint(1)");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<bool>("HasFile")
.HasColumnType("tinyint(1)");
b.Property<int>("MovieDbId")
.HasColumnType("int");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<int>("TvDbId")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("int");
b.Property<int>("SeasonNumber")
.HasColumnType("int");
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("UserPlayedEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("int");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("JellyfinId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Navigation("Episodes");
b.Navigation("Seasons");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,366 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.ExternalMySql
{
/// <inheritdoc />
public partial class WatchListUserId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "UserPlayedMovie",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "UserPlayedEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SonarrEpisodeCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SonarrCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SickRageEpisodeCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SickRageCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "RadarrCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexWatchlistHistory",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AddColumn<DateTime>(
name: "AddedAt",
table: "PlexWatchlistHistory",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<string>(
name: "UserId",
table: "PlexWatchlistHistory",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexServerContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexSeasonsContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "LidarrArtistCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "LidarrAlbumCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "JellyfinEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "JellyfinContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "EmbyEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "EmbyContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "CouchPotatoCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AddedAt",
table: "PlexWatchlistHistory");
migrationBuilder.DropColumn(
name: "UserId",
table: "PlexWatchlistHistory");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "UserPlayedMovie",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "UserPlayedEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SonarrEpisodeCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SonarrCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SickRageEpisodeCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "SickRageCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "RadarrCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexWatchlistHistory",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexServerContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexSeasonsContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "PlexEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "LidarrArtistCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "LidarrAlbumCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "JellyfinEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "JellyfinContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "EmbyEpisode",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "EmbyContent",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "CouchPotatoCache",
type: "int",
nullable: false,
oldClrType: typeof(int),
oldType: "int")
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
}
}
}

View file

@ -2,6 +2,7 @@
using System; using System;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context.MySql; using Ombi.Store.Context.MySql;
@ -16,15 +17,19 @@ namespace Ombi.Store.Migrations.ExternalMySql
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.9") .HasAnnotation("ProductVersion", "8.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 64); .HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId") b.Property<int>("TheMovieDbId")
.HasColumnType("int"); .HasColumnType("int");
@ -39,6 +44,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -84,6 +91,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -127,6 +136,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -172,6 +183,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -215,6 +228,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -250,6 +265,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("ArtistId") b.Property<int>("ArtistId")
.HasColumnType("int"); .HasColumnType("int");
@ -273,6 +290,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber") b.Property<int>("EpisodeNumber")
.HasColumnType("int"); .HasColumnType("int");
@ -304,6 +323,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ParentKey") b.Property<string>("ParentKey")
.HasColumnType("longtext"); .HasColumnType("longtext");
@ -332,6 +353,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
@ -380,9 +403,17 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("datetime(6)");
b.Property<string>("TmdbId") b.Property<string>("TmdbId")
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<string>("UserId")
.HasColumnType("longtext");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("PlexWatchlistHistory"); b.ToTable("PlexWatchlistHistory");
@ -394,6 +425,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<bool>("Has4K") b.Property<bool>("Has4K")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
@ -417,6 +450,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TvDbId") b.Property<int>("TvDbId")
.HasColumnType("int"); .HasColumnType("int");
@ -431,6 +466,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber") b.Property<int>("EpisodeNumber")
.HasColumnType("int"); .HasColumnType("int");
@ -451,6 +488,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId") b.Property<int>("TheMovieDbId")
.HasColumnType("int"); .HasColumnType("int");
@ -468,6 +507,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber") b.Property<int>("EpisodeNumber")
.HasColumnType("int"); .HasColumnType("int");
@ -494,6 +535,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber") b.Property<int>("EpisodeNumber")
.HasColumnType("int"); .HasColumnType("int");
@ -517,6 +560,8 @@ namespace Ombi.Store.Migrations.ExternalMySql
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("int");
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId") b.Property<int>("TheMovieDbId")
.HasColumnType("int"); .HasColumnType("int");

View file

@ -0,0 +1,635 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Ombi.Store.Context.Postgres;
#nullable disable
namespace Ombi.Store.Migrations.ExternalPostgres
{
[DbContext(typeof(ExternalPostgresContext))]
[Migration("20240909071802_WatchListUserId")]
partial class WatchListUserId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<string>("EmbyId")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("Has4K")
.HasColumnType("boolean");
b.Property<string>("ImdbId")
.HasColumnType("text");
b.Property<string>("ProviderId")
.HasColumnType("text");
b.Property<string>("Quality")
.HasColumnType("text");
b.Property<string>("TheMovieDbId")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<string>("TvDbId")
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<string>("Url")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<string>("EmbyId")
.HasColumnType("text");
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<string>("ImdbId")
.HasColumnType("text");
b.Property<string>("ParentId")
.HasColumnType("text");
b.Property<string>("ProviderId")
.HasColumnType("text");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<string>("TheMovieDbId")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<string>("TvDbId")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<bool>("Has4K")
.HasColumnType("boolean");
b.Property<string>("ImdbId")
.HasColumnType("text");
b.Property<string>("JellyfinId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ProviderId")
.HasColumnType("text");
b.Property<string>("Quality")
.HasColumnType("text");
b.Property<string>("TheMovieDbId")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<string>("TvDbId")
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<string>("Url")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("JellyfinContent");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<string>("ImdbId")
.HasColumnType("text");
b.Property<string>("JellyfinId")
.HasColumnType("text");
b.Property<string>("ParentId")
.HasColumnType("text");
b.Property<string>("ProviderId")
.HasColumnType("text");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<string>("TheMovieDbId")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<string>("TvDbId")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("JellyfinEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<int>("ArtistId")
.HasColumnType("integer");
b.Property<string>("ForeignAlbumId")
.HasColumnType("text");
b.Property<bool>("Monitored")
.HasColumnType("boolean");
b.Property<decimal>("PercentOfTracks")
.HasColumnType("numeric");
b.Property<DateTime>("ReleaseDate")
.HasColumnType("timestamp without time zone");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<int>("TrackCount")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("LidarrAlbumCache");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ArtistId")
.HasColumnType("integer");
b.Property<string>("ArtistName")
.HasColumnType("text");
b.Property<string>("ForeignArtistId")
.HasColumnType("text");
b.Property<bool>("Monitored")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("LidarrArtistCache");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<string>("GrandparentKey")
.HasColumnType("text");
b.Property<string>("Key")
.HasColumnType("text");
b.Property<string>("ParentKey")
.HasColumnType("text");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<string>("Title")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ParentKey")
.HasColumnType("text");
b.Property<string>("PlexContentId")
.HasColumnType("text");
b.Property<int?>("PlexServerContentId")
.HasColumnType("integer");
b.Property<string>("SeasonKey")
.HasColumnType("text");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<bool>("Has4K")
.HasColumnType("boolean");
b.Property<string>("ImdbId")
.HasColumnType("text");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Quality")
.HasColumnType("text");
b.Property<string>("ReleaseYear")
.HasColumnType("text");
b.Property<int?>("RequestId")
.HasColumnType("integer");
b.Property<string>("TheMovieDbId")
.HasColumnType("text");
b.Property<string>("Title")
.HasColumnType("text");
b.Property<string>("TvDbId")
.HasColumnType("text");
b.Property<int>("Type")
.HasColumnType("integer");
b.Property<string>("Url")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<string>("TmdbId")
.HasColumnType("text");
b.Property<string>("UserId")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("PlexWatchlistHistory");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<bool>("Has4K")
.HasColumnType("boolean");
b.Property<bool>("HasFile")
.HasColumnType("boolean");
b.Property<bool>("HasRegular")
.HasColumnType("boolean");
b.Property<int>("TheMovieDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("TvDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<int>("TvDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("integer");
b.Property<int>("TvDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<bool>("HasFile")
.HasColumnType("boolean");
b.Property<int>("MovieDbId")
.HasColumnType("integer");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<int>("TvDbId")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("EpisodeNumber")
.HasColumnType("integer");
b.Property<int>("SeasonNumber")
.HasColumnType("integer");
b.Property<int>("TheMovieDbId")
.HasColumnType("integer");
b.Property<string>("UserId")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("UserPlayedEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("TheMovieDbId")
.HasColumnType("integer");
b.Property<string>("UserId")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("JellyfinId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Navigation("Episodes");
b.Navigation("Seasons");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,152 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.ExternalPostgres
{
/// <inheritdoc />
public partial class WatchListUserId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "AddedAt",
table: "PlexWatchlistHistory",
type: "timestamp without time zone",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<string>(
name: "UserId",
table: "PlexWatchlistHistory",
type: "text",
nullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "PlexServerContent",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ReleaseDate",
table: "LidarrAlbumCache",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "LidarrAlbumCache",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "JellyfinEpisode",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "JellyfinContent",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "EmbyEpisode",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "EmbyContent",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AddedAt",
table: "PlexWatchlistHistory");
migrationBuilder.DropColumn(
name: "UserId",
table: "PlexWatchlistHistory");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "PlexServerContent",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ReleaseDate",
table: "LidarrAlbumCache",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "LidarrAlbumCache",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "JellyfinEpisode",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "JellyfinContent",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "EmbyEpisode",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AddedAt",
table: "EmbyContent",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
}
}
}

View file

@ -17,7 +17,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "6.0.22") .HasAnnotation("ProductVersion", "8.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -47,7 +47,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<string>("EmbyId") b.Property<string>("EmbyId")
.IsRequired() .IsRequired()
@ -94,7 +94,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<string>("EmbyId") b.Property<string>("EmbyId")
.HasColumnType("text"); .HasColumnType("text");
@ -139,7 +139,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<bool>("Has4K") b.Property<bool>("Has4K")
.HasColumnType("boolean"); .HasColumnType("boolean");
@ -186,7 +186,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<int>("EpisodeNumber") b.Property<int>("EpisodeNumber")
.HasColumnType("integer"); .HasColumnType("integer");
@ -231,7 +231,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<int>("ArtistId") b.Property<int>("ArtistId")
.HasColumnType("integer"); .HasColumnType("integer");
@ -246,7 +246,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
.HasColumnType("numeric"); .HasColumnType("numeric");
b.Property<DateTime>("ReleaseDate") b.Property<DateTime>("ReleaseDate")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<string>("Title") b.Property<string>("Title")
.HasColumnType("text"); .HasColumnType("text");
@ -356,7 +356,7 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt") b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp without time zone");
b.Property<bool>("Has4K") b.Property<bool>("Has4K")
.HasColumnType("boolean"); .HasColumnType("boolean");
@ -405,9 +405,15 @@ namespace Ombi.Store.Migrations.ExternalPostgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id")); NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("AddedAt")
.HasColumnType("timestamp without time zone");
b.Property<string>("TmdbId") b.Property<string>("TmdbId")
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("UserId")
.HasColumnType("text");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("PlexWatchlistHistory"); b.ToTable("PlexWatchlistHistory");

View file

@ -0,0 +1,594 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Ombi.Store.Context.Sqlite;
#nullable disable
namespace Ombi.Store.Migrations.ExternalSqlite
{
[DbContext(typeof(ExternalSqliteContext))]
[Migration("20240909070705_WatchListUserId")]
partial class WatchListUserId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.5");
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchPotatoCache");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("EmbyId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("EmbyContent");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("EmbyId")
.HasColumnType("TEXT");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("ParentId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("EmbyEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("JellyfinId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("JellyfinContent");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("JellyfinId")
.HasColumnType("TEXT");
b.Property<string>("ParentId")
.HasColumnType("TEXT");
b.Property<string>("ProviderId")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ParentId");
b.ToTable("JellyfinEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<int>("ArtistId")
.HasColumnType("INTEGER");
b.Property<string>("ForeignAlbumId")
.HasColumnType("TEXT");
b.Property<bool>("Monitored")
.HasColumnType("INTEGER");
b.Property<decimal>("PercentOfTracks")
.HasColumnType("TEXT");
b.Property<DateTime>("ReleaseDate")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<int>("TrackCount")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("LidarrAlbumCache");
});
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("ArtistId")
.HasColumnType("INTEGER");
b.Property<string>("ArtistName")
.HasColumnType("TEXT");
b.Property<string>("ForeignArtistId")
.HasColumnType("TEXT");
b.Property<bool>("Monitored")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("LidarrArtistCache");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<string>("GrandparentKey")
.HasColumnType("TEXT");
b.Property<string>("Key")
.HasColumnType("TEXT");
b.Property<string>("ParentKey")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("GrandparentKey");
b.ToTable("PlexEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ParentKey")
.HasColumnType("TEXT");
b.Property<string>("PlexContentId")
.HasColumnType("TEXT");
b.Property<int?>("PlexServerContentId")
.HasColumnType("INTEGER");
b.Property<string>("SeasonKey")
.HasColumnType("TEXT");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PlexServerContentId");
b.ToTable("PlexSeasonsContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Quality")
.HasColumnType("TEXT");
b.Property<string>("ReleaseYear")
.HasColumnType("TEXT");
b.Property<int?>("RequestId")
.HasColumnType("INTEGER");
b.Property<string>("TheMovieDbId")
.HasColumnType("TEXT");
b.Property<string>("Title")
.HasColumnType("TEXT");
b.Property<string>("TvDbId")
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<string>("Url")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("PlexServerContent");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexWatchlistHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("TmdbId")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("PlexWatchlistHistory");
});
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("Has4K")
.HasColumnType("INTEGER");
b.Property<bool>("HasFile")
.HasColumnType("INTEGER");
b.Property<bool>("HasRegular")
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("RadarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SickRageCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SickRageEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SonarrCache");
});
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<bool>("HasFile")
.HasColumnType("INTEGER");
b.Property<int>("MovieDbId")
.HasColumnType("INTEGER");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<int>("TvDbId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("SonarrEpisodeCache");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedEpisode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("EpisodeNumber")
.HasColumnType("INTEGER");
b.Property<int>("SeasonNumber")
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserPlayedEpisode");
});
modelBuilder.Entity("Ombi.Store.Entities.UserPlayedMovie", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("TheMovieDbId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserPlayedMovie");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("EmbyId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.JellyfinContent", "Series")
.WithMany("Episodes")
.HasForeignKey("ParentId")
.HasPrincipalKey("JellyfinId");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series")
.WithMany("Episodes")
.HasForeignKey("GrandparentKey")
.HasPrincipalKey("Key");
b.Navigation("Series");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
{
b.HasOne("Ombi.Store.Entities.PlexServerContent", null)
.WithMany("Seasons")
.HasForeignKey("PlexServerContentId");
});
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.JellyfinContent", b =>
{
b.Navigation("Episodes");
});
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
{
b.Navigation("Episodes");
b.Navigation("Seasons");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.ExternalSqlite
{
/// <inheritdoc />
public partial class WatchListUserId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "AddedAt",
table: "PlexWatchlistHistory",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<string>(
name: "UserId",
table: "PlexWatchlistHistory",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AddedAt",
table: "PlexWatchlistHistory");
migrationBuilder.DropColumn(
name: "UserId",
table: "PlexWatchlistHistory");
}
}
}

View file

@ -15,7 +15,7 @@ namespace Ombi.Store.Migrations.ExternalSqlite
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.9"); modelBuilder.HasAnnotation("ProductVersion", "8.0.5");
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
{ {
@ -378,9 +378,15 @@ namespace Ombi.Store.Migrations.ExternalSqlite
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<DateTime>("AddedAt")
.HasColumnType("TEXT");
b.Property<string>("TmdbId") b.Property<string>("TmdbId")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("PlexWatchlistHistory"); b.ToTable("PlexWatchlistHistory");

View file

@ -19,7 +19,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.22" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" /> <PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="Polly" Version="7.2.3" /> <PackageReference Include="Polly" Version="7.2.3" />

View file

@ -94,7 +94,7 @@ namespace Ombi.Store.Repository
public async Task<PlexServerContent> GetByKey(string key) public async Task<PlexServerContent> GetByKey(string key)
{ {
return await Db.PlexServerContent.Include(x => x.Seasons).FirstOrDefaultAsync(x => x.Key == key); return await Db.PlexServerContent.Include(x => x.Seasons).Include(x => x.Episodes).FirstOrDefaultAsync(x => x.Key == key);
} }
public IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate) public IEnumerable<PlexServerContent> GetWhereContentByCustom(Expression<Func<PlexServerContent, bool>> predicate)

View file

@ -19,7 +19,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.12.0" /> <PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" /> <PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />

View file

@ -105,7 +105,6 @@
"options": { "options": {
"tsConfig": [ "tsConfig": [
"src/tsconfig.json" "src/tsconfig.json"
"src/tsconfig.json"
], ],
"exclude": [ "exclude": [
"**/node_modules/**" "**/node_modules/**"

View file

@ -13,26 +13,26 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^17.2.4", "@angular/animations": "^17.3.11",
"@angular/cdk": "16.2.14", "@angular/cdk": "16.2.14",
"@angular/common": "^17.2.4", "@angular/common": "^17.3.11",
"@angular/compiler": "^17.2.4", "@angular/compiler": "^17.3.11",
"@angular/core": "^17.2.4", "@angular/core": "^17.3.11",
"@angular/forms": "^17.2.4", "@angular/forms": "^17.3.11",
"@angular/material": "^14.2.7", "@angular/material": "^14.2.7",
"@angular/platform-browser": "^17.2.4", "@angular/platform-browser": "^17.3.11",
"@angular/platform-browser-dynamic": "^17.2.4", "@angular/platform-browser-dynamic": "^17.3.11",
"@angular/platform-server": "^17.2.4", "@angular/platform-server": "^17.3.11",
"@angular/router": "^17.2.4", "@angular/router": "^17.3.11",
"@angularclass/hmr": "^3.0.0", "@angularclass/hmr": "^3.0.0",
"@auth0/angular-jwt": "^5.0.2", "@auth0/angular-jwt": "^5.0.2",
"@fortawesome/fontawesome-free": "^6.4.2", "@fortawesome/fontawesome-free": "^6.6.0",
"@microsoft/signalr": "^6.0.23", "@microsoft/signalr": "^6.0.23",
"@ngx-translate/core": "^14.0.0", "@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^7.0.0", "@ngx-translate/http-loader": "^8.0.0",
"@ngxs/devtools-plugin": "3.8.1", "@ngxs/devtools-plugin": "3.8.2",
"@ngxs/store": "3.8.1", "@ngxs/store": "3.8.2",
"@types/jquery": "^3.5.23", "@types/jquery": "^3.5.30",
"@yellowspot/ng-truncate": "^2.0.0", "@yellowspot/ng-truncate": "^2.0.0",
"angularx-qrcode": "^16.0.0", "angularx-qrcode": "^16.0.0",
"bootstrap": "^4.2.1", "bootstrap": "^4.2.1",
@ -44,13 +44,13 @@
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
"ngx-clipboard": "^16.0.0", "ngx-clipboard": "^16.0.0",
"ngx-date-fns": "^11.0.0", "ngx-date-fns": "^11.0.0",
"ngx-infinite-scroll": "^17.0.0", "ngx-infinite-scroll": "^17.0.1",
"popper.js": "^1.14.3", "popper.js": "^1.14.3",
"primeicons": "^6.0.1", "primeicons": "^6.0.1",
"primeng": "^17.6.0", "primeng": "^17.11.0",
"rxjs": "^7.5.4", "rxjs": "^7.5.4",
"ts-md5": "^1.2.7", "ts-md5": "^1.2.7",
"zone.js": "0.14.3" "zone.js": "0.14.7"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.1.3", "@angular-devkit/build-angular": "^17.1.3",

View file

@ -5,9 +5,17 @@
<mat-button-toggle id="{{id}}Tv" [ngClass]="{'button-active': discoverOptions === DiscoverOption.Tv}" value="{{DiscoverOption.Tv}}" class="discover-filter-button">{{'Discovery.Tv' | translate}}</mat-button-toggle> <mat-button-toggle id="{{id}}Tv" [ngClass]="{'button-active': discoverOptions === DiscoverOption.Tv}" value="{{DiscoverOption.Tv}}" class="discover-filter-button">{{'Discovery.Tv' | translate}}</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
@defer (when discoverResults.length > 0; prefetch on idle) {
<p-carousel #carousel [numVisible]="10" [numScroll]="10" [page]="0" [value]="discoverResults" [responsiveOptions]="responsiveOptions" (onPage)="newPage()"> <p-carousel #carousel [numVisible]="10" [numScroll]="10" [page]="0" [value]="discoverResults" [responsiveOptions]="responsiveOptions" (onPage)="newPage()">
<ng-template let-result pTemplate="item"> <ng-template let-result pTemplate="item">
<discover-card [discoverType]="discoverType" [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card> <discover-card [discoverType]="discoverType" [isAdmin]="isAdmin" [result]="result" [is4kEnabled]="is4kEnabled"></discover-card>
</ng-template> </ng-template>
</p-carousel> </p-carousel>
}
@placeholder(minimum 300) {
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
}

View file

@ -105,6 +105,30 @@
padding: 5px; padding: 5px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(10% - 9px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}
@media (min-width:755px){ @media (min-width:755px){
::ng-deep .p-carousel-item{ ::ng-deep .p-carousel-item{
flex: 1 0 200px !important; flex: 1 0 200px !important;

View file

@ -43,7 +43,7 @@ export class CarouselListComponent implements OnInit {
get mediaTypeStorageKey() { get mediaTypeStorageKey() {
return "DiscoverOptions" + this.discoverType.toString(); return "DiscoverOptions" + this.discoverType.toString();
}; };
private amountToLoad = 17; private amountToLoad = 10;
private currentlyLoaded = 0; private currentlyLoaded = 0;
private baseUrl: string = ""; private baseUrl: string = "";
@ -148,6 +148,7 @@ export class CarouselListComponent implements OnInit {
} }
public async ngOnInit() { public async ngOnInit() {
this.is4kEnabled = this.featureFacade.is4kEnabled(); this.is4kEnabled = this.featureFacade.is4kEnabled();
this.currentlyLoaded = 0; this.currentlyLoaded = 0;
const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey); const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey);
@ -155,11 +156,15 @@ export class CarouselListComponent implements OnInit {
this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]]; this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
} }
let currentIteration = 0; // Load initial data - just enough to fill the first carousel page
while (this.discoverResults.length <= 14 && currentIteration <= 3) { // This reduces initial API calls and improves loading performance
currentIteration++; await this.loadData(false);
// If we don't have enough results to fill the carousel, load one more batch
if (this.discoverResults.length < 10) {
await this.loadData(false); await this.loadData(false);
} }
} }
public async toggleChanged(event: MatButtonToggleChange) { public async toggleChanged(event: MatButtonToggleChange) {
@ -293,7 +298,6 @@ export class CarouselListComponent implements OnInit {
} }
this.discoverResults.push(...tempResults); this.discoverResults.push(...tempResults);
this.carousel.ngAfterContentInit();
this.finishLoading(); this.finishLoading();
} }

View file

@ -1,21 +1,35 @@
<div class="small-middle-container"> <div class="small-middle-container">
<div class="section">
@defer (on viewport; prefetch on idle) { @defer (on viewport; prefetch on idle) {
<div class="section">
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2> <h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2>
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select> <genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
}
@placeholder {
<p-skeleton width="100%" height="2rem"></p-skeleton>
}
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.Genres' | translate }}</h2>
<p-skeleton width="100%" height="60px"></p-skeleton>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section"> <div class="section">
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2> <h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<div> <div>
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list> <ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
</div> </div>
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section" [hidden]="!showSeasonal"> <div class="section" [hidden]="!showSeasonal">
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2> <h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<div> <div>
@ -27,25 +41,68 @@
></carousel-list> ></carousel-list>
</div> </div>
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section"> <div class="section">
<h2>{{ 'Discovery.PopularTab' | translate }}</h2> <h2>{{ 'Discovery.PopularTab' | translate }}</h2>
<div> <div>
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list> <carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
</div> </div>
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.PopularTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section"> <div class="section">
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2> <h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
<div> <div>
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list> <carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
</div> </div>
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
@defer (on viewport; prefetch on idle) {
<div class="section"> <div class="section">
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2> <h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
<div> <div>
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list> <carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
</div> </div>
</div> </div>
} @placeholder(minimum 300) {
<div class="section">
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div>
</div>
</div>
}
</div> </div>

View file

@ -10,3 +10,27 @@ h2{
margin-left:40px; margin-left:40px;
font-size: 24px; font-size: 24px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(10% - 9px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}

View file

@ -1,13 +1,26 @@
<div class="genre-container" *ngIf="movieGenreList$ | async as movies">
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of movies" @defer (when movieGenreList()) {
<div class="genre-container">
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of movieGenreList()"
class="discover-filter-buttons-group"> class="discover-filter-buttons-group">
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle> <mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
<div class="genre-container" *ngIf="tvGenreList$ | async as tv"> }
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of tv" @placeholder(minimum 500) {
<p-skeleton width="100%" height="3.5rem"></p-skeleton>
}
@defer (when tvGenreList()) {
<div class="genre-container">
<mat-button-toggle-group name="discoverMode" (change)="toggleChanged($event, genre.type)" *ngFor="let genre of tvGenreList()"
class="discover-filter-buttons-group"> class="discover-filter-buttons-group">
<mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle> <mat-button-toggle value="{{genre.id}}" class="discover-filter-button">{{genre.name}}</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div>
}
@placeholder(minimum 500) {
<p-skeleton width="100%" height="3.5rem"></p-skeleton>
}
<mat-spinner *ngIf="isLoading" [diameter]="30"></mat-spinner> <mat-spinner *ngIf="isLoading" [diameter]="30"></mat-spinner>
</div>

View file

@ -1,10 +1,11 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit, computed, signal } from "@angular/core";
import { SearchV2Service } from "../../../services"; import { SearchV2Service } from "../../../services";
import { MatButtonToggleChange } from "@angular/material/button-toggle"; import { MatButtonToggleChange } from "@angular/material/button-toggle";
import { RequestType } from "../../../interfaces"; import { RequestType } from "../../../interfaces";
import { AdvancedSearchDialogDataService } from "app/shared/advanced-search-dialog/advanced-search-dialog-data.service"; import { AdvancedSearchDialogDataService } from "app/shared/advanced-search-dialog/advanced-search-dialog-data.service";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { map, Observable } from "rxjs"; import { map, Observable } from "rxjs";
import { toSignal } from '@angular/core/rxjs-interop';
interface IGenreSelect { interface IGenreSelect {
name: string; name: string;
@ -17,8 +18,10 @@ interface IGenreSelect {
styleUrls: ["./genre-button-select.component.scss"], styleUrls: ["./genre-button-select.component.scss"],
}) })
export class GenreButtonSelectComponent implements OnInit { export class GenreButtonSelectComponent implements OnInit {
public movieGenreList$: Observable<IGenreSelect[]> = null;
public tvGenreList$: Observable<IGenreSelect[]> = null; public movieGenreList = signal<IGenreSelect[]>(null);
public tvGenreList = signal<IGenreSelect[]>(null);
isLoading: boolean = false; isLoading: boolean = false;
@ -27,8 +30,14 @@ export class GenreButtonSelectComponent implements OnInit {
private router: Router) { } private router: Router) { }
public ngOnInit(): void { public ngOnInit(): void {
this.movieGenreList$ = this.searchService.getGenres("movie").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "movie" })))); this.searchService.getGenres("movie").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "movie" } as IGenreSelect))))
this.tvGenreList$ = this.searchService.getGenres("tv").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "tv" })))); .subscribe(x => {
this.movieGenreList.set(x);
});
this.searchService.getGenres("tv").pipe(map(x => x.slice(0, 10).map(y => ({ name: y.name, id: y.id, type: "tv" } as IGenreSelect))))
.subscribe(x => {
this.tvGenreList.set(x);
});
} }
public async toggleChanged(event: MatButtonToggleChange, type: "movie"|"tv") { public async toggleChanged(event: MatButtonToggleChange, type: "movie"|"tv") {

View file

@ -1,9 +1,8 @@
<div *ngIf="requests$ | async as requests"> @defer (when requests(); prefetch on idle) {
<div *ngIf="requests.length > 0"> <div *ngIf="requests().length > 0">
<p-carousel #carousel [value]="requests" [numVisible]="3" [numScroll]="1" <p-carousel #carousel [value]="requests()" [numVisible]="3" [numScroll]="1"
[responsiveOptions]="responsiveOptions" [page]="0"> [responsiveOptions]="responsiveOptions" [page]="0">
<ng-template let-result pTemplate="item"> <ng-template let-result pTemplate="item">
@defer (on viewport; prefetch on idle) {
<ombi-detailed-card <ombi-detailed-card
[request]="result" [request]="result"
[isAdmin]="isAdmin" [isAdmin]="isAdmin"
@ -11,11 +10,13 @@
(onApprove)="approve(result)" (onApprove)="approve(result)"
(onDeny)="deny(result)"> (onDeny)="deny(result)">
</ombi-detailed-card> </ombi-detailed-card>
}
@placeholder {
<p-skeleton width="100%" height="315px"></p-skeleton>
}
</ng-template> </ng-template>
</p-carousel> </p-carousel>
</div> </div>
}@placeholder(minimum 300) {
<div class="row loading-container">
<div class="col-2" *ngFor="let item of [1,2,3,4,5]">
<p-skeleton width="100%" height="270px"></p-skeleton>
</div> </div>
</div>
}

View file

@ -105,6 +105,30 @@
padding: 5px; padding: 5px;
} }
.loading-container {
display: flex;
gap: 10px;
padding: 0 20px;
margin-top: 20px;
}
.loading-container .col-2 {
flex: 0 0 auto;
width: calc(20% - 8px);
}
@media (max-width: 768px) {
.loading-container .col-2 {
width: calc(50% - 5px);
}
}
@media (max-width: 480px) {
.loading-container .col-2 {
width: calc(100% - 0px);
}
}
@media (min-width:755px){ @media (min-width:755px){
::ng-deep .p-carousel-item{ ::ng-deep .p-carousel-item{
flex: 1 0 200px !important; flex: 1 0 200px !important;

View file

@ -1,4 +1,4 @@
import { Component, OnInit, Input, ViewChild, OnDestroy } from "@angular/core"; import { Component, OnInit, Input, ViewChild, OnDestroy, signal } from "@angular/core";
import { IRecentlyRequested, IRequestEngineResult, RequestType } from "../../../interfaces"; import { IRecentlyRequested, IRequestEngineResult, RequestType } from "../../../interfaces";
import { Carousel } from 'primeng/carousel'; import { Carousel } from 'primeng/carousel';
import { ResponsiveOptions } from "../carousel.options"; import { ResponsiveOptions } from "../carousel.options";
@ -31,6 +31,7 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
@ViewChild('carousel', {static: false}) carousel: Carousel; @ViewChild('carousel', {static: false}) carousel: Carousel;
public requests$: Observable<IRecentlyRequested[]>; public requests$: Observable<IRecentlyRequested[]>;
public requests = signal<IRecentlyRequested[]>(null);
public responsiveOptions: any; public responsiveOptions: any;
public RequestType = RequestType; public RequestType = RequestType;
@ -119,11 +120,13 @@ export class RecentlyRequestedListComponent implements OnInit, OnDestroy {
} }
private loadData() { private loadData() {
this.requests$ = this.requestServiceV2.getRecentlyRequested().pipe( this.requestServiceV2.getRecentlyRequested().pipe(
tap(() => this.loading()), tap(() => this.loading()),
takeUntil(this.$loadSub), takeUntil(this.$loadSub),
finalize(() => this.finishLoading()) finalize(() => this.finishLoading())
); ).subscribe(x => {
this.requests.set(x);
});
} }
private loading() { private loading() {

View file

@ -52,6 +52,7 @@ export enum NotificationType {
IssueComment = 10, IssueComment = 10,
Newsletter = 11, Newsletter = 11,
PartiallyAvailable = 12, PartiallyAvailable = 12,
PlexWatchlistTokenExpired = 13
} }
export interface IDiscordNotifcationSettings extends INotificationSettings { export interface IDiscordNotifcationSettings extends INotificationSettings {

View file

@ -25,12 +25,19 @@ export interface IMovieRequests extends IFullBaseRequest {
requestedDate: Date; requestedDate: Date;
watchedByRequestedUser: boolean; watchedByRequestedUser: boolean;
playedByUsersCount: number; playedByUsersCount: number;
requestCombination: RequestCombination;
// For the UI // For the UI
rootPathOverrideTitle: string; rootPathOverrideTitle: string;
qualityOverrideTitle: string; qualityOverrideTitle: string;
} }
export enum RequestCombination {
Normal,
FourK,
Both
}
export interface IMovieAdvancedOptions { export interface IMovieAdvancedOptions {
requestId: number; requestId: number;
qualityOverride: number; qualityOverride: number;

View file

@ -114,6 +114,7 @@ export interface IPlexSettings extends ISettings {
enable: boolean; enable: boolean;
enableWatchlistImport: boolean; enableWatchlistImport: boolean;
monitorAll: boolean; monitorAll: boolean;
notifyOnWatchlistTokenExpiration: boolean;
servers: IPlexServer[]; servers: IPlexServer[];
} }

View file

@ -1,6 +1,6 @@
import { Component, Inject, OnInit } from "@angular/core"; import { Component, Inject, OnInit } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { IAdvancedData, IRadarrProfile, IRadarrRootFolder } from "../../../../../interfaces"; import { IAdvancedData, IRadarrProfile, IRadarrRootFolder, RequestCombination } from "../../../../../interfaces";
import { RadarrService } from "../../../../../services"; import { RadarrService } from "../../../../../services";
@Component({ @Component({
@ -11,6 +11,8 @@ export class MovieAdvancedOptionsComponent implements OnInit {
public radarrProfiles: IRadarrProfile[]; public radarrProfiles: IRadarrProfile[];
public radarrRootFolders: IRadarrRootFolder[]; public radarrRootFolders: IRadarrRootFolder[];
public show4k: boolean = false;
public showNormal: boolean = false;
constructor(public dialogRef: MatDialogRef<MovieAdvancedOptionsComponent>, @Inject(MAT_DIALOG_DATA) public data: IAdvancedData, constructor(public dialogRef: MatDialogRef<MovieAdvancedOptionsComponent>, @Inject(MAT_DIALOG_DATA) public data: IAdvancedData,
private radarrService: RadarrService private radarrService: RadarrService
@ -19,6 +21,20 @@ export class MovieAdvancedOptionsComponent implements OnInit {
public async ngOnInit() { public async ngOnInit() {
this.show4k = this.data.movieRequest.requestCombination === RequestCombination.FourK || this.data.movieRequest.requestCombination === RequestCombination.Both;
this.showNormal = this.data.movieRequest.requestCombination === RequestCombination.Normal || this.data.movieRequest.requestCombination === RequestCombination.Both;
if (this.show4k) {
this.radarrService.getQualityProfiles4kFromSettings().subscribe(c => {
this.radarrProfiles = c;
this.data.profiles = c;
this.setQualityOverrides();
});
this.radarrService.getRootFolders4kFromSettings().subscribe(c => {
this.radarrRootFolders = c;
this.data.rootFolders = c;
this.setRootFolderOverrides();
});
} else { // Currently show either 4k or normal, if it's a dual request there needs to be more work done to save the overrides for 4k separately
this.radarrService.getQualityProfilesFromSettings().subscribe(c => { this.radarrService.getQualityProfilesFromSettings().subscribe(c => {
this.radarrProfiles = c; this.radarrProfiles = c;
this.data.profiles = c; this.data.profiles = c;
@ -30,6 +46,7 @@ export class MovieAdvancedOptionsComponent implements OnInit {
this.setRootFolderOverrides(); this.setRootFolderOverrides();
}); });
} }
}
private setQualityOverrides(): void { private setQualityOverrides(): void {
if (this.radarrProfiles) { if (this.radarrProfiles) {

View file

@ -1,5 +1,6 @@
import { Pipe, PipeTransform } from "@angular/core"; import { Pipe, PipeTransform } from "@angular/core";
import { FormatPipe } from 'ngx-date-fns'; import { FormatPipe } from 'ngx-date-fns';
import { parseISO, format } from 'date-fns';
@Pipe({ @Pipe({
name: "ombiDate", name: "ombiDate",
@ -10,8 +11,16 @@ export class OmbiDatePipe implements PipeTransform {
private FormatPipe: FormatPipe, private FormatPipe: FormatPipe,
) {} ) {}
public transform(value: string, format: string ) { public transform(value: string, formatStr: string ) {
const date = new Date(value); if (!value) {
return this.FormatPipe.transform(date, format); return '';
}
// Parse the ISO string as UTC
const utcDate = parseISO(value);
// Format the date using date-fns format function
// This will automatically handle the UTC to local conversion
return format(utcDate, formatStr);
} }
} }

View file

@ -1,19 +1,22 @@
<div class="small-middle-container"> <div class="watchlist-dialog-container">
<fieldset style="fieldset"> <mat-card class="watchlist-dialog-card">
<legend mat-dialog-title>Watchlist User Errors</legend> <mat-card-header>
<div mat-dialog-content> <mat-card-title>Watchlist User Errors</mat-card-title>
<p> </mat-card-header>
<mat-card-content>
<div class="watchlist-info-section">
<mat-icon color="warn" class="info-icon">error_outline</mat-icon>
<span>
If there is an authentication error, this is because of an authentication issue with Plex (Token has expired). If there is an authentication error, this is because of an authentication issue with Plex (Token has expired).
If this happens the user needs to re-login to Ombi. If this happens the user needs to re-login to Ombi.
</p> </span>
<p> </div>
<em class="fa-solid fa-check key"></em> Successfully syncing the watchlist <div class="watchlist-legend">
<br> <span><mat-icon color="primary">check_circle</mat-icon> Successfully syncing the watchlist</span>
<em class="fa-solid fa-times key"></em> Authentication error syncing the watchlist <span><mat-icon color="warn">cancel</mat-icon> Authentication error syncing the watchlist</span>
<br> <span><mat-icon color="accent">person_off</mat-icon> Not enabled for user (They need to log into Ombi via Plex)</span>
<em class="fas fa-user-alt-slash key"></em> Not enabled for user (They need to log into Ombi via Plex) </div>
</p> <table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8 modern-table">
<table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="userName"> <ng-container matColumnDef="userName">
<th mat-header-cell *matHeaderCellDef> Username </th> <th mat-header-cell *matHeaderCellDef> Username </th>
<td mat-cell *matCellDef="let element"> {{element.userName}} </td> <td mat-cell *matCellDef="let element"> {{element.userName}} </td>
@ -21,18 +24,17 @@
<ng-container matColumnDef="syncStatus"> <ng-container matColumnDef="syncStatus">
<th mat-header-cell *matHeaderCellDef> Watchlist Sync Result </th> <th mat-header-cell *matHeaderCellDef> Watchlist Sync Result </th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<em *ngIf="element.syncStatus === WatchlistSyncStatus.Successful" class="fa-solid fa-check"></em> <mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.Successful" color="primary">check_circle</mat-icon>
<em *ngIf="element.syncStatus === WatchlistSyncStatus.Failed" class="fa-solid fa-times"></em> <mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.Failed" color="warn">cancel</mat-icon>
<em *ngIf="element.syncStatus === WatchlistSyncStatus.NotEnabled" class="fas fa-user-alt-slash"></em> <mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.NotEnabled" color="accent">person_off</mat-icon>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
</div> </mat-card-content>
<mat-dialog-actions align="end"> <mat-card-actions align="end">
<button mat-button mat-dialog-close>Close</button> <button mat-stroked-button mat-dialog-close color="accent">Close</button>
</mat-dialog-actions> </mat-card-actions>
</fieldset> </mat-card>
</div> </div>

View file

@ -11,3 +11,95 @@
.key { .key {
width: 40px; width: 40px;
} }
.watchlist-dialog-container {
display: flex;
justify-content: center;
align-items: flex-start;
margin: 0 auto;
}
.watchlist-dialog-card {
background: #23272f;
color: #f1f3f6;
border-radius: 12px;
border: 1px solid #353a45;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.25);
min-width: 420px;
max-width: 600px;
width: 100%;
max-height: 70vh;
display: flex;
flex-direction: column;
}
mat-card-content {
flex: 1 1 auto;
overflow-y: auto;
min-height: 0;
}
mat-card-header {
border-bottom: 1px solid #353a45;
margin-bottom: 12px;
flex: 0 0 auto;
}
mat-card-title {
color: #fff !important;
font-weight: 700 !important;
letter-spacing: 0.5px;
}
.watchlist-info-section {
display: flex;
align-items: center;
background: #23272f;
color: #e0e3ea;
padding: 12px 0 8px 0;
font-size: 15px;
gap: 12px;
}
.info-icon {
font-size: 28px;
color: #ffb300;
}
.watchlist-legend {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 18px;
margin-top: 8px;
font-size: 14px;
color: #b0b6c3;
}
.watchlist-legend mat-icon {
vertical-align: middle;
margin-right: 6px;
}
.modern-table {
background: transparent;
color: #f1f3f6;
border-radius: 8px;
margin-top: 10px;
}
.modern-table th, .modern-table td {
color: #f1f3f6;
font-size: 15px;
}
mat-card-actions {
padding-top: 16px;
flex: 0 0 auto;
background: #23272f;
}
button[mat-stroked-button] {
font-weight: 600;
border-radius: 6px;
}

View file

@ -1,137 +1,179 @@
<settings-menu></settings-menu> <settings-menu></settings-menu>
<div class="small-middle-container" *ngIf="settings">
<fieldset style="width:100%;"> <div class="plex-settings-container" *ngIf="settings">
<legend>Plex Configuration</legend> <mat-card class="settings-card">
<div class="col-12"> <mat-card-header>
<div class="md-form-field align-right"> <mat-card-title>Plex Configuration</mat-card-title>
<button (click)="openWatchlistUserLog()" type="button" class="mat-focus-indicator mat-flat-button mat-button-base mat-accent">Watchlist User Errors</button> </mat-card-header>
<mat-card-content>
<!-- Watchlist Settings Section -->
<div class="settings-section">
<div class="section-header">
<h2>Watchlist Settings</h2>
</div> </div>
<div class="settings-grid">
<mat-card class="setting-card">
<mat-card-content>
<div class="setting-header">
<h3>Enable Plex</h3>
<mat-slide-toggle [id]="'enable'" [(ngModel)]="settings.enable"></mat-slide-toggle>
</div> </div>
<settings-plex-form-field [label]="'Enable'" [type]="'checkbox'" [id]="'enable'" [(value)]="settings.enable"></settings-plex-form-field> </mat-card-content>
</mat-card>
<settings-plex-form-field [label]="'Enable User Watchlist Requests'" [type]="'checkbox'" [id]="'enableWatchlistImport'" [(value)]="settings.enableWatchlistImport"> <mat-card class="setting-card">
<small bottom>When a Plex User adds something to their watchlist in Plex, it will turn up in Ombi as a Request if enabled. This <b>only</b> applies to users that are logging in with their Plex Account <mat-card-content>
<div class="setting-header">
<h3>Enable User Watchlist Requests</h3>
<mat-slide-toggle [id]="'enableWatchlistImport'" [(ngModel)]="settings.enableWatchlistImport"></mat-slide-toggle>
</div>
<p class="setting-description">
When a Plex User adds something to their watchlist in Plex, it will turn up in Ombi as a Request if enabled.
This <strong>only</strong> applies to users that are logging in with their Plex Account.
<br>Request limits if set are all still applied <br>Request limits if set are all still applied
</small> </p>
</settings-plex-form-field> </mat-card-content>
</mat-card>
<settings-plex-form-field [disabled]="!settings.enableWatchlistImport" [label]="'Watchlist - Request Whole Show'" disabled [type]="'checkbox'" [id]="'monitorAll'" [(value)]="settings.monitorAll"> <mat-card class="setting-card" [class.disabled]="!settings.enableWatchlistImport">
<small bottom>If enabled then watchlist requests for TV Shows will request the <strong><em>whole</em></strong> show. If not enabled it will only request the latest season. <mat-card-content>
</small> <div class="setting-header">
</settings-plex-form-field> <h3>Request Whole Show</h3>
<mat-slide-toggle [id]="'monitorAll'" [(ngModel)]="settings.monitorAll" [disabled]="!settings.enableWatchlistImport"></mat-slide-toggle>
</div>
<p class="setting-description">
If enabled then watchlist requests for TV Shows will request the <strong>whole</strong> show.
If not enabled it will only request the latest season.
</p>
</mat-card-content>
</mat-card>
<mat-card class="setting-card" [class.disabled]="!settings.enableWatchlistImport">
<mat-card-content>
<div class="setting-header">
<h3>Notify on Token Expiration</h3>
<mat-slide-toggle [id]="'notifyOnWatchlistTokenExpiration'" [(ngModel)]="settings.notifyOnWatchlistTokenExpiration" [disabled]="!settings.enableWatchlistImport"></mat-slide-toggle>
</div>
<p class="setting-description">
When enabled, users will receive a notification if their Plex watchlist token expires and they need to log into Ombi again to continue using the watchlist feature.
<br><strong>Note:</strong> This requires email notifications to be configured in the notification settings, and users must have an email address set on their account to receive these notifications.
</p>
</mat-card-content>
</mat-card>
</div>
<mat-card class="info-banner">
<mat-icon color="primary" style="margin-right: 12px;">info</mat-icon>
<span style="flex:1;">
Some users may need to re-log in to use the watchlist feature.
</span>
<button mat-button color="accent" (click)="openWatchlistUserLog()">
View Users
</button>
</mat-card>
</div>
<!-- Main Content Area -->
<hr> <div class="main-content">
<!-- Left Column - Servers and Actions -->
<div class="row"> <div class="content-column">
<div class="col-md-7"> <!-- Servers Section -->
<h2 style="margin: 1em 0 0 0;">Servers</h2> <div class="settings-section">
<mat-list style="display:flex; flex-flow: wrap;"> <h2>Plex Servers</h2>
<div class="servers-grid">
<mat-card class="server-card" *ngFor="let server of settings.servers"> <mat-card class="server-card" *ngFor="let server of settings.servers">
<button mat-button (click)="edit(server)" id="{{server.name}}-button"> <mat-card-content>
<h3>{{server.name}}</h3> <button mat-button (click)="edit(server)" [id]="server.name + '-button'">
<mat-icon>dns</mat-icon>
<span>{{server.name}}</span>
</button> </button>
</mat-card-content>
</mat-card> </mat-card>
<mat-card class="server-card new-server-card"> <mat-card class="server-card new-server">
<mat-card-content>
<button mat-button (click)="newServer()" id="newServer"> <button mat-button (click)="newServer()" id="newServer">
<i class="fas fa-plus fa-xl"></i> <mat-icon>add_circle</mat-icon>
<h3>Manually Add Server</h3> <span>Add Server</span>
</button> </button>
</mat-card-content>
</mat-card> </mat-card>
</mat-list>
<div class="row">
<br />
<div class="form-group col-2">
<button mat-raised-button (click)="runSync(PlexSyncType.Full)" type="button" id="fullSync"
class="mat-focus-indicator mat-stroked-button mat-button-base">Full
Sync</button><br />
</div> </div>
<div class="form-group col-2">
<button mat-raised-button (click)="runSync(PlexSyncType.RecentlyAdded)" type="button" id="recentlyAddedSync"
class="mat-focus-indicator mat-stroked-button mat-button-base">Partial Sync</button>
</div> </div>
<div class="form-group col-2">
<button mat-raised-button (click)="runSync(PlexSyncType.ClearAndReSync)" type="button" id="clearData" <!-- Sync Actions Section -->
class="mat-focus-indicator mat-stroked-button mat-button-base"> <div class="settings-section">
Clear Data And Resync <h2>Sync Actions</h2>
<div class="sync-actions-grid">
<button mat-stroked-button (click)="runSync(PlexSyncType.Full)" id="fullSync">
<mat-icon>sync</mat-icon>
Full Sync
</button> </button>
</div> <button mat-stroked-button (click)="runSync(PlexSyncType.RecentlyAdded)" id="recentlyAddedSync">
<div class="form-group col-12"> <mat-icon>update</mat-icon>
<button mat-raised-button (click)="runSync(PlexSyncType.WatchlistImport)" type="button" id="watchlistImport" Partial Sync
class="mat-focus-indicator mat-stroked-button mat-button-base"> </button>
<button mat-stroked-button (click)="runSync(PlexSyncType.ClearAndReSync)" id="clearData">
<mat-icon>cleaning_services</mat-icon>
Clear & Resync
</button>
<button mat-stroked-button (click)="runSync(PlexSyncType.WatchlistImport)" id="watchlistImport">
<mat-icon>playlist_add</mat-icon>
Run Watchlist Import Run Watchlist Import
</button> </button>
</div> </div>
</div>
<div class="row">
<div class="col-md-2">
<div class="form-group">
<div>
<button mat-raised-button (click)="save()" type="submit" id="save"
class="mat-focus-indicator mat-raised-button mat-button-base mat-accent">Submit</button>
</div>
</div>
</div> </div>
</div> </div>
</div> <!-- Right Column - Plex Credentials -->
<div class="content-column">
<div class="settings-section">
<h2>Plex Credentials</h2>
<mat-card class="credentials-card">
<mat-card-content>
<p class="credentials-description">
These fields are optional to automatically fill in your Plex server settings.
<br>This will pass your username and password to the Plex.tv API to grab the servers associated with this user.
<br>If you have 2FA enabled on your account, you need to append the 2FA code to the end of your password.
</p>
<div class="col-md-5"> <mat-form-field appearance="outline" class="full-width">
<div class="md-form-field"> <mat-label>Username</mat-label>
<label for="username" class="control-label"> <input matInput [id]="'username'" [(ngModel)]="username">
<h3>Plex Credentials</h3> </mat-form-field>
<small>These fields are optional to automatically fill in your Plex server settings. <br>
This will pass your username and password to the Plex.tv API to grab the servers associated with this user.
<br>
If you have 2FA enabled on your account, you need to append the 2FA code to the end of your password.</small>
</label>
</div>
<settings-plex-form-field [label]="'Username'" [id]="'username'" [(value)]="username"></settings-plex-form-field> <mat-form-field appearance="outline" class="full-width">
<settings-plex-form-field [label]="'Password'" [id]="'password'" [type]="'password'" [(value)]="password"></settings-plex-form-field> <mat-label>Password</mat-label>
<input matInput [id]="'password'" type="password" [(ngModel)]="password">
</mat-form-field>
<div class="md-form-field"> <button mat-raised-button color="primary" id="loadServers" (click)="requestServers()" class="full-width">
<div class="right"> <mat-icon>key</mat-icon>
<button mat-raised-button id="loadServers" (click)="requestServers()" Load Servers
class="mat-stroked-button">Load Servers
<i class="fas fa-key"></i>
</button> </button>
</div>
</div>
<div class="row"> <mat-form-field appearance="outline" class="full-width mt-3">
<div class="col-2 align-self-center"> <mat-label>Select Server</mat-label>
Please select the server: <mat-select [id]="'servers'" *ngIf="loadedServers">
</div> <mat-option (click)="selectServer(s)" *ngFor="let s of loadedServers.servers.server" [value]="s.server">
<div class="md-form-field col-10"> {{s.name}}
<div *ngIf="!loadedServers"> </mat-option>
<mat-form-field appearance="outline" floatLabel=auto>
<input disabled matInput placeholder="No Servers Loaded" id="servers">
</mat-form-field>
</div>
<div *ngIf="loadedServers">
<mat-form-field appearance="outline">
<mat-select placeholder="Servers Loaded! Please Select" id="servers">
<mat-option (click)="selectServer(s)"
*ngFor="let s of loadedServers.servers.server" [value]="s.server">
{{s.name}}</mat-option>
</mat-select> </mat-select>
<input matInput disabled placeholder="No Servers Loaded" *ngIf="!loadedServers">
</mat-form-field> </mat-form-field>
</mat-card-content>
</mat-card>
</div> </div>
</div> </div>
</div> </div>
</div> </mat-card-content>
</div>
</fieldset> <mat-card-actions align="end">
<button mat-raised-button color="accent" (click)="save()" id="save">
<mat-icon>save</mat-icon>
Save Changes
</button>
</mat-card-actions>
</mat-card>
</div> </div>
<!--(){{settings|json}}-->

View file

@ -44,3 +44,257 @@
margin: 0; margin: 0;
} }
} }
.plex-settings-container {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
}
.settings-card {
margin-bottom: 20px;
background: transparent;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.25);
border-radius: 12px;
border: 1px solid #353a45;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
margin-bottom: 30px;
}
.setting-card {
height: 100%;
background: transparent;
border-radius: 10px;
border: 1px solid #353a45;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
color: #f1f3f6;
}
.setting-card.disabled {
opacity: 0.6;
}
.setting-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-weight: 600;
color: #fff;
}
.setting-description {
color: #e0e3ea;
font-size: 15px;
margin: 0;
font-weight: 400;
line-height: 1.6;
}
.main-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 36px;
margin-top: 36px;
}
.servers-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 18px;
}
.server-card {
text-align: center;
background: transparent;
border-radius: 10px;
border: 1px solid #353a45;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
color: #f1f3f6;
}
.server-card button {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
color: #f1f3f6;
font-weight: 500;
}
.server-card mat-icon {
font-size: 32px;
height: 32px;
width: 32px;
margin-bottom: 10px;
color: #90caf9;
}
.sync-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 18px;
}
.sync-actions-grid button {
display: flex;
flex-direction: column;
align-items: center;
padding: 24px;
color: #f1f3f6;
background: transparent;
border: 1px solid #353a45;
border-radius: 10px;
font-weight: 500;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
}
.sync-actions-grid mat-icon {
font-size: 32px;
height: 32px;
width: 32px;
margin-bottom: 10px;
color: #90caf9;
}
.credentials-card {
padding: 24px;
background: transparent;
border-radius: 10px;
border: 1px solid #353a45;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
color: #f1f3f6;
}
.credentials-description {
color: #e0e3ea;
margin-bottom: 20px;
font-size: 15px;
font-weight: 400;
}
mat-card-title, h2, h3 {
color: #fff !important;
font-weight: 700 !important;
letter-spacing: 0.5px;
}
mat-card-header {
border-bottom: 1px solid #353a45;
margin-bottom: 16px;
}
mat-form-field {
color: #f1f3f6 !important;
}
mat-label, .mat-form-field-label {
color: #b0b6c3 !important;
font-weight: 500;
}
input[matInput], .mat-input-element {
color: #f1f3f6 !important;
background: transparent !important;
}
mat-select {
color: #f1f3f6 !important;
background: transparent !important;
}
.full-width {
width: 100%;
}
.mt-3 {
margin-top: 1rem;
}
.mat-slide-toggle.mat-checked .mat-slide-toggle-bar {
background-color: #90caf9 !important;
}
.mat-slide-toggle-thumb {
background-color: #2196f3 !important;
}
button[mat-flat-button], button[mat-raised-button], button[mat-stroked-button], button[mat-button] {
font-weight: 600;
letter-spacing: 0.2px;
color: #f1f3f6;
background: #2196f3;
border-radius: 6px;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.12);
transition: background 0.2s;
}
button[mat-flat-button]:hover, button[mat-raised-button]:hover, button[mat-stroked-button]:hover, button[mat-button]:hover {
background: #42a5f5;
}
@media (max-width: 1200px) {
.main-content {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.settings-grid {
grid-template-columns: 1fr;
}
.servers-grid {
grid-template-columns: 1fr;
}
.sync-actions-grid {
grid-template-columns: 1fr;
}
}
.watchlist-errors-btn-row {
display: flex;
justify-content: center;
margin-top: 18px;
}
.info-banner {
display: flex;
align-items: center;
background: #23272f;
color: #e0e3ea;
border: 1px solid #1976d2;
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.12);
border-radius: 8px;
padding: 16px 20px;
margin-top: 18px;
margin-bottom: 0;
font-size: 16px;
font-weight: 500;
gap: 12px;
}
.info-banner mat-icon {
font-size: 28px;
color: #42a5f5;
}
.info-banner button[mat-button] {
margin-left: 16px;
font-weight: 600;
}

View file

@ -28,10 +28,12 @@
<mat-form-field appearance="outline" > <mat-form-field appearance="outline" >
<mat-label>Hostname or IP</mat-label> <mat-label>Hostname or IP</mat-label>
<input matInput formControlName="ip"> <input matInput formControlName="ip">
<mat-error>Please enter a valid hostname or ip address</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" > <mat-form-field appearance="outline" >
<mat-label>Port</mat-label> <mat-label>Port</mat-label>
<input matInput formControlName="port"> <input matInput formControlName="port">
<mat-error>Please enter a valid port number</mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle formControlName="ssl"> <mat-slide-toggle formControlName="ssl">
SSL SSL
@ -42,6 +44,7 @@
<mat-form-field appearance="outline" > <mat-form-field appearance="outline" >
<mat-label>API key</mat-label> <mat-label>API key</mat-label>
<input matInput formControlName="apiKey"> <input matInput formControlName="apiKey">
<mat-error>Please enter an API Key</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="md-form-field"> <div class="md-form-field">
@ -65,6 +68,7 @@
{{quality.name}} {{quality.name}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error>Please select a value</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@ -80,6 +84,7 @@
{{folder.path}} {{folder.path}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error>Please select a value</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@ -95,6 +100,7 @@
{{tag.label}} {{tag.label}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error>Please select a value</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@ -107,6 +113,7 @@
{{min.name}} {{min.name}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error>Please select a value</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>

View file

@ -63,14 +63,14 @@ export class RadarrFormComponent implements OnInit {
} }
public toggleValidators() { public toggleValidators() {
debugger;
const enabled = this.form.controls.enabled.value as boolean; const enabled = this.form.controls.enabled.value as boolean;
this.form.controls.apiKey.setValidators(enabled ? [Validators.required] : null); this.form.controls.apiKey.setValidators(enabled ? [Validators.required] : null);
this.form.controls.defaultQualityProfile.setValidators(enabled ? [Validators.required] : null); this.form.controls.defaultQualityProfile.setValidators(enabled ? [Validators.required, Validators.min(1)] : null);
this.form.controls.defaultRootPath.setValidators(enabled ? [Validators.required] : null); this.form.controls.defaultRootPath.setValidators(enabled ? [Validators.required] : null);
this.form.controls.ip.setValidators(enabled ? [Validators.required] : null); this.form.controls.ip.setValidators(enabled ? [Validators.required] : null);
this.form.controls.port.setValidators(enabled ? [Validators.required] : null); this.form.controls.port.setValidators(enabled ? [Validators.required, Validators.min(1)] : null);
this.form.controls.minimumAvailability.setValidators(enabled ? [Validators.required] : null); this.form.controls.minimumAvailability.setValidators(enabled ? [Validators.required] : null);
enabled ? this.form.markAllAsTouched() : this.form.markAsUntouched();
} }
public getProfiles(form: UntypedFormGroup) { public getProfiles(form: UntypedFormGroup) {
@ -120,6 +120,7 @@ export class RadarrFormComponent implements OnInit {
this.notificationService.success("Successfully connected to Radarr!"); this.notificationService.success("Successfully connected to Radarr!");
} else if (result.expectedSubDir) { } else if (result.expectedSubDir) {
this.notificationService.error("Your Radarr Base URL must be set to " + result.expectedSubDir); this.notificationService.error("Your Radarr Base URL must be set to " + result.expectedSubDir);
form.controls.subDir.setValue(result.expectedSubDir);
} else { } else {
this.notificationService.error("We could not connect to Radarr!"); this.notificationService.error("We could not connect to Radarr!");
} }

View file

@ -1,5 +1,5 @@
<settings-menu></settings-menu> <settings-menu></settings-menu>
<div *ngIf="form" class="small-middle-container"> <div *ngIf="form$ | async as form" class="small-middle-container">
<fieldset> <fieldset>
<legend>Radarr Settings</legend> <legend>Radarr Settings</legend>
<div class="md-form-field" style="margin-top:1em;"></div> <div class="md-form-field" style="margin-top:1em;"></div>

View file

@ -1,4 +1,4 @@
import { Component, OnInit, QueryList, ViewChildren } from "@angular/core"; import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from "@angular/core";
import { UntypedFormBuilder, UntypedFormGroup } from "@angular/forms"; import { UntypedFormBuilder, UntypedFormGroup } from "@angular/forms";
import { RadarrFacade } from "app/state/radarr"; import { RadarrFacade } from "app/state/radarr";
@ -6,36 +6,41 @@ import { IMinimumAvailability, IRadarrCombined, IRadarrProfile, IRadarrRootFolde
import { NotificationService } from "../../services"; import { NotificationService } from "../../services";
import { FeaturesFacade } from "../../state/features/features.facade"; import { FeaturesFacade } from "../../state/features/features.facade";
import { RadarrFormComponent } from "./components/radarr-form.component"; import { RadarrFormComponent } from "./components/radarr-form.component";
import { Observable, ReplaySubject, Subject, combineLatest, map, switchMap, takeUntil, tap } from "rxjs";
@Component({ @Component({
templateUrl: "./radarr.component.html", templateUrl: "./radarr.component.html",
styleUrls: ["./radarr.component.scss"] styleUrls: ["./radarr.component.scss"]
}) })
export class RadarrComponent implements OnInit { export class RadarrComponent implements OnInit, OnDestroy {
public qualities: IRadarrProfile[]; public qualities: IRadarrProfile[];
public rootFolders: IRadarrRootFolder[]; public rootFolders: IRadarrRootFolder[];
public minimumAvailabilityOptions: IMinimumAvailability[]; public minimumAvailabilityOptions: IMinimumAvailability[];
public profilesRunning: boolean; public profilesRunning: boolean;
public rootFoldersRunning: boolean; public rootFoldersRunning: boolean;
public form: UntypedFormGroup;
public is4kEnabled: boolean = false; public is4kEnabled: boolean = false;
@ViewChildren('4kForm') public form4k: QueryList<RadarrFormComponent>; public readonly form$: Observable<UntypedFormGroup>;
@ViewChildren('normalForm') public normalForm: QueryList<RadarrFormComponent>;
constructor(private radarrFacade: RadarrFacade, private readonly form4k$: ReplaySubject<QueryList<RadarrFormComponent>>;
private notificationService: NotificationService, private readonly normalForm$: ReplaySubject<QueryList<RadarrFormComponent>>;
private featureFacade: FeaturesFacade, private readonly destroyed$: Subject<void>;
private fb: UntypedFormBuilder) { }
constructor(
private readonly radarrFacade: RadarrFacade,
private readonly notificationService: NotificationService,
private readonly featureFacade: FeaturesFacade,
readonly fb: UntypedFormBuilder
) {
this.form4k$ = new ReplaySubject();
this.normalForm$ = new ReplaySubject();
this.destroyed$ = new Subject();
public ngOnInit() { this.form$ = radarrFacade.state$()
this.is4kEnabled = this.featureFacade.is4kEnabled(); .pipe(
this.radarrFacade.state$() map(x => fb.group({
.subscribe(x => { radarr: fb.group({
this.form = this.fb.group({
radarr: this.fb.group({
enabled: [x.settings.radarr.enabled], enabled: [x.settings.radarr.enabled],
apiKey: [x.settings.radarr.apiKey], apiKey: [x.settings.radarr.apiKey],
defaultQualityProfile: [+x.settings.radarr.defaultQualityProfile], defaultQualityProfile: [+x.settings.radarr.defaultQualityProfile],
@ -50,7 +55,7 @@ export class RadarrComponent implements OnInit {
minimumAvailability: [x.settings.radarr.minimumAvailability], minimumAvailability: [x.settings.radarr.minimumAvailability],
scanForAvailability: [x.settings.radarr.scanForAvailability] scanForAvailability: [x.settings.radarr.scanForAvailability]
}), }),
radarr4K: this.fb.group({ radarr4K: fb.group({
enabled: [x.settings.radarr4K.enabled], enabled: [x.settings.radarr4K.enabled],
apiKey: [x.settings.radarr4K.apiKey], apiKey: [x.settings.radarr4K.apiKey],
defaultQualityProfile: [+x.settings.radarr4K.defaultQualityProfile], defaultQualityProfile: [+x.settings.radarr4K.defaultQualityProfile],
@ -65,19 +70,44 @@ export class RadarrComponent implements OnInit {
minimumAvailability: [x.settings.radarr4K.minimumAvailability], minimumAvailability: [x.settings.radarr4K.minimumAvailability],
scanForAvailability: [x.settings.radarr4K.scanForAvailability] scanForAvailability: [x.settings.radarr4K.scanForAvailability]
}), }),
});
this.normalForm.changes.forEach((comp => {
comp.first.toggleValidators();
})) }))
)
}
@ViewChildren('4kForm')
protected set form4k(component: QueryList<RadarrFormComponent>) {
this.form4k$.next(component);
}
@ViewChildren('normalForm')
protected set normalForm(component: QueryList<RadarrFormComponent>) {
this.normalForm$.next(component);
}
public ngOnInit() {
this.is4kEnabled = this.featureFacade.is4kEnabled();
combineLatest([this.form$, this.normalForm$])
.pipe(
switchMap(([, normalForm]) => normalForm.changes),
tap(comp => comp.first.toggleValidators()),
takeUntil(this.destroyed$)
).subscribe();
if (this.is4kEnabled) { if (this.is4kEnabled) {
this.form4k.changes.forEach((comp => { combineLatest([this.form$, this.form4k$])
comp.first.toggleValidators(); .pipe(
})) switchMap(([, form4k]) => form4k.changes),
tap(comp => comp.first.toggleValidators()),
takeUntil(this.destroyed$)
).subscribe();
} }
});
} }
public ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
public onSubmit(form: UntypedFormGroup) { public onSubmit(form: UntypedFormGroup) {
if (form.invalid) { if (form.invalid) {

View file

@ -18,7 +18,7 @@ export class RadarrSettingsState {
@Action(LoadSettings) @Action(LoadSettings)
public load({ setState }: StateContext<RadarrState>): Observable<RadarrState> { public load({ setState }: StateContext<RadarrState>): Observable<RadarrState> {
const isAdmin = this.authService.hasRole("Admin"); const isAdmin = this.authService.isAdmin();
const calls = isAdmin ? [this.settingsService.getRadarr()] : [of({})]; const calls = isAdmin ? [this.settingsService.getRadarr()] : [of({})];
return combineLatest(calls).pipe( return combineLatest(calls).pipe(

View file

@ -18,7 +18,7 @@ export class SonarrSettingsState {
@Action(LoadSettings) @Action(LoadSettings)
public load({ setState }: StateContext<SonarrState>): Observable<SonarrState> { public load({ setState }: StateContext<SonarrState>): Observable<SonarrState> {
const isAdmin = this.authService.hasRole("Admin"); const isAdmin = this.authService.isAdmin();
const calls = isAdmin ? [this.sonarrService.getVersion(), this.settingsService.getSonarr()] : [of(""), of({})]; const calls = isAdmin ? [this.sonarrService.getVersion(), this.settingsService.getSonarr()] : [of(""), of({})];
return combineLatest(calls).pipe( return combineLatest(calls).pipe(

View file

@ -37,6 +37,13 @@ export class UserManagementUserComponent implements OnInit {
private appUrl: string = this.customizationFacade.appUrl(); private appUrl: string = this.customizationFacade.appUrl();
private accessToken: string; private accessToken: string;
// List of excluded notification agents that should not be shown in user preferences
private readonly excludedAgents = [
INotificationAgent.Email,
INotificationAgent.Mobile,
INotificationAgent.Webhook
];
constructor(private identityService: IdentityService, constructor(private identityService: IdentityService,
private notificationService: MessageService, private notificationService: MessageService,
private router: Router, private router: Router,
@ -74,9 +81,15 @@ export class UserManagementUserComponent implements OnInit {
} }
}); });
if(this.edit) { if(this.edit) {
this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x); this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => {
// Filter out excluded notification agents
this.notificationPreferences = x.filter(pref => !this.excludedAgents.includes(pref.agent));
});
} else { } else {
this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x); this.identityService.getNotificationPreferences().subscribe(x => {
// Filter out excluded notification agents
this.notificationPreferences = x.filter(pref => !this.excludedAgents.includes(pref.agent));
});
} }
this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => { this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => {
this.sonarrQualities = x; this.sonarrQualities = x;

View file

@ -0,0 +1,104 @@
<div class="mediaserver-container">
<div class="left-container mediaserver">
<i class="fa fa-database text-logo"></i>
</div>
<div class="right-container mediaserver">
<div class="right-container-content mediaserver">
<h1>Choose a Database</h1>
<h4>
SQLite is the default option and the easiest to set up, as it requires no additional configuration.
<br>However, it has significant limitations, including potential performance issues and database locking.
<br>While many users start with SQLite and later migrate to MySQL or MariaDB, we <b>recommend</b> beginning with MySQL or MariaDB from the start for a more robust and scalable experience.
<br/>
<br/>
For more information on using alternate databases, <a target="_blank" href="https://docs.ombi.app/info/alternate-databases/">see the documentation.</a>
</h4>
<form [formGroup]="form">
<mat-tab-group (selectedTabChange)="tabChange($event)">
<mat-tab label="SQLite">
<p class="space-or">
Just press next to continue with SQLite
</p>
</mat-tab>
<mat-tab label="MySQL/MariaDB">
<p class="space-or">
Please enter your MySQL/MariaDB connection details below
</p>
<div>
<mat-form-field>
<input matInput type="text" formControlName="host" id="host" placeholder="Host">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="number" formControlName="port" id="port" placeholder="Port">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="text" formControlName="name" id="database" placeholder="Database Name">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="text" formControlName="user" id="user" placeholder="User">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="password" formControlName="password" id="password" placeholder="Password">
</mat-form-field>
</div>
<p>{{connectionString | async}}</p>
<div style="text-align: center; margin-top: 20px">
<button (click)="save()" id="databaseSave" mat-raised-button color="accent" type="button" class="viewon-btn database" [disabled]="form.invalid">Save</button>
<div id="spinner"></div>
</div>
</mat-tab>
<mat-tab label="Postgres">
<p class="space-or">
Please enter your Postgres connection details below
</p>
<div>
<mat-form-field>
<input matInput type="text" formControlName="host" id="host" placeholder="Host">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="number" formControlName="port" id="port" placeholder="Port">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="text" formControlName="name" id="database" placeholder="Database Name">
<mat-error>This field is required</mat-error>
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="text" formControlName="user" id="user" placeholder="User">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput type="password" formControlName="password" id="password" placeholder="Password">
</mat-form-field>
</div>
<p>{{connectionString | async}}</p>
<div style="text-align: center; margin-top: 20px">
<button (click)="save()" id="databaseSave" mat-raised-button color="accent" type="button" class="viewon-btn database" [disabled]="form.invalid">Save</button>
<div id="spinner"></div>
</div>
</mat-tab>
</mat-tab-group>
</form>
</div>
</div>
</div>

View file

@ -0,0 +1,92 @@
import { Component, EventEmitter, OnInit, Output } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { BehaviorSubject } from "rxjs";
import { WizardService } from "../services/wizard.service";
import { NotificationService } from "app/services";
import { MatTabChangeEvent } from "@angular/material/tabs";
@Component({
templateUrl: "./database.component.html",
styleUrls: ["../welcome/welcome.component.scss"],
selector: "wizard-database-selector",
})
export class DatabaseComponent implements OnInit {
public constructor(private fb: FormBuilder, private service: WizardService, private notification: NotificationService) { }
@Output() public configuredDatabase = new EventEmitter<void>();
public form: FormGroup;
public connectionString = new BehaviorSubject<string>("Server=;Port=3306;Database=ombi");
public ngOnInit(): void {
this.form = this.fb.group({
type: [""],
host: ["", [Validators.required]],
port: [3306, [Validators.required]],
name: ["ombi", [Validators.required]],
user: [""],
password: [""],
});
this.form.valueChanges.subscribe(x => {
console.log(x);
let connection = `Server=${x.host};Port=${x.port};Database=${x.name}`;
if (x.user) {
connection += `;User=${x.user}`;
if (x.password) {
connection += `;Password=*******`;
}
}
if (x.type !== "MySQL") {
connection = connection.replace("Server", "Host").replace("User", "Username");
}
this.connectionString.next(connection);
});
}
public tabChange(event: MatTabChangeEvent) {
if (event.index === 0) {
this.form.reset();
}
if (event.index === 1) {
this.form.reset({
type: "MySQL",
host: "",
name: "ombi",
port: 3306,
});
this.form.controls.type.setValue("MySQL");
}
if (event.index === 2) {
this.form.reset({
type:"Postgres",
host: "",
name: "ombi",
port: 5432,
});
}
this.form.markAllAsTouched();
}
public save() {
this.service.addDatabaseConfig(this.form.value).subscribe({
next: () => {
this.notification.success(`Database configuration updated! Please now restart Ombi!`);
this.configuredDatabase.emit();
},
error: error => {
if (error.error.message) {
this.notification.error(error.error.message);
} else {
this.notification.error("Something went wrong, please check the logs");
}
},
});
}
}

View file

@ -0,0 +1,13 @@
export interface DatabaseSettings {
type: string;
host: string;
port: number;
name: string;
user: string;
password: string;
}
export interface DatabaseConfigurationResult {
success: boolean;
message: string;
}

View file

@ -5,6 +5,7 @@ import { Observable } from "rxjs";
import { ICustomizationSettings } from "../../interfaces"; import { ICustomizationSettings } from "../../interfaces";
import { ServiceHelpers } from "../../services"; import { ServiceHelpers } from "../../services";
import { IOmbiConfigModel } from "../models/OmbiConfigModel"; import { IOmbiConfigModel } from "../models/OmbiConfigModel";
import { DatabaseConfigurationResult, DatabaseSettings } from "../models/DatabaseSettings";
@Injectable() @Injectable()
@ -16,4 +17,8 @@ export class WizardService extends ServiceHelpers {
public addOmbiConfig(config: IOmbiConfigModel): Observable<ICustomizationSettings> { public addOmbiConfig(config: IOmbiConfigModel): Observable<ICustomizationSettings> {
return this.http.post<ICustomizationSettings>(`${this.url}config`, config, {headers: this.headers}); return this.http.post<ICustomizationSettings>(`${this.url}config`, config, {headers: this.headers});
} }
public addDatabaseConfig(config: DatabaseSettings): Observable<DatabaseConfigurationResult> {
return this.http.post<DatabaseConfigurationResult>(`${this.url}database`, config, {headers: this.headers});
}
} }

View file

@ -1,5 +1,6 @@
<div class="wizard-background"> <div class="wizard-background">
<div class="container wizard-inner"> <div class="container wizard-inner">
@if (!needsRestart) {
<mat-stepper linear #stepper> <mat-stepper linear #stepper>
<mat-step > <mat-step >
<form > <form >
@ -29,6 +30,12 @@
</div> </div>
</form> </form>
</mat-step> </mat-step>
<mat-step>
<ng-template matStepLabel>Database</ng-template>
<wizard-database-selector (configuredDatabase)="databaseConfigured()"></wizard-database-selector>
<button mat-button matStepperPrevious class="mat-raised-button mat-error left">Back</button>
<button mat-button matStepperNext class="mat-raised-button mat-accent right" data-test="nextDatabase">Next</button>
</mat-step>
<mat-step [optional]="true"> <mat-step [optional]="true">
<form > <form >
@ -82,5 +89,22 @@
</div> </div>
</mat-step> </mat-step>
</mat-stepper> </mat-stepper>
} @else {
<mat-stepper linear>
<mat-step >
<ng-template matStepLabel>Restart</ng-template>
<div class="welcome-container">
<div class="left-container mediaserver">
<i class="fa fa-database text-logo"></i>
</div>
<div class="right-container">
<div class="right-container-content">
<h1>Please Restart Ombi for the database changes to take effect!</h1>
</div>
</div>
</div>
</mat-step>
</mat-stepper>
}
</div> </div>
</div> </div>

View file

@ -151,6 +151,12 @@ p.space-or{
color: #A45FC4; color: #A45FC4;
} }
.viewon-btn.database {
border: 1px solid #A45FC4;
color: #A45FC4;
}
.text-logo{ .text-logo{
font-size:12em; font-size:12em;
} }

View file

@ -17,6 +17,7 @@ export class WelcomeComponent implements OnInit {
@ViewChild('stepper', {static: false}) public stepper: MatStepper; @ViewChild('stepper', {static: false}) public stepper: MatStepper;
public localUser: ICreateWizardUser; public localUser: ICreateWizardUser;
public needsRestart: boolean = false;
public config: IOmbiConfigModel; public config: IOmbiConfigModel;
constructor(private router: Router, private identityService: IdentityService, constructor(private router: Router, private identityService: IdentityService,
@ -48,7 +49,7 @@ export class WelcomeComponent implements OnInit {
this.settingsService.verifyUrl(this.config.applicationUrl).subscribe(x => { this.settingsService.verifyUrl(this.config.applicationUrl).subscribe(x => {
if (!x) { if (!x) {
this.notificationService.error(`The URL "${this.config.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`); this.notificationService.error(`The URL "${this.config.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`);
this.stepper.selectedIndex = 3; this.stepper.selectedIndex = 4;
return; return;
} }
this.saveConfig(); this.saveConfig();
@ -58,6 +59,10 @@ export class WelcomeComponent implements OnInit {
} }
} }
public databaseConfigured() {
this.needsRestart = true;
}
private saveConfig() { private saveConfig() {
this.WizardService.addOmbiConfig(this.config).subscribe({ this.WizardService.addOmbiConfig(this.config).subscribe({
next: (config) => { next: (config) => {

View file

@ -12,6 +12,7 @@ import { MediaServerComponent } from "./mediaserver/mediaserver.component";
import { PlexComponent } from "./plex/plex.component"; import { PlexComponent } from "./plex/plex.component";
import { WelcomeComponent } from "./welcome/welcome.component"; import { WelcomeComponent } from "./welcome/welcome.component";
import { OmbiConfigComponent } from "./ombiconfig/ombiconfig.component"; import { OmbiConfigComponent } from "./ombiconfig/ombiconfig.component";
import { DatabaseComponent } from "./database/database.component";
import { EmbyService } from "../services"; import { EmbyService } from "../services";
import { JellyfinService } from "../services"; import { JellyfinService } from "../services";
@ -48,6 +49,7 @@ const routes: Routes = [
EmbyComponent, EmbyComponent,
JellyfinComponent, JellyfinComponent,
OmbiConfigComponent, OmbiConfigComponent,
DatabaseComponent,
], ],
exports: [ exports: [
RouterModule, RouterModule,

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -14,7 +13,6 @@ using Ombi.Api.TheMovieDb.Models;
using Ombi.Attributes; using Ombi.Attributes;
using Ombi.Core.Authentication; using Ombi.Core.Authentication;
using Ombi.Core.Engine; using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces;
using Ombi.Core.Helpers; using Ombi.Core.Helpers;
using Ombi.Core.Models.UI; using Ombi.Core.Models.UI;
using Ombi.Core.Services; using Ombi.Core.Services;
@ -29,9 +27,7 @@ using Ombi.Schedule.Jobs.Ombi;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using Ombi.Settings.Settings.Models.Notifications; using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using Ombi.Store.Repository.Requests;
using IdentityResult = Microsoft.AspNetCore.Identity.IdentityResult; using IdentityResult = Microsoft.AspNetCore.Identity.IdentityResult;
using OmbiIdentityResult = Ombi.Models.Identity.IdentityResult; using OmbiIdentityResult = Ombi.Models.Identity.IdentityResult;

View file

@ -40,7 +40,6 @@ namespace Ombi.Controllers.V1
/// <summary> /// <summary>
/// The Settings Controller /// The Settings Controller
/// </summary> /// </summary>
[Admin]
[ApiV1] [ApiV1]
[Produces("application/json")] [Produces("application/json")]
[ApiController] [ApiController]
@ -78,6 +77,7 @@ namespace Ombi.Controllers.V1
/// Gets the Ombi settings. /// Gets the Ombi settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("ombi")] [HttpGet("ombi")]
public async Task<OmbiSettings> OmbiSettings() public async Task<OmbiSettings> OmbiSettings()
{ {
@ -110,6 +110,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="ombi">The ombi.</param> /// <param name="ombi">The ombi.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("ombi")] [HttpPost("ombi")]
public async Task<bool> OmbiSettings([FromBody]OmbiSettings ombi) public async Task<bool> OmbiSettings([FromBody]OmbiSettings ombi)
{ {
@ -145,6 +146,7 @@ namespace Ombi.Controllers.V1
return model; return model;
} }
[Admin]
[HttpPost("ombi/resetApi")] [HttpPost("ombi/resetApi")]
public async Task<string> ResetApiKey() public async Task<string> ResetApiKey()
{ {
@ -159,6 +161,7 @@ namespace Ombi.Controllers.V1
/// Gets the Plex Settings. /// Gets the Plex Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("plex")] [HttpGet("plex")]
public async Task<PlexSettings> PlexSettings() public async Task<PlexSettings> PlexSettings()
{ {
@ -185,6 +188,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="plex">The plex.</param> /// <param name="plex">The plex.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("plex")] [HttpPost("plex")]
public async Task<bool> PlexSettings([FromBody]PlexSettings plex) public async Task<bool> PlexSettings([FromBody]PlexSettings plex)
{ {
@ -207,6 +211,7 @@ namespace Ombi.Controllers.V1
/// Gets the Emby Settings. /// Gets the Emby Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("emby")] [HttpGet("emby")]
public async Task<EmbySettings> EmbySettings() public async Task<EmbySettings> EmbySettings()
{ {
@ -218,6 +223,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="emby">The emby.</param> /// <param name="emby">The emby.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("emby")] [HttpPost("emby")]
public async Task<bool> EmbySettings([FromBody]EmbySettings emby) public async Task<bool> EmbySettings([FromBody]EmbySettings emby)
{ {
@ -243,6 +249,7 @@ namespace Ombi.Controllers.V1
/// Gets the Jellyfin Settings. /// Gets the Jellyfin Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("jellyfin")] [HttpGet("jellyfin")]
public async Task<JellyfinSettings> JellyfinSettings() public async Task<JellyfinSettings> JellyfinSettings()
{ {
@ -254,6 +261,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="jellyfin">The jellyfin.</param> /// <param name="jellyfin">The jellyfin.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("jellyfin")] [HttpPost("jellyfin")]
public async Task<bool> JellyfinSettings([FromBody]JellyfinSettings jellyfin) public async Task<bool> JellyfinSettings([FromBody]JellyfinSettings jellyfin)
{ {
@ -291,6 +299,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("landingpage")] [HttpPost("landingpage")]
public async Task<bool> LandingPageSettings([FromBody]LandingPageSettings settings) public async Task<bool> LandingPageSettings([FromBody]LandingPageSettings settings)
{ {
@ -326,6 +335,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("customization")] [HttpPost("customization")]
public async Task<bool> CustomizationSettings([FromBody]CustomizationSettings settings) public async Task<bool> CustomizationSettings([FromBody]CustomizationSettings settings)
{ {
@ -344,6 +354,7 @@ namespace Ombi.Controllers.V1
/// Get's the preset themes available /// Get's the preset themes available
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("themes")] [HttpGet("themes")]
public async Task<IEnumerable<PresetThemeViewModel>> GetThemes() public async Task<IEnumerable<PresetThemeViewModel>> GetThemes()
{ {
@ -377,6 +388,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("sonarr")] [HttpGet("sonarr")]
[PowerUser]
public async Task<SonarrSettings> SonarrSettings() public async Task<SonarrSettings> SonarrSettings()
{ {
return await Get<SonarrSettings>(); return await Get<SonarrSettings>();
@ -388,6 +400,7 @@ namespace Ombi.Controllers.V1
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[HttpPost("sonarr")] [HttpPost("sonarr")]
[Admin]
public async Task<bool> SonarrSettings([FromBody]SonarrSettings settings) public async Task<bool> SonarrSettings([FromBody]SonarrSettings settings)
{ {
var result = await Save(settings); var result = await Save(settings);
@ -403,6 +416,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("radarr")] [HttpGet("radarr")]
[PowerUser]
public async Task<RadarrCombinedModel> RadarrSettings() public async Task<RadarrCombinedModel> RadarrSettings()
{ {
return new RadarrCombinedModel return new RadarrCombinedModel
@ -416,6 +430,7 @@ namespace Ombi.Controllers.V1
/// Gets the Lidarr Settings. /// Gets the Lidarr Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("lidarr")] [HttpGet("lidarr")]
public async Task<LidarrSettings> LidarrSettings() public async Task<LidarrSettings> LidarrSettings()
{ {
@ -439,6 +454,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("lidarr")] [HttpPost("lidarr")]
public async Task<bool> LidarrSettings([FromBody]LidarrSettings settings) public async Task<bool> LidarrSettings([FromBody]LidarrSettings settings)
{ {
@ -455,6 +471,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("authentication")] [HttpPost("authentication")]
public async Task<bool> AuthenticationsSettings([FromBody]AuthenticationSettings settings) public async Task<bool> AuthenticationsSettings([FromBody]AuthenticationSettings settings)
{ {
@ -477,6 +494,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("radarr")] [HttpPost("radarr")]
public async Task<bool> RadarrSettings([FromBody]RadarrCombinedModel settings) public async Task<bool> RadarrSettings([FromBody]RadarrCombinedModel settings)
{ {
@ -498,6 +516,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("Update")] [HttpPost("Update")]
public async Task<bool> UpdateSettings([FromBody]UpdateSettings settings) public async Task<bool> UpdateSettings([FromBody]UpdateSettings settings)
{ {
@ -508,6 +527,7 @@ namespace Ombi.Controllers.V1
/// Gets the UserManagement Settings. /// Gets the UserManagement Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("UserManagement")] [HttpGet("UserManagement")]
public async Task<UserManagementSettings> UserManagementSettings() public async Task<UserManagementSettings> UserManagementSettings()
{ {
@ -519,6 +539,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("UserManagement")] [HttpPost("UserManagement")]
public async Task<bool> UserManagementSettings([FromBody]UserManagementSettings settings) public async Task<bool> UserManagementSettings([FromBody]UserManagementSettings settings)
{ {
@ -529,6 +550,7 @@ namespace Ombi.Controllers.V1
/// Gets the Update Settings. /// Gets the Update Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("Update")] [HttpGet("Update")]
public async Task<UpdateSettings> UpdateSettings() public async Task<UpdateSettings> UpdateSettings()
{ {
@ -541,6 +563,7 @@ namespace Ombi.Controllers.V1
/// Gets the CouchPotatoSettings Settings. /// Gets the CouchPotatoSettings Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("CouchPotato")] [HttpGet("CouchPotato")]
public async Task<CouchPotatoSettings> CouchPotatoSettings() public async Task<CouchPotatoSettings> CouchPotatoSettings()
{ {
@ -552,6 +575,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("CouchPotato")] [HttpPost("CouchPotato")]
public async Task<bool> CouchPotatoSettings([FromBody]CouchPotatoSettings settings) public async Task<bool> CouchPotatoSettings([FromBody]CouchPotatoSettings settings)
{ {
@ -562,6 +586,7 @@ namespace Ombi.Controllers.V1
/// Gets the DogNzbSettings Settings. /// Gets the DogNzbSettings Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("DogNzb")] [HttpGet("DogNzb")]
public async Task<DogNzbSettings> DogNzbSettings() public async Task<DogNzbSettings> DogNzbSettings()
{ {
@ -573,6 +598,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("DogNzb")] [HttpPost("DogNzb")]
public async Task<bool> DogNzbSettings([FromBody]DogNzbSettings settings) public async Task<bool> DogNzbSettings([FromBody]DogNzbSettings settings)
{ {
@ -584,6 +610,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("SickRage")] [HttpPost("SickRage")]
public async Task<bool> SickRageSettings([FromBody]SickRageSettings settings) public async Task<bool> SickRageSettings([FromBody]SickRageSettings settings)
{ {
@ -594,6 +621,7 @@ namespace Ombi.Controllers.V1
/// Gets the SickRage Settings. /// Gets the SickRage Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("SickRage")] [HttpGet("SickRage")]
public async Task<SickRageSettings> SickRageSettings() public async Task<SickRageSettings> SickRageSettings()
{ {
@ -604,6 +632,7 @@ namespace Ombi.Controllers.V1
/// Gets the JobSettings Settings. /// Gets the JobSettings Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("jobs")] [HttpGet("jobs")]
public async Task<JobSettings> JobSettings() public async Task<JobSettings> JobSettings()
{ {
@ -636,6 +665,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("jobs")] [HttpPost("jobs")]
public async Task<JobSettingsViewModel> JobSettings([FromBody]JobSettings settings) public async Task<JobSettingsViewModel> JobSettings([FromBody]JobSettings settings)
{ {
@ -679,6 +709,7 @@ namespace Ombi.Controllers.V1
} }
[HttpPost("testcron")] [HttpPost("testcron")]
[Admin]
public CronTestModel TestCron([FromBody] CronViewModelBody body) public CronTestModel TestCron([FromBody] CronViewModelBody body)
{ {
var model = new CronTestModel(); var model = new CronTestModel();
@ -712,6 +743,7 @@ namespace Ombi.Controllers.V1
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[HttpPost("Issues")] [HttpPost("Issues")]
[Admin]
public async Task<bool> IssueSettings([FromBody]IssueSettings settings) public async Task<bool> IssueSettings([FromBody]IssueSettings settings)
{ {
return await Save(settings); return await Save(settings);
@ -742,6 +774,7 @@ namespace Ombi.Controllers.V1
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
/// <returns></returns> /// <returns></returns>
[HttpPost("vote")] [HttpPost("vote")]
[Admin]
public async Task<bool> VoteSettings([FromBody]VoteSettings settings) public async Task<bool> VoteSettings([FromBody]VoteSettings settings)
{ {
return await Save(settings); return await Save(settings);
@ -752,6 +785,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("vote")] [HttpGet("vote")]
[Admin]
public async Task<VoteSettings> VoteSettings() public async Task<VoteSettings> VoteSettings()
{ {
return await Get<VoteSettings>(); return await Get<VoteSettings>();
@ -770,6 +804,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="settings">The settings.</param> /// <param name="settings">The settings.</param>
[HttpPost("themoviedb")] [HttpPost("themoviedb")]
[Admin]
public async Task<bool> TheMovieDbSettings([FromBody]TheMovieDbSettings settings) public async Task<bool> TheMovieDbSettings([FromBody]TheMovieDbSettings settings)
{ {
return await Save(settings); return await Save(settings);
@ -778,6 +813,7 @@ namespace Ombi.Controllers.V1
/// <summary> /// <summary>
/// Get The Movie DB settings. /// Get The Movie DB settings.
/// </summary> /// </summary>
[Admin]
[HttpGet("themoviedb")] [HttpGet("themoviedb")]
public async Task<TheMovieDbSettings> TheMovieDbSettings() public async Task<TheMovieDbSettings> TheMovieDbSettings()
{ {
@ -789,6 +825,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/email")] [HttpPost("notifications/email")]
public async Task<bool> EmailNotificationSettings([FromBody] EmailNotificationsViewModel model) public async Task<bool> EmailNotificationSettings([FromBody] EmailNotificationsViewModel model)
{ {
@ -806,6 +843,7 @@ namespace Ombi.Controllers.V1
/// Gets the Email Notification Settings. /// Gets the Email Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/email")] [HttpGet("notifications/email")]
public async Task<EmailNotificationsViewModel> EmailNotificationSettings() public async Task<EmailNotificationsViewModel> EmailNotificationSettings()
{ {
@ -836,6 +874,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/discord")] [HttpPost("notifications/discord")]
public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model) public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model)
{ {
@ -853,6 +892,7 @@ namespace Ombi.Controllers.V1
/// Gets the discord Notification Settings. /// Gets the discord Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/discord")] [HttpGet("notifications/discord")]
public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings() public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings()
{ {
@ -871,6 +911,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/telegram")] [HttpPost("notifications/telegram")]
public async Task<bool> TelegramNotificationSettings([FromBody] TelegramNotificationsViewModel model) public async Task<bool> TelegramNotificationSettings([FromBody] TelegramNotificationsViewModel model)
{ {
@ -888,6 +929,7 @@ namespace Ombi.Controllers.V1
/// Gets the telegram Notification Settings. /// Gets the telegram Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/telegram")] [HttpGet("notifications/telegram")]
public async Task<TelegramNotificationsViewModel> TelegramNotificationSettings() public async Task<TelegramNotificationsViewModel> TelegramNotificationSettings()
{ {
@ -905,6 +947,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/pushbullet")] [HttpPost("notifications/pushbullet")]
public async Task<bool> PushbulletNotificationSettings([FromBody] PushbulletNotificationViewModel model) public async Task<bool> PushbulletNotificationSettings([FromBody] PushbulletNotificationViewModel model)
{ {
@ -922,6 +965,7 @@ namespace Ombi.Controllers.V1
/// Gets the pushbullet Notification Settings. /// Gets the pushbullet Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/pushbullet")] [HttpGet("notifications/pushbullet")]
public async Task<PushbulletNotificationViewModel> PushbulletNotificationSettings() public async Task<PushbulletNotificationViewModel> PushbulletNotificationSettings()
{ {
@ -939,6 +983,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/pushover")] [HttpPost("notifications/pushover")]
public async Task<bool> PushoverNotificationSettings([FromBody] PushoverNotificationViewModel model) public async Task<bool> PushoverNotificationSettings([FromBody] PushoverNotificationViewModel model)
{ {
@ -956,6 +1001,7 @@ namespace Ombi.Controllers.V1
/// Gets the pushover Notification Settings. /// Gets the pushover Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/pushover")] [HttpGet("notifications/pushover")]
public async Task<PushoverNotificationViewModel> PushoverNotificationSettings() public async Task<PushoverNotificationViewModel> PushoverNotificationSettings()
{ {
@ -974,6 +1020,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/slack")] [HttpPost("notifications/slack")]
public async Task<bool> SlacktNotificationSettings([FromBody] SlackNotificationsViewModel model) public async Task<bool> SlacktNotificationSettings([FromBody] SlackNotificationsViewModel model)
{ {
@ -991,6 +1038,7 @@ namespace Ombi.Controllers.V1
/// Gets the slack Notification Settings. /// Gets the slack Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/slack")] [HttpGet("notifications/slack")]
public async Task<SlackNotificationsViewModel> SlackNotificationSettings() public async Task<SlackNotificationsViewModel> SlackNotificationSettings()
{ {
@ -1008,6 +1056,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/mattermost")] [HttpPost("notifications/mattermost")]
public async Task<bool> MattermostNotificationSettings([FromBody] MattermostNotificationsViewModel model) public async Task<bool> MattermostNotificationSettings([FromBody] MattermostNotificationsViewModel model)
{ {
@ -1025,6 +1074,7 @@ namespace Ombi.Controllers.V1
/// Gets the Mattermost Notification Settings. /// Gets the Mattermost Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/mattermost")] [HttpGet("notifications/mattermost")]
public async Task<MattermostNotificationsViewModel> MattermostNotificationSettings() public async Task<MattermostNotificationsViewModel> MattermostNotificationSettings()
{ {
@ -1041,6 +1091,7 @@ namespace Ombi.Controllers.V1
/// Gets the Twilio Notification Settings. /// Gets the Twilio Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/twilio")] [HttpGet("notifications/twilio")]
public async Task<TwilioSettingsViewModel> TwilioNotificationSettings() public async Task<TwilioSettingsViewModel> TwilioNotificationSettings()
{ {
@ -1062,6 +1113,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/twilio")] [HttpPost("notifications/twilio")]
public async Task<bool> TwilioNotificationSettings([FromBody] TwilioSettingsViewModel model) public async Task<bool> TwilioNotificationSettings([FromBody] TwilioSettingsViewModel model)
{ {
@ -1080,6 +1132,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/mobile")] [HttpPost("notifications/mobile")]
public async Task<bool> MobileNotificationSettings([FromBody] MobileNotificationsViewModel model) public async Task<bool> MobileNotificationSettings([FromBody] MobileNotificationsViewModel model)
{ {
@ -1097,6 +1150,7 @@ namespace Ombi.Controllers.V1
/// Gets the Mobile Notification Settings. /// Gets the Mobile Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/mobile")] [HttpGet("notifications/mobile")]
public async Task<MobileNotificationsViewModel> MobileNotificationSettings() public async Task<MobileNotificationsViewModel> MobileNotificationSettings()
{ {
@ -1114,6 +1168,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/gotify")] [HttpPost("notifications/gotify")]
public async Task<bool> GotifyNotificationSettings([FromBody] GotifyNotificationViewModel model) public async Task<bool> GotifyNotificationSettings([FromBody] GotifyNotificationViewModel model)
{ {
@ -1131,6 +1186,7 @@ namespace Ombi.Controllers.V1
/// Gets the gotify Notification Settings. /// Gets the gotify Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/gotify")] [HttpGet("notifications/gotify")]
public async Task<GotifyNotificationViewModel> GotifyNotificationSettings() public async Task<GotifyNotificationViewModel> GotifyNotificationSettings()
{ {
@ -1148,6 +1204,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/webhook")] [HttpPost("notifications/webhook")]
public async Task<bool> WebhookNotificationSettings([FromBody] WebhookNotificationViewModel model) public async Task<bool> WebhookNotificationSettings([FromBody] WebhookNotificationViewModel model)
{ {
@ -1161,6 +1218,7 @@ namespace Ombi.Controllers.V1
/// Gets the webhook notification settings. /// Gets the webhook notification settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/webhook")] [HttpGet("notifications/webhook")]
public async Task<WebhookNotificationViewModel> WebhookNotificationSettings() public async Task<WebhookNotificationViewModel> WebhookNotificationSettings()
{ {
@ -1175,6 +1233,7 @@ namespace Ombi.Controllers.V1
/// </summary> /// </summary>
/// <param name="model">The model.</param> /// <param name="model">The model.</param>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpPost("notifications/newsletter")] [HttpPost("notifications/newsletter")]
public async Task<bool> NewsletterSettings([FromBody] NewsletterNotificationViewModel model) public async Task<bool> NewsletterSettings([FromBody] NewsletterNotificationViewModel model)
{ {
@ -1189,6 +1248,7 @@ namespace Ombi.Controllers.V1
} }
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
[Admin]
[HttpPost("notifications/newsletterdatabase")] [HttpPost("notifications/newsletterdatabase")]
public async Task<bool> UpdateNewsletterDatabase() public async Task<bool> UpdateNewsletterDatabase()
{ {
@ -1199,6 +1259,7 @@ namespace Ombi.Controllers.V1
/// Gets the Newsletter Notification Settings. /// Gets the Newsletter Notification Settings.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Admin]
[HttpGet("notifications/newsletter")] [HttpGet("notifications/newsletter")]
public async Task<NewsletterNotificationViewModel> NewsletterSettings() public async Task<NewsletterNotificationViewModel> NewsletterSettings()
{ {

View file

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Authorization; using System;
using System.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Attributes; using Ombi.Attributes;
using Ombi.Core.Settings; using Ombi.Core.Settings;
@ -6,6 +8,10 @@ using Ombi.Helpers;
using Ombi.Models.V2; using Ombi.Models.V2;
using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using MySqlConnector;
using Npgsql;
using Ombi.Core.Services;
namespace Ombi.Controllers.V2 namespace Ombi.Controllers.V2
{ {
@ -13,10 +19,20 @@ namespace Ombi.Controllers.V2
[AllowAnonymous] [AllowAnonymous]
public class WizardController : V2Controller public class WizardController : V2Controller
{ {
private readonly ISettingsService<OmbiSettings> _ombiSettings;
private readonly IDatabaseConfigurationService _databaseConfigurationService;
private readonly ILogger _logger;
private ISettingsService<CustomizationSettings> _customizationSettings { get; } private ISettingsService<CustomizationSettings> _customizationSettings { get; }
public WizardController(ISettingsService<CustomizationSettings> customizationSettings) public WizardController(
ISettingsService<CustomizationSettings> customizationSettings,
ISettingsService<OmbiSettings> ombiSettings,
IDatabaseConfigurationService databaseConfigurationService,
ILogger<WizardController> logger)
{ {
_ombiSettings = ombiSettings;
_databaseConfigurationService = databaseConfigurationService;
_logger = logger;
_customizationSettings = customizationSettings; _customizationSettings = customizationSettings;
} }
@ -29,6 +45,13 @@ namespace Ombi.Controllers.V2
return BadRequest(); return BadRequest();
} }
var ombiSettings = await _ombiSettings.GetSettingsAsync();
if (ombiSettings.Wizard)
{
_logger.LogError("Wizard has already been completed");
return BadRequest();
}
var settings = await _customizationSettings.GetSettingsAsync(); var settings = await _customizationSettings.GetSettingsAsync();
if (config.ApplicationName.HasValue()) if (config.ApplicationName.HasValue())
@ -50,5 +73,67 @@ namespace Ombi.Controllers.V2
return new OkObjectResult(settings); return new OkObjectResult(settings);
} }
[HttpPost("database")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> DatabaseConfig([FromBody] WizardDatabaseConfiguration config, CancellationToken token)
{
if (config == null)
{
return BadRequest();
}
var ombiSettings = await _ombiSettings.GetSettingsAsync();
if (ombiSettings.Wizard)
{
_logger.LogError("Wizard has already been completed");
return BadRequest();
}
var sanitizedType = config.Type.Replace(Environment.NewLine, "").Replace("\n", "").Replace("\r", "");
_logger.LogInformation("Setting up database type: {0}", sanitizedType);
var connectionString = string.Empty;
if (config.Type == IDatabaseConfigurationService.MySqlDatabase)
{
_logger.LogInformation("Building MySQL connectionstring");
var builder = new MySqlConnectionStringBuilder
{
Database = config.Name,
Port = Convert.ToUInt32(config.Port),
Server = config.Host,
UserID = config.User,
Password = config.Password
};
connectionString = builder.ToString();
}
if (config.Type == IDatabaseConfigurationService.PostgresDatabase)
{
_logger.LogInformation("Building Postgres connectionstring");
var builder = new NpgsqlConnectionStringBuilder
{
Host = config.Host,
Port = config.Port,
Database = config.Name,
Username = config.User,
Password = config.Password
};
connectionString = builder.ToString();
}
var result = await _databaseConfigurationService.ConfigureDatabase(config.Type, connectionString, token);
if (!result)
{
return BadRequest(new DatabaseConfigurationResult(false, "Could not configure the database, please check the logs"));
}
return Ok(new DatabaseConfigurationResult(true, "Database configured successfully"));
}
public record DatabaseConfigurationResult(bool Success, string Message);
} }
} }

View file

@ -8,6 +8,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks;
using MySqlConnector; using MySqlConnector;
using Newtonsoft.Json; using Newtonsoft.Json;
using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal; using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
using Ombi.Core.Helpers;
using Ombi.Core.Models;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Context; using Ombi.Store.Context;
using Ombi.Store.Context.MySql; using Ombi.Store.Context.MySql;
@ -38,11 +40,11 @@ namespace Ombi.Extensions
AddSqliteHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase); AddSqliteHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
break; break;
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<OmbiContext, OmbiMySqlContext>(x => ConfigureMySql(x, configuration.OmbiDatabase)); services.AddDbContext<OmbiContext, OmbiMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.OmbiDatabase));
AddMySqlHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase); AddMySqlHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
break; break;
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<OmbiContext, OmbiPostgresContext>(x => ConfigurePostgres(x, configuration.OmbiDatabase)); services.AddDbContext<OmbiContext, OmbiPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.OmbiDatabase));
AddPostgresHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase); AddPostgresHealthCheck(hcBuilder, "Ombi Database", configuration.OmbiDatabase);
break; break;
} }
@ -54,11 +56,11 @@ namespace Ombi.Extensions
AddSqliteHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase); AddSqliteHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
break; break;
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<ExternalContext, ExternalMySqlContext>(x => ConfigureMySql(x, configuration.ExternalDatabase)); services.AddDbContext<ExternalContext, ExternalMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.ExternalDatabase));
AddMySqlHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase); AddMySqlHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
break; break;
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<ExternalContext, ExternalPostgresContext>(x => ConfigurePostgres(x, configuration.ExternalDatabase)); services.AddDbContext<ExternalContext, ExternalPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.ExternalDatabase));
AddPostgresHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase); AddPostgresHealthCheck(hcBuilder, "External Database", configuration.ExternalDatabase);
break; break;
} }
@ -70,11 +72,11 @@ namespace Ombi.Extensions
AddSqliteHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase); AddSqliteHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
break; break;
case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(MySqlDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<SettingsContext, SettingsMySqlContext>(x => ConfigureMySql(x, configuration.SettingsDatabase)); services.AddDbContext<SettingsContext, SettingsMySqlContext>(x => DatabaseConfigurationSetup.ConfigureMySql(x, configuration.SettingsDatabase));
AddMySqlHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase); AddMySqlHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
break; break;
case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase): case var type when type.Equals(PostgresDatabase, StringComparison.InvariantCultureIgnoreCase):
services.AddDbContext<SettingsContext, SettingsPostgresContext>(x => ConfigurePostgres(x, configuration.SettingsDatabase)); services.AddDbContext<SettingsContext, SettingsPostgresContext>(x => DatabaseConfigurationSetup.ConfigurePostgres(x, configuration.SettingsDatabase));
AddPostgresHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase); AddPostgresHealthCheck(hcBuilder, "Settings Database", configuration.SettingsDatabase);
break; break;
} }
@ -150,95 +152,5 @@ namespace Ombi.Extensions
SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD); SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD);
options.UseSqlite(config.ConnectionString); options.UseSqlite(config.ConnectionString);
} }
public static void ConfigureMySql(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
if (string.IsNullOrEmpty(config.ConnectionString))
{
throw new ArgumentNullException("ConnectionString for the MySql/Mariadb database is empty");
}
options.UseMySql(config.ConnectionString, GetServerVersion(config.ConnectionString), b =>
{
//b.CharSetBehavior(Pomelo.EntityFrameworkCore.MySql.Infrastructure.CharSetBehavior.NeverAppend); // ##ISSUE, link to migrations?
b.EnableRetryOnFailure();
});
}
public static void ConfigurePostgres(DbContextOptionsBuilder options, PerDatabaseConfiguration config)
{
options.UseNpgsql(config.ConnectionString, b =>
{
b.EnableRetryOnFailure();
}).ReplaceService<ISqlGenerationHelper, NpgsqlCaseInsensitiveSqlGenerationHelper>();
}
private static ServerVersion GetServerVersion(string connectionString)
{
// Workaround Windows bug, that can lead to the following exception:
//
// MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
// ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
// ---> System.ComponentModel.Win32Exception (0x8009030F): The message or signature supplied for verification has been altered
//
// See https://github.com/dotnet/runtime/issues/17005#issuecomment-305848835
//
// Also workaround for the fact, that ServerVersion.AutoDetect() does not use any retrying strategy.
ServerVersion serverVersion = null;
#pragma warning disable EF1001
var retryPolicy = Policy.Handle<Exception>(exception => MySqlTransientExceptionDetector.ShouldRetryOn(exception))
#pragma warning restore EF1001
.WaitAndRetry(3, (count, context) => TimeSpan.FromMilliseconds(count * 250));
serverVersion = retryPolicy.Execute(() => serverVersion = ServerVersion.AutoDetect(connectionString));
return serverVersion;
}
public class DatabaseConfiguration
{
public DatabaseConfiguration()
{
}
public DatabaseConfiguration(string defaultSqlitePath)
{
OmbiDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "Ombi.db")}");
SettingsDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiSettings.db")}");
ExternalDatabase = new PerDatabaseConfiguration(SqliteDatabase, $"Data Source={Path.Combine(defaultSqlitePath, "OmbiExternal.db")}");
}
public PerDatabaseConfiguration OmbiDatabase { get; set; }
public PerDatabaseConfiguration SettingsDatabase { get; set; }
public PerDatabaseConfiguration ExternalDatabase { get; set; }
}
public class PerDatabaseConfiguration
{
public PerDatabaseConfiguration(string type, string connectionString)
{
Type = type;
ConnectionString = connectionString;
}
// Used in Deserialization
public PerDatabaseConfiguration()
{
}
public string Type { get; set; }
public string ConnectionString { get; set; }
}
public class NpgsqlCaseInsensitiveSqlGenerationHelper : NpgsqlSqlGenerationHelper
{
const string EFMigrationsHisory = "__EFMigrationsHistory";
public NpgsqlCaseInsensitiveSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies)
: base(dependencies) { }
public override string DelimitIdentifier(string identifier) =>
base.DelimitIdentifier(identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
public override void DelimitIdentifier(StringBuilder builder, string identifier)
=> base.DelimitIdentifier(builder, identifier == EFMigrationsHisory ? identifier : identifier.ToLower());
}
} }
} }

View file

@ -0,0 +1,3 @@
namespace Ombi.Models.V2;
public record WizardDatabaseConfiguration(string Type, string Host, int Port, string Name, string User, string Password);

View file

@ -54,10 +54,6 @@
<Content Remove="database.json" /> <Content Remove="database.json" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="database.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" /> <None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup> </ItemGroup>

View file

@ -87,7 +87,10 @@ namespace Ombi
services.AddJwtAuthentication(); services.AddJwtAuthentication();
services.AddMvc() services.AddMvc()
.AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); .AddNewtonsoftJson(x => {
x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
x.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
});
services.AddOmbiMappingProfile(); services.AddOmbiMappingProfile();
services.AddAutoMapper(expression => expression.AddCollectionMappers()); services.AddAutoMapper(expression => expression.AddCollectionMappers());

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