diff --git a/.github/workflows/automation-tests.yml b/.github/workflows/automation-tests.yml
index 97572ad25..5c40edd58 100644
--- a/.github/workflows/automation-tests.yml
+++ b/.github/workflows/automation-tests.yml
@@ -24,7 +24,7 @@ jobs:
with:
node-version: '18'
- - uses: actions/cache@v2
+ - uses: actions/cache@v4
with:
path: |
'**/node_modules'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 54b623518..998b8387c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,7 +15,7 @@ jobs:
node-version: '18'
- name: NodeModules Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
@@ -42,7 +42,7 @@ jobs:
dotnet-version: '8.0.x'
- name: Nuget Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@@ -112,7 +112,7 @@ jobs:
dotnet-version: '5.0.x'
- name: Nuget Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index f068794be..d142c46c8 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -17,7 +17,7 @@
# fetch-depth: 0
# - name: NodeModules Cache
-# uses: actions/cache@v2
+# uses: actions/cache@v4
# with:
# path: '**/node_modules'
# key: node_modules-${{ hashFiles('**/yarn.lock') }}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index ea65cfaa6..5a0e0a46d 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -20,7 +20,7 @@ jobs:
node-version: '18'
- name: NodeModules Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
@@ -41,7 +41,7 @@ jobs:
dotnet-version: '8.0.x'
- name: Nuget Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@@ -101,7 +101,7 @@ jobs:
dotnet-version: '8.0.x'
- name: Nuget Cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f01e65c8..115942b8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,117 @@
+## [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)
@@ -2097,879 +2211,3 @@
-## [4.43.5](https://github.com/Ombi-app/Ombi/compare/v4.43.4...v4.43.5) (2023-08-24)
-
-
-
-## [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)
-
-
-
-# [4.39.0](https://github.com/Ombi-app/Ombi/compare/v4.35.11...v4.39.0) (2023-05-17)
-
-
-### Bug Fixes
-
-* **emby:** Fix Emby played sync running a full sync during recently added sync ([#4932](https://github.com/Ombi-app/Ombi/issues/4932)) ([9424586](https://github.com/Ombi-app/Ombi/commit/9424586e9c1b622b6475aeb8ee3cf4a8f346da6e))
-
-
-### Features
-
-* Hide watched status when request is not available ([#4934](https://github.com/Ombi-app/Ombi/issues/4934)) ([82c7f1c](https://github.com/Ombi-app/Ombi/commit/82c7f1c44fd7c87d57cc2b0c34a10fcda7628f4e))
-
-
-
-## [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))
-
-
-
-## [4.35.18](https://github.com/Ombi-app/Ombi/compare/v4.35.17...v4.35.18) (2023-04-15)
-
-
-### Bug Fixes
-
-* **#4906:** :bug: Fixed an issue with power users and permissions ([80884bc](https://github.com/Ombi-app/Ombi/commit/80884bcd725c329867c278ad235cd4096cd4fe7a))
-
-
-
-## [4.35.17](https://github.com/Ombi-app/Ombi/compare/v4.35.16...v4.35.17) (2023-04-15)
-
-
-### Bug Fixes
-
-* **discover:** Fix denied requests displayed as approved ([#4901](https://github.com/Ombi-app/Ombi/issues/4901)) ([1e87f20](https://github.com/Ombi-app/Ombi/commit/1e87f2010491b0f3fdda70d2b19d9afd94438df7))
-* Fix denied movie shown as 'processing request' in details view ([#4900](https://github.com/Ombi-app/Ombi/issues/4900)) ([0069bfd](https://github.com/Ombi-app/Ombi/commit/0069bfdf54e0785bad45c832ca052f19fd4b940b))
-
-
-
-## [4.35.16](https://github.com/Ombi-app/Ombi/compare/v4.35.15...v4.35.16) (2023-04-13)
-
-
-### Bug Fixes
-
-* Support duplicates in Emby/JF collections ([#4902](https://github.com/Ombi-app/Ombi/issues/4902)) ([141f96d](https://github.com/Ombi-app/Ombi/commit/141f96da5e45d5b3fa5f496806b102e473da6607))
-
-
-
-## [4.35.15](https://github.com/Ombi-app/Ombi/compare/v4.35.14...v4.35.15) (2023-04-06)
-
-
-### Bug Fixes
-
-* **sonarr:** :bug: Stop the sonarr version endpoint from breaking when Sonarr is down [#4895](https://github.com/Ombi-app/Ombi/issues/4895) ([7bb8bec](https://github.com/Ombi-app/Ombi/commit/7bb8becfb140ef6012356752a71d53b5b404e482))
-
-
-
-## [4.35.14](https://github.com/Ombi-app/Ombi/compare/v4.35.13...v4.35.14) (2023-04-06)
-
-
-### Bug Fixes
-
-* Some minor tweaks to the movie info panel ([#4883](https://github.com/Ombi-app/Ombi/issues/4883)) ([1244487](https://github.com/Ombi-app/Ombi/commit/12444871df2f7602200f73971fce962f06b4a80b))
-
-
-
-## [4.35.13](https://github.com/Ombi-app/Ombi/compare/v4.35.12...v4.35.13) (2023-03-28)
-
-
-### Bug Fixes
-
-* **sonarr:** :bug: Added some more error handling and information around testing sonarr ([bd2c2d3](https://github.com/Ombi-app/Ombi/commit/bd2c2d3901e239393010fd582b207f1571fb4b7e)), closes [#4877](https://github.com/Ombi-app/Ombi/issues/4877)
-
-
-
-## [4.35.12](https://github.com/Ombi-app/Ombi/compare/v4.35.10...v4.35.12) (2023-03-25)
-
-
-### Bug Fixes
-
-* **sonarr:** :bug: Improved the error handling in the sonarr settings page in the UI ([fcd78fe](https://github.com/Ombi-app/Ombi/commit/fcd78fee619d10ec7d78e8c8ec6c3ac4b0a361a1)), closes [#4877](https://github.com/Ombi-app/Ombi/issues/4877)
-
-
-
-## [4.35.9](https://github.com/Ombi-app/Ombi/compare/v4.35.8...v4.35.9) (2023-02-24)
-
-
-
-## [4.35.8](https://github.com/Ombi-app/Ombi/compare/v4.35.7...v4.35.8) (2023-02-17)
-
-
-### Bug Fixes
-
-* **plex-oauth:** 🐛 Fixed an issue where using OAuth you could log in as a Ombi Local user [#4835](https://github.com/Ombi-app/Ombi/issues/4835) ([4098da3](https://github.com/Ombi-app/Ombi/commit/4098da305aaea9dae9a552644268a4fed7204cfe))
-
-
-
-## [4.35.7](https://github.com/Ombi-app/Ombi/compare/v4.35.6...v4.35.7) (2023-02-10)
-
-
-### Bug Fixes
-
-* **wizard:** :bug: Stop access to the wizard when you have already setup ombi ([#4866](https://github.com/Ombi-app/Ombi/issues/4866)) ([353de98](https://github.com/Ombi-app/Ombi/commit/353de981a462e1753288d225ec4644a44a62d2bc))
-
-
-
-## [4.35.6](https://github.com/Ombi-app/Ombi/compare/v4.35.5...v4.35.6) (2023-01-31)
-
-
-### Bug Fixes
-
-* Fixed the issue where the login page is still present after logging in with oauth ([aca4ee3](https://github.com/Ombi-app/Ombi/commit/aca4ee37915a28200e5233be3dc711ccc4a5aee9))
-
-
-
-## [4.35.5](https://github.com/Ombi-app/Ombi/compare/v4.35.4...v4.35.5) (2023-01-24)
-
-
-### Bug Fixes
-
-* **radarr-settings:** 🐛 Fixed a typo ([4a50a00](https://github.com/Ombi-app/Ombi/commit/4a50a00d4729d99f4359874b9af4dbc58a0c220b))
-
-
-
-## [4.35.4](https://github.com/Ombi-app/Ombi/compare/v4.35.3...v4.35.4) (2023-01-22)
-
-
-### Bug Fixes
-
-* **discover:** :bug: Fixed the default poster not taking into account the base url in some scenarios [#4845](https://github.com/Ombi-app/Ombi/issues/4845) ([8eda250](https://github.com/Ombi-app/Ombi/commit/8eda250367953183daec03ccb5cdf9fe94275b27))
-* **Hide music from navbar and request list when not enabled:** :bug: ([5123a76](https://github.com/Ombi-app/Ombi/commit/5123a76954e9f81d58c05e31afc7a29aec19cb7a))
-
-
-
-## [4.35.3](https://github.com/Ombi-app/Ombi/compare/v4.35.2...v4.35.3) (2023-01-13)
-
-
-### Bug Fixes
-
-* **#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](https://github.com/Ombi-app/Ombi/issues/4848)) ([f229d88](https://github.com/Ombi-app/Ombi/commit/f229d88bd744bc5253b5d3db69ae5ef22d014230))
-
-
-
-## [4.35.2](https://github.com/Ombi-app/Ombi/compare/v4.35.1...v4.35.2) (2023-01-08)
-
-
-### Bug Fixes
-
-* **database:** Just some tweaks, shouldn't notice any difference, maybe a less error in the log ([67fb992](https://github.com/Ombi-app/Ombi/commit/67fb9921c0c025025286eb6c0a9d09fd01b18465))
-
-
-
-## [4.35.1](https://github.com/Ombi-app/Ombi/compare/v4.35.0...v4.35.1) (2023-01-06)
-
-
-### Bug Fixes
-
-* **plex-watchlist:** Index out of bounds error ([8cd556e](https://github.com/Ombi-app/Ombi/commit/8cd556e268931596b9c1d1ae0ce533bfaaf330f4))
-
-
-
-# [4.35.0](https://github.com/Ombi-app/Ombi/compare/v4.34.1...v4.35.0) (2023-01-04)
-
-
-### Features
-
-* Add the option for header authentication to create users ([#4841](https://github.com/Ombi-app/Ombi/issues/4841)) ([e6c9ce5](https://github.com/Ombi-app/Ombi/commit/e6c9ce5ad0056608ecda8273fb8124ed292e2942))
-
-
-
-## [4.34.1](https://github.com/Ombi-app/Ombi/compare/v4.34.0...v4.34.1) (2023-01-04)
-
-
-### Bug Fixes
-
-* **plex-watchlist:** Lookup the ID from different sources when Plex doesn't contain the metadata ([#4843](https://github.com/Ombi-app/Ombi/issues/4843)) ([a2cc23b](https://github.com/Ombi-app/Ombi/commit/a2cc23b351c4a568c44e6c855f94db9f71ad084a))
-
-
-
-# [4.34.0](https://github.com/Ombi-app/Ombi/compare/v4.33.1...v4.34.0) (2023-01-04)
-
-
-### Features
-
-* Radarr tags ([#4815](https://github.com/Ombi-app/Ombi/issues/4815)) ([6fa5064](https://github.com/Ombi-app/Ombi/commit/6fa506491fe867cdeef9df79991ae49319d71c3d))
-
-
-
-## [4.33.1](https://github.com/Ombi-app/Ombi/compare/v4.33.0...v4.33.1) (2022-12-22)
-
-
-### Bug Fixes
-
-* **plex:** Added the watchlist request whole show back into the settings ([10701c4](https://github.com/Ombi-app/Ombi/commit/10701c4a0b6190eebb75c5d8b18224f3d0bc8502))
-
-
-
-# [4.33.0](https://github.com/Ombi-app/Ombi/compare/v4.32.3...v4.33.0) (2022-12-01)
-
-
-### Features
-
-* Angular 15 and Dependency upgrades ([#4818](https://github.com/Ombi-app/Ombi/issues/4818)) ([4816acf](https://github.com/Ombi-app/Ombi/commit/4816acf6f94443d23ebef6091d4cfcbca580f9ca))
-
-
-
-## [4.32.3](https://github.com/Ombi-app/Ombi/compare/v4.32.2...v4.32.3) (2022-11-24)
-
-
-### Bug Fixes
-
-* **sonarr:** V4 actually works this time around ([f62e70f](https://github.com/Ombi-app/Ombi/commit/f62e70fc493c7971da5e4508ce10522f5df0bbf7))
-
-
-
-## [4.32.2](https://github.com/Ombi-app/Ombi/compare/v4.32.1...v4.32.2) (2022-11-23)
-
-
-### Bug Fixes
-
-* **sonarr:** :bug: Sonarr V4 should work now ([#4810](https://github.com/Ombi-app/Ombi/issues/4810)) ([37655af](https://github.com/Ombi-app/Ombi/commit/37655aff9d3d133b42f5664bc9445d6571e966d6))
-
-
-
-## [4.32.1](https://github.com/Ombi-app/Ombi/compare/v4.32.0...v4.32.1) (2022-11-21)
-
-
-### Bug Fixes
-
-* **plex:** :bug: Fixed the issue where you couldn't add a new server on a fresh setup after the settings page rework ([187b18d](https://github.com/Ombi-app/Ombi/commit/187b18d5c01f6a13831e4a410b5d7c349e27d847))
-
-
-
-# [4.32.0](https://github.com/Ombi-app/Ombi/compare/v4.31.0...v4.32.0) (2022-11-18)
-
-
-### Bug Fixes
-
-* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4801](https://github.com/Ombi-app/Ombi/issues/4801)) ([4692003](https://github.com/Ombi-app/Ombi/commit/46920032baed04675b2ffbe1700afdc0740a4ac4))
-
-
-### Features
-
-* **plex:** Rework the Plex Settings page ([#4805](https://github.com/Ombi-app/Ombi/issues/4805)) ([1b8c47f](https://github.com/Ombi-app/Ombi/commit/1b8c47f3163f618851d4904732cb07015e1e93ff))
-
-
-
-# [4.31.0](https://github.com/Ombi-app/Ombi/compare/v4.30.0...v4.31.0) (2022-11-18)
-
-
-### Features
-
-* **sonarr:** Added the ability to add default tags when sending to Sonarr ([#4803](https://github.com/Ombi-app/Ombi/issues/4803)) ([ecfbb8e](https://github.com/Ombi-app/Ombi/commit/ecfbb8eda91e1a90239dcf8be847afcc2394a78e))
-
-
-
-# [4.30.0](https://github.com/Ombi-app/Ombi/compare/v4.29.3...v4.30.0) (2022-11-17)
-
-
-### Features
-
-* **sonarr:** :sparkles: Add the username to a Sonarr tag when sent to Sonarr ([#4802](https://github.com/Ombi-app/Ombi/issues/4802)) ([1d5fabd](https://github.com/Ombi-app/Ombi/commit/1d5fabd317e3ce8f6dd31f06d15dc81277f39dbd))
-
-
-
-## [4.29.3](https://github.com/Ombi-app/Ombi/compare/v4.29.2...v4.29.3) (2022-11-14)
-
-
-### Bug Fixes
-
-* **notifications:** Fixed the Partially TV notifications going to the admin [#4797](https://github.com/Ombi-app/Ombi/issues/4797) ([#4799](https://github.com/Ombi-app/Ombi/issues/4799)) ([bcb3e7f](https://github.com/Ombi-app/Ombi/commit/bcb3e7f00380a4c4278f59dc55febf43e6d05d47))
-* Only log error messages from Microsoft ([#4787](https://github.com/Ombi-app/Ombi/issues/4787)) ([c614e0c](https://github.com/Ombi-app/Ombi/commit/c614e0ca5fe5023cbe7ced326145273cd75be85d))
-
-
-
-## [4.29.2](https://github.com/Ombi-app/Ombi/compare/v4.29.1...v4.29.2) (2022-10-24)
-
-
-### Bug Fixes
-
-* **plex:** Fixed an issue where sometimes the availability checker would throw an exception when checking episodes ([17ba202](https://github.com/Ombi-app/Ombi/commit/17ba2020ee0950c2c0e0e03fdb7835b579da75a9))
-
-
-
-## [4.29.1](https://github.com/Ombi-app/Ombi/compare/v4.29.0...v4.29.1) (2022-10-22)
-
-
-### Bug Fixes
-
-* Consistently reset loading flag when requesting movies on discover page. ([#4777](https://github.com/Ombi-app/Ombi/issues/4777)) ([a40ab5c](https://github.com/Ombi-app/Ombi/commit/a40ab5cddf769d4147696eca50c1610b466ab99b))
-* **sonarr:** :bug: Fixed an issue where the language list didn't correctly load for power users in the advanced options [#4782](https://github.com/Ombi-app/Ombi/issues/4782) ([2173670](https://github.com/Ombi-app/Ombi/commit/217367047d1568070dd507e54ad3fd2c68f05b88))
-
-
-
-# [4.29.0](https://github.com/Ombi-app/Ombi/compare/v4.28.1...v4.29.0) (2022-10-19)
-
-
-### Bug Fixes
-
-* Partially Available prevents further TV requests ([#4768](https://github.com/Ombi-app/Ombi/issues/4768)) ([#4779](https://github.com/Ombi-app/Ombi/issues/4779)) ([031e2b9](https://github.com/Ombi-app/Ombi/commit/031e2b9283b239827cabaca4e35f69f2f93a4d7b))
-* Unable to Delete Jellyfin Server ([#4705](https://github.com/Ombi-app/Ombi/issues/4705)) ([#4780](https://github.com/Ombi-app/Ombi/issues/4780)) ([76a0d0d](https://github.com/Ombi-app/Ombi/commit/76a0d0d26893bd480fea4735f77522ac6261a425))
-
-
-### Features
-
-* Provide a flag for missing users on Plex Server ([#4688](https://github.com/Ombi-app/Ombi/issues/4688)) ([#4778](https://github.com/Ombi-app/Ombi/issues/4778)) ([b4a14c2](https://github.com/Ombi-app/Ombi/commit/b4a14c2d28218409390e517b226130e3e84efee1))
-
-
-
-## [4.28.1](https://github.com/Ombi-app/Ombi/compare/v4.28.0...v4.28.1) (2022-10-19)
-
-
-### Bug Fixes
-
-* **plex:** :bug: Fixed not being able to enable watchlist requests in the Plex settings ([3e5158e](https://github.com/Ombi-app/Ombi/commit/3e5158ef9cda58ea2dd3be143f07aa5433691d79))
-* Reworked the version check ([#4719](https://github.com/Ombi-app/Ombi/issues/4719)) ([#4781](https://github.com/Ombi-app/Ombi/issues/4781)) ([55855c5](https://github.com/Ombi-app/Ombi/commit/55855c5adda3cd1c51b7fbd0c19b469fc813f98e))
-
-
-
-# [4.28.0](https://github.com/Ombi-app/Ombi/compare/v4.27.8...v4.28.0) (2022-10-07)
-
-
-### Features
-
-* **plex:** ✨ Added the ability to configure the watchlist to request the whole TV show rather than latest season ([#4774](https://github.com/Ombi-app/Ombi/issues/4774)) ([fa65712](https://github.com/Ombi-app/Ombi/commit/fa65712bd570fe8d5d21b8ca0abe182b84960017))
-
-
-
-## [4.27.8](https://github.com/Ombi-app/Ombi/compare/v4.27.7...v4.27.8) (2022-10-07)
-
-
-
-## [4.27.7](https://github.com/Ombi-app/Ombi/compare/v4.27.6...v4.27.7) (2022-10-07)
-
-
-### Bug Fixes
-
-* Fixes default image for recently requested items. ([#4767](https://github.com/Ombi-app/Ombi/issues/4767)) ([2e6f35f](https://github.com/Ombi-app/Ombi/commit/2e6f35f89abb3dd3685ec8289f8620c7ef7072cd))
-
-
-
-## [4.27.6](https://github.com/Ombi-app/Ombi/compare/v4.27.5...v4.27.6) (2022-10-01)
-
-
-### Bug Fixes
-
-* **notifications:** Fixed the error when sending multiple test notifications. Added more logging when Discord complains the message is invalid ([fc14780](https://github.com/Ombi-app/Ombi/commit/fc14780bd354483119ddcbb55a8c382e1890a783))
-
-
-
-## [4.27.5](https://github.com/Ombi-app/Ombi/compare/v4.27.4...v4.27.5) (2022-09-30)
-
-
-### Bug Fixes
-
-* **importer:** 🐛 Allow you to only import Plex Admins without the Plex Users ([8c9ad9b](https://github.com/Ombi-app/Ombi/commit/8c9ad9b414fdc6c88bdb911d6057ae5d38783b98))
-
-
-
-## [4.27.4](https://github.com/Ombi-app/Ombi/compare/v4.27.3...v4.27.4) (2022-09-30)
-
-
-
-## [4.27.3](https://github.com/Ombi-app/Ombi/compare/v4.27.2...v4.27.3) (2022-09-30)
-
-
-### Bug Fixes
-
-* **availability:** 🐛 Fixed a issue with the availability checker after the previous update. Added full test coverage around that area ([28e2480](https://github.com/Ombi-app/Ombi/commit/28e248046ad56390595f84172bbd5f5961325b4d))
-
-
-
-## [4.27.2](https://github.com/Ombi-app/Ombi/compare/v4.27.1...v4.27.2) (2022-09-29)
-
-
-### Bug Fixes
-
-* **sonarr:** :bug: 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](https://github.com/Ombi-app/Ombi/issues/4764)) ([2cddec7](https://github.com/Ombi-app/Ombi/commit/2cddec759004b6490f686ff74cb092238e3dc946))
-
-
-
-## [4.27.1](https://github.com/Ombi-app/Ombi/compare/v4.27.0...v4.27.1) (2022-09-20)
-
-
-### Bug Fixes
-
-* **plex:** stop the plex sync from deleting episodes when we can't find the plex key ([66b05e5](https://github.com/Ombi-app/Ombi/commit/66b05e5a85dbfe1fec5f9366e80987f2cfa1f4fe))
-
-
-
-# [4.27.0](https://github.com/Ombi-app/Ombi/compare/v4.26.0...v4.27.0) (2022-09-14)
-
-
-### Features
-
-* Recently requested improvements ([#4755](https://github.com/Ombi-app/Ombi/issues/4755)) ([ff04d87](https://github.com/Ombi-app/Ombi/commit/ff04d875343604c77c391bf55d0968977e480281))
-
-
-
-# [4.26.0](https://github.com/Ombi-app/Ombi/compare/v4.25.1...v4.26.0) (2022-09-07)
-
-
-### Features
-
-* **notifications:** Add more curly variables for partially available notification ([66aa101](https://github.com/Ombi-app/Ombi/commit/66aa101019c4c4b34e186db9d303049d02b9c781))
-
-
-
-## [4.25.1](https://github.com/Ombi-app/Ombi/compare/v4.25.0...v4.25.1) (2022-09-07)
-
-
-### Bug Fixes
-
-* **webhook:** Remove added trailing slash from webhook URL [#4710](https://github.com/Ombi-app/Ombi/issues/4710) ([369eb33](https://github.com/Ombi-app/Ombi/commit/369eb339171671101be219486e2aab27a20f3d74))
-
-
-
-# [4.25.0](https://github.com/Ombi-app/Ombi/compare/v4.24.0...v4.25.0) (2022-08-23)
-
-
-### Bug Fixes
-
-* fixed stats controller ([#4742](https://github.com/Ombi-app/Ombi/issues/4742)) ([47ea64b](https://github.com/Ombi-app/Ombi/commit/47ea64b5a401770f1943b575ca40f84d515e96b3))
-
-
-### Features
-
-* Watchlist history errors([#4741](https://github.com/Ombi-app/Ombi/issues/4741)) ([c222f1a](https://github.com/Ombi-app/Ombi/commit/c222f1a945e944ef34e68cad2b61f40e57cab823))
-
-
-
-# [4.24.0](https://github.com/Ombi-app/Ombi/compare/v4.23.2...v4.24.0) (2022-08-22)
-
-
-### Features
-
-* add crew on movie page ([#4722](https://github.com/Ombi-app/Ombi/issues/4722)) ([1d53261](https://github.com/Ombi-app/Ombi/commit/1d532613823804b25984bd1d223d081a54ad143d))
-
-
-
-## [4.23.2](https://github.com/Ombi-app/Ombi/compare/v4.23.1...v4.23.2) (2022-08-22)
-
-
-### Bug Fixes
-
-* Fix conflicting property name for Swagger ([#4733](https://github.com/Ombi-app/Ombi/issues/4733)) ([d661f32](https://github.com/Ombi-app/Ombi/commit/d661f32e8a9e105faab6380b4b7b642896b98163))
-
-
-
-## [4.23.1](https://github.com/Ombi-app/Ombi/compare/v4.23.0...v4.23.1) (2022-08-12)
-
-
-### Bug Fixes
-
-* Localize recently requested on discover page ([#4729](https://github.com/Ombi-app/Ombi/issues/4729)) ([bf65c76](https://github.com/Ombi-app/Ombi/commit/bf65c76ff9ce38f65a9e5feb872734e8d8e35eb6))
-
-
-
-# [4.23.0](https://github.com/Ombi-app/Ombi/compare/v4.22.5...v4.23.0) (2022-08-09)
-
-
-### Bug Fixes
-
-* Log Microsoft warnings to log file ([#4723](https://github.com/Ombi-app/Ombi/issues/4723)) ([26ac75f](https://github.com/Ombi-app/Ombi/commit/26ac75f0c223c2a91e3471797ae46ede3fde89cc))
-
-
-### Features
-
-* ✨ Recently Requested on Discover Page ([#4387](https://github.com/Ombi-app/Ombi/issues/4387)) ([44d38fb](https://github.com/Ombi-app/Ombi/commit/44d38fbaae521dbb467b61c7471b2384015ac52e))
-
-
-
-## [4.22.4](https://github.com/Ombi-app/Ombi/compare/v4.22.3...v4.22.4) (2022-08-04)
-
-
-### Bug Fixes
-
-* :bug: Fixed missing externals ([#4712](https://github.com/Ombi-app/Ombi/issues/4712)) ([fcc1eaa](https://github.com/Ombi-app/Ombi/commit/fcc1eaaa377683dcdc81d62a2a688fb0c4490c7b))
-* fixed trakt image not loading when base url present ([#4711](https://github.com/Ombi-app/Ombi/issues/4711)) ([f102dcf](https://github.com/Ombi-app/Ombi/commit/f102dcf751c2eb62ebfe30f9f8e4b2ad863c3b0d))
-* **translations:** 🌐 New translations from Crowdin [skip ci] ([#4713](https://github.com/Ombi-app/Ombi/issues/4713)) ([ff142b0](https://github.com/Ombi-app/Ombi/commit/ff142b09abbb2f9540387284222552e6e12639fe))
-
-
-
-## [4.22.3](https://github.com/Ombi-app/Ombi/compare/v4.22.2...v4.22.3) (2022-07-28)
-
-
-### Bug Fixes
-
-* Override Sonarr V3 Profiles endpoint ([#4678](https://github.com/Ombi-app/Ombi/issues/4678)) ([875da95](https://github.com/Ombi-app/Ombi/commit/875da959f353119b05138d68ee6d32a49e14b91e))
-
-
-
-## [4.22.2](https://github.com/Ombi-app/Ombi/compare/v4.22.1...v4.22.2) (2022-07-25)
-
-
-### Bug Fixes
-
-* fixed an issue where I broke images for some users ([81ddc85](https://github.com/Ombi-app/Ombi/commit/81ddc8553b9094c3f6843b036daebb2eb9262e00))
-
-
-
-## [4.22.1](https://github.com/Ombi-app/Ombi/compare/v4.22.0...v4.22.1) (2022-07-25)
-
-
-### Bug Fixes
-
-* **discover:** :bug: Created new Image component to handle 429's from TMDB ([#4698](https://github.com/Ombi-app/Ombi/issues/4698)) and fixed [#4635](https://github.com/Ombi-app/Ombi/issues/4635) ([#4699](https://github.com/Ombi-app/Ombi/issues/4699)) ([f22d3da](https://github.com/Ombi-app/Ombi/commit/f22d3da765799365455b919027f7563e52b347c3))
-
-
-
-# [4.22.0](https://github.com/Ombi-app/Ombi/compare/v4.21.2...v4.22.0) (2022-07-22)
-
-
-### Features
-
-* **discover:** ✨ Added infinite scroll on advanced search results ([898bc89](https://github.com/Ombi-app/Ombi/commit/898bc89fa78245c1f3de9481f6c724f087a16e39))
-
-
-
-## [4.21.2](https://github.com/Ombi-app/Ombi/compare/v4.21.1...v4.21.2) (2022-07-22)
-
-
-### Bug Fixes
-
-* Landing and Login page improvements ([#4690](https://github.com/Ombi-app/Ombi/issues/4690)) ([6d423b5](https://github.com/Ombi-app/Ombi/commit/6d423b5447c52c5e59d8d2bd92a23b47468eb736))
-
-
-
-## [4.21.1](https://github.com/Ombi-app/Ombi/compare/v4.21.0...v4.21.1) (2022-07-11)
-
-
-### Bug Fixes
-
-* **images:** Retry images with a backoff when we get a Too Many requests from TheMovieDb [#4685](https://github.com/Ombi-app/Ombi/issues/4685) ([3f1f35d](https://github.com/Ombi-app/Ombi/commit/3f1f35df3164db6739691cdda8f925c296239791))
-
-
-
-# [4.21.0](https://github.com/Ombi-app/Ombi/compare/v4.20.4...v4.21.0) (2022-06-22)
-
-
-### Features
-
-* Upgrade to Angular14 ([#4668](https://github.com/Ombi-app/Ombi/issues/4668)) ([b9d55a4](https://github.com/Ombi-app/Ombi/commit/b9d55a469b412558cbf67c1e25db7fdda5964cd8))
-
-
-### Performance Improvements
-
-* stop populating obsolete subscribe fields ([#4625](https://github.com/Ombi-app/Ombi/issues/4625)) ([9a73463](https://github.com/Ombi-app/Ombi/commit/9a734637665f671b17c2bb440d93b35a891c142b))
-
-
-
-## [4.20.4](https://github.com/Ombi-app/Ombi/compare/v4.20.3...v4.20.4) (2022-06-15)
-
-
-### Bug Fixes
-
-* fixed build ([f877921](https://github.com/Ombi-app/Ombi/commit/f8779219146051ea74f8b6408658ff7975afb88b))
-
-
-
-## [4.20.3](https://github.com/Ombi-app/Ombi/compare/v4.20.2...v4.20.3) (2022-06-05)
-
-
-### Bug Fixes
-
-* **plex:** 🐛 Fixed an issue with the Plex Sync ([ab1a11a](https://github.com/Ombi-app/Ombi/commit/ab1a11af78efbe9d37bd55aa80a640796c138a98))
-
-
-
-## [4.20.2](https://github.com/Ombi-app/Ombi/compare/v4.20.1...v4.20.2) (2022-06-03)
-
-
-### Bug Fixes
-
-* :bug: Fixed the Request on Behalf of having blanks ([#4667](https://github.com/Ombi-app/Ombi/issues/4667)) ([7dd9b1c](https://github.com/Ombi-app/Ombi/commit/7dd9b1cac07f571dd35b362544e4fe0226c4b817))
-
-
-
-## [4.20.1](https://github.com/Ombi-app/Ombi/compare/v4.20.0...v4.20.1) (2022-05-27)
-
-
-### Bug Fixes
-
-* added media type tag to media type text ([#4638](https://github.com/Ombi-app/Ombi/issues/4638)) ([fe501d3](https://github.com/Ombi-app/Ombi/commit/fe501d34a0c36ac9f000b107eca49dbc6694d006))
-* **API:** Fix pagination in some edge cases ([#4649](https://github.com/Ombi-app/Ombi/issues/4649)) ([a70bf8f](https://github.com/Ombi-app/Ombi/commit/a70bf8f46c76d74c9dfdf908c53bd9955ca0a35d))
-* **discover:** Carousel touch not working when scrolling page and recommendations and similar movie navigation ([#4633](https://github.com/Ombi-app/Ombi/issues/4633)) ([d5ef1d5](https://github.com/Ombi-app/Ombi/commit/d5ef1d53e5f77d19dba8b8059c80b538a3e14f2a))
-* Improve Swagger documentation ([#4652](https://github.com/Ombi-app/Ombi/issues/4652)) ([181892b](https://github.com/Ombi-app/Ombi/commit/181892bcfe88e6d76febf49ef57745d04552d08e))
-* Missing Poster broken link fix ([#4637](https://github.com/Ombi-app/Ombi/issues/4637)) ([4070f0d](https://github.com/Ombi-app/Ombi/commit/4070f0d093b1c92487a1c80cabad8283a9650f51))
-* **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](https://github.com/Ombi-app/Ombi/issues/4648)) ([6d16442](https://github.com/Ombi-app/Ombi/commit/6d16442d4d714920367df065a3ced42b729f4233))
-* **sync:** Emby+Jellyfin - sync multi-episode files of 3+ episodes ([bd8fd89](https://github.com/Ombi-app/Ombi/commit/bd8fd890554c9d85d6da4d2cee813e82ce698e52))
-
-
-
-# [4.20.0](https://github.com/Ombi-app/Ombi/compare/v4.19.1...v4.20.0) (2022-04-28)
-
-
-### Features
-
-* **discover:** Show more relevant shows in upcoming TV ([8357819](https://github.com/Ombi-app/Ombi/commit/8357819b53b8c675c0b246d7006b5a778bdba33f))
-
-
-
-## [4.19.1](https://github.com/Ombi-app/Ombi/compare/v4.19.0...v4.19.1) (2022-04-27)
-
-
-
-# [4.19.0](https://github.com/Ombi-app/Ombi/compare/v4.18.0...v4.19.0) (2022-04-27)
-
-
-### Features
-
-* **sync:** Detect reidentified movies in Emby and Jellyfin ([5938077](https://github.com/Ombi-app/Ombi/commit/5938077d82a5357f79c07b218b3986557a5816e8))
-* **sync:** Detect reidentified series in Emby and Jellyfin ([9096e91](https://github.com/Ombi-app/Ombi/commit/9096e91d55d268819bce22831f8a8b27f2a1776b))
-
-
-
-# [4.18.0](https://github.com/Ombi-app/Ombi/compare/v4.17.0...v4.18.0) (2022-04-26)
-
-
-### Bug Fixes
-
-* **discover:** Fix cache mix up ([03d9422](https://github.com/Ombi-app/Ombi/commit/03d94220c7eaafb50c6c80a6ed1150794b873ac3))
-* **discover:** Fix new trending feature detection ([6794b88](https://github.com/Ombi-app/Ombi/commit/6794b887f6544fb41528bdd9728b7824b65e47ee))
-* **settings:** Allow toggling features when there are more than one ([a373359](https://github.com/Ombi-app/Ombi/commit/a373359ae8e6bad42b558a6e01a8ff2840d3bbaa))
-
-
-### Features
-
-* **discover:** Add new trending source experimental feature ([1a0823c](https://github.com/Ombi-app/Ombi/commit/1a0823ca80559417c67323aaeaa1ef5243e98031))
-* **discover:** Default trending source to new logic ([4f12939](https://github.com/Ombi-app/Ombi/commit/4f12939e22020a67a5ee75e2907923faea136e8d))
-
-
-
-# [4.17.0](https://github.com/Ombi-app/Ombi/compare/v4.16.17...v4.17.0) (2022-04-25)
-
-
-
-## [4.16.17](https://github.com/Ombi-app/Ombi/compare/v4.16.16...v4.16.17) (2022-04-25)
-
-
-
-## [4.16.16](https://github.com/Ombi-app/Ombi/compare/v4.16.15...v4.16.16) (2022-04-25)
-
-
-### Bug Fixes
-
-* **4616:** :bug: fixed mandatory fields ([d8f2260](https://github.com/Ombi-app/Ombi/commit/d8f2260c7ae3ed48386743b7adbd06e284487034))
-
-
-
-## [4.16.15](https://github.com/Ombi-app/Ombi/compare/v4.16.14...v4.16.15) (2022-04-24)
-
-
-### Features
-
-* **discover:** Add original language filter ([ef7ec86](https://github.com/Ombi-app/Ombi/commit/ef7ec861d8aede2a4817752c990617f583805391))
-
-
-
-## [4.16.14](https://github.com/Ombi-app/Ombi/compare/v4.16.13...v4.16.14) (2022-04-19)
-
-
-
-## [4.16.13](https://github.com/Ombi-app/Ombi/compare/v4.16.12...v4.16.13) (2022-04-19)
-
-
-
diff --git a/README.md b/README.md
index 8091207a6..8fb37b8f8 100644
--- a/README.md
+++ b/README.md
@@ -122,10 +122,10 @@ Here are some of the features Ombi has:
-
-
+
+
- Matt Jeanes
+ Amy Jeanes
@@ -752,8 +752,8 @@ Here are some of the features Ombi has:
-
-
+
+
Aljosa Asanovic
diff --git a/src/.idea/.idea.Ombi/.idea/contentModel.xml b/src/.idea/.idea.Ombi/.idea/contentModel.xml
deleted file mode 100644
index ffc2d3ce6..000000000
--- a/src/.idea/.idea.Ombi/.idea/contentModel.xml
+++ /dev/null
@@ -1,2309 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/.idea/.idea.Ombi/.idea/dbnavigator.xml b/src/.idea/.idea.Ombi/.idea/dbnavigator.xml
new file mode 100644
index 000000000..4e210d20a
--- /dev/null
+++ b/src/.idea/.idea.Ombi/.idea/dbnavigator.xml
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Ombi/.idea/indexLayout.xml b/src/.idea/.idea.Ombi/.idea/indexLayout.xml
index 27ba142e9..7b08163ce 100644
--- a/src/.idea/.idea.Ombi/.idea/indexLayout.xml
+++ b/src/.idea/.idea.Ombi/.idea/indexLayout.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/src/.idea/.idea.Ombi/.idea/modules.xml b/src/.idea/.idea.Ombi/.idea/modules.xml
deleted file mode 100644
index 4d6d71637..000000000
--- a/src/.idea/.idea.Ombi/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/.idea/.idea.Ombi/.idea/projectSettingsUpdater.xml b/src/.idea/.idea.Ombi/.idea/projectSettingsUpdater.xml
new file mode 100644
index 000000000..64af657f5
--- /dev/null
+++ b/src/.idea/.idea.Ombi/.idea/projectSettingsUpdater.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.idea/.idea.Ombi/.idea/workspace.xml b/src/.idea/.idea.Ombi/.idea/workspace.xml
index 30951a63b..d008ba9a7 100644
--- a/src/.idea/.idea.Ombi/.idea/workspace.xml
+++ b/src/.idea/.idea.Ombi/.idea/workspace.xml
@@ -1,25 +1,20 @@
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
@@ -237,27 +232,75 @@
+ {
+ "lastFilter": {
+ "state": "OPEN",
+ "assignee": "tidusjar"
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "selectedUrlAndAccountId": {
+ "url": "https://github.com/ombi-app/ombi",
+ "accountId": "22dd09fe-fb9e-48a4-bfcc-3c152edf3f25"
+ }
+}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
@@ -275,12 +318,17 @@
+ {
+ "customColor": "",
+ "associatedIndex": 0
+}
+
@@ -343,27 +391,26 @@
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -376,7 +423,7 @@
-
+
@@ -391,7 +438,7 @@
-
+
@@ -406,7 +453,7 @@
-
+
@@ -421,7 +468,7 @@
-
+
@@ -436,10 +483,11 @@
-
+
+
@@ -448,6 +496,9 @@
1563957157468
+
+
+
@@ -493,7 +544,11 @@
-
+
+
+
+
+
@@ -505,7 +560,7 @@
file://$PROJECT_DIR$/Ombi/Controllers/V1/TokenController.cs
48
-
+
@@ -518,12 +573,12 @@
file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs
59
-
+
-
+
-
+
@@ -531,12 +586,12 @@
file://$PROJECT_DIR$/Ombi.Core/Engine/V2/MultiSearchEngine.cs
49
-
+
-
+
-
+
@@ -544,16 +599,55 @@
file://$PROJECT_DIR$/Ombi.Api.MusicBrainz/MusicBrainzApi.cs
30
-
+
-
+
-
+
+
+ file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
+ 110
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
+ 77
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
+ 100
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.idea/.idea.Ombi/riderModule.iml b/src/.idea/.idea.Ombi/riderModule.iml
deleted file mode 100644
index d716cec6d..000000000
--- a/src/.idea/.idea.Ombi/riderModule.iml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/.vscode/tasks.json b/src/.vscode/tasks.json
index ad868d2dc..ad83d25c8 100644
--- a/src/.vscode/tasks.json
+++ b/src/.vscode/tasks.json
@@ -1,22 +1,31 @@
{
- "version": "0.1.0",
+ "version": "2.0.0",
"command": "dotnet",
- "isShellCommand": true,
"args": [],
"tasks": [
{
- "taskName": "build",
+ "label": "build",
+ "type": "shell",
+ "command": "dotnet",
"args": [
+ "build",
"${workspaceRoot}/Ombi/Ombi.csproj"
],
- "isBuildCommand": true,
- "problemMatcher": "$msCompile"
+ "problemMatcher": "$msCompile",
+ "group": {
+ "_id": "build",
+ "isDefault": false
+ }
},
{
- "taskName": "lint",
+ "label": "lint",
+ "type": "shell",
"command": "npm",
- "isShellCommand": true,
- "args": ["run", "lint"]
+ "args": [
+ "run",
+ "lint"
+ ],
+ "problemMatcher": []
}
]
}
\ No newline at end of file
diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs
index 6632da875..be6a61c16 100644
--- a/src/Ombi.Api.Plex/IPlexApi.cs
+++ b/src/Ombi.Api.Plex/IPlexApi.cs
@@ -29,5 +29,6 @@ namespace Ombi.Api.Plex
Task AddUser(string emailAddress, string serverId, string authToken, int[] libs);
Task GetWatchlist(string plexToken, CancellationToken cancellationToken);
Task GetWatchlistMetadata(string ratingKey, string plexToken, CancellationToken cancellationToken);
+ Task Ping(string authToken, CancellationToken cancellationToken = default);
}
}
\ No newline at end of file
diff --git a/src/Ombi.Api.Plex/Models/Friends/PlexFriends.cs b/src/Ombi.Api.Plex/Models/Friends/PlexFriends.cs
index 1e597d916..c0442da03 100644
--- a/src/Ombi.Api.Plex/Models/Friends/PlexFriends.cs
+++ b/src/Ombi.Api.Plex/Models/Friends/PlexFriends.cs
@@ -22,6 +22,18 @@ namespace Ombi.Api.Plex.Models.Friends
///
[XmlAttribute(AttributeName = "home")]
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")]
diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs
index cc0d13aaa..8babba05d 100644
--- a/src/Ombi.Api.Plex/PlexApi.cs
+++ b/src/Ombi.Api.Plex/PlexApi.cs
@@ -68,7 +68,7 @@ namespace Ombi.Api.Plex
private const string FriendsUri = "https://plex.tv/api/users";
private const string GetAccountUri = "https://plex.tv/users/account.json";
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/";
///
/// Sign into the Plex API
@@ -320,6 +320,30 @@ namespace Ombi.Api.Plex
return result;
}
+ ///
+ /// Pings the Plex API to validate if a token is still valid
+ ///
+ /// The authentication token to validate
+ /// Cancellation token
+ /// True if the token is valid, false otherwise
+ public async Task 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;
+ }
+ }
+
///
/// Adds the required headers and also the authorization header
diff --git a/src/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs b/src/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs
new file mode 100644
index 000000000..d29da4a5a
--- /dev/null
+++ b/src/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs
@@ -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 KeepTokenAliveAsync(string token, CancellationToken cancellationToken);
+ }
+
+ public class PlexTokenKeepAliveService : IPlexTokenKeepAliveService
+ {
+ private readonly IPlexApi _plexApi;
+ private readonly ILogger _logger;
+
+ public PlexTokenKeepAliveService(IPlexApi plexApi, ILogger logger)
+ {
+ _plexApi = plexApi;
+ _logger = logger;
+ }
+
+ public async Task 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs
index 4d3cd2cf9..82a6bce21 100644
--- a/src/Ombi.Core/Engine/MovieRequestEngine.cs
+++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs
@@ -598,13 +598,13 @@ namespace Ombi.Core.Engine
public async Task 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);
}
public async Task 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)
{
return new RequestEngineResult
@@ -790,7 +790,7 @@ namespace Ombi.Core.Engine
public async Task 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)
{
return new RequestEngineResult
@@ -805,7 +805,7 @@ namespace Ombi.Core.Engine
public async Task MarkUnavailable(int modelId, bool is4K)
{
- var request = await MovieRepository.Find(modelId);
+ var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null)
{
return new RequestEngineResult
@@ -834,7 +834,7 @@ namespace Ombi.Core.Engine
public async Task MarkAvailable(int modelId, bool is4K)
{
- var request = await MovieRepository.Find(modelId);
+ var request = await MovieRepository.GetWithUser().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null)
{
return new RequestEngineResult
diff --git a/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs b/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
new file mode 100644
index 000000000..2f1933184
--- /dev/null
+++ b/src/Ombi.Core/Helpers/DatabaseConfigurationSetup.cs
@@ -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();
+ }
+
+ 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 => 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());
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Helpers/FileSystem.cs b/src/Ombi.Core/Helpers/FileSystem.cs
new file mode 100644
index 000000000..97b9da0bf
--- /dev/null
+++ b/src/Ombi.Core/Helpers/FileSystem.cs
@@ -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
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Helpers/IFileSystem.cs b/src/Ombi.Core/Helpers/IFileSystem.cs
new file mode 100644
index 000000000..da2c9bba5
--- /dev/null
+++ b/src/Ombi.Core/Helpers/IFileSystem.cs
@@ -0,0 +1,7 @@
+namespace Ombi.Core.Helpers;
+
+public interface IFileSystem
+{
+ bool FileExists(string path);
+ // Add other file system operations as needed
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Models/DatabaseConfiguration.cs b/src/Ombi.Core/Models/DatabaseConfiguration.cs
new file mode 100644
index 000000000..550800108
--- /dev/null
+++ b/src/Ombi.Core/Models/DatabaseConfiguration.cs
@@ -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; }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs
index 26da3465c..a7ae4a6e7 100644
--- a/src/Ombi.Core/Senders/MovieSender.cs
+++ b/src/Ombi.Core/Senders/MovieSender.cs
@@ -182,7 +182,10 @@ namespace Ombi.Core.Senders
if (settings.SendUserTags)
{
var userTag = await GetOrCreateTag(model, settings);
- tags.Add(userTag.id);
+ if (userTag != null)
+ {
+ tags.Add(userTag.id);
+ }
}
// Overrides on the request take priority
@@ -198,7 +201,9 @@ namespace Ombi.Core.Senders
List movies;
// 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);
if (existingMovie == null)
@@ -246,6 +251,12 @@ namespace Ombi.Core.Senders
private async Task 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;
// Does tag exist?
diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs
index 247ce8b6e..1928ecea9 100644
--- a/src/Ombi.Core/Senders/TvSender.cs
+++ b/src/Ombi.Core/Senders/TvSender.cs
@@ -133,7 +133,14 @@ namespace Ombi.Core.Senders
string seriesType;
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);
+ if (profiles != null)
+ {
+ Logger.LogInformation("Found user quality profile for user {UserId}", model.RequestedUserId);
+ }
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
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.
}
+ Logger.LogInformation("Using anime path ID: {AnimePath}", animePath);
rootFolderPath = await GetSonarrRootPath(animePath, s);
languageProfileId = s.LanguageProfileAnime > 0 ? s.LanguageProfileAnime : s.LanguageProfile;
@@ -154,6 +163,7 @@ namespace Ombi.Core.Senders
{
if (profiles.SonarrRootPathAnime > 0)
{
+ Logger.LogInformation("Using user's anime root path override: {RootPath}", profiles.SonarrRootPathAnime);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPathAnime, s);
}
if (profiles.SonarrQualityProfileAnime > 0)
@@ -169,11 +179,13 @@ namespace Ombi.Core.Senders
int.TryParse(s.QualityProfile, out qualityToUse);
// Get the root path from the rootfolder selected.
// 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);
if (profiles != null)
{
if (profiles.SonarrRootPath > 0)
{
+ Logger.LogInformation("Using user's standard root path override: {RootPath}", profiles.SonarrRootPath);
rootFolderPath = await GetSonarrRootPath(profiles.SonarrRootPath, s);
}
if (profiles.SonarrQualityProfile > 0)
@@ -193,6 +205,7 @@ namespace Ombi.Core.Senders
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);
}
@@ -201,6 +214,8 @@ namespace Ombi.Core.Senders
languageProfileId = model.ParentRequest.LanguageProfile.Value;
}
+ Logger.LogInformation("Final root folder path: {RootFolderPath}", rootFolderPath);
+
try
{
if (tagToUse.HasValue)
@@ -520,17 +535,36 @@ namespace Ombi.Core.Senders
private async Task GetSonarrRootPath(int pathId, SonarrSettings sonarrSettings)
{
+ Logger.LogInformation("Getting Sonarr root path for ID: {PathId}", pathId);
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)
{
- 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;
}
}
diff --git a/src/Ombi.Core/Services/DatabaseConfigurationService.cs b/src/Ombi.Core/Services/DatabaseConfigurationService.cs
new file mode 100644
index 000000000..750499b19
--- /dev/null
+++ b/src/Ombi.Core/Services/DatabaseConfigurationService.cs
@@ -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 logger,
+ IFileSystem fileSystem)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ }
+
+ public async Task 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;
+ }
+}
\ No newline at end of file
diff --git a/src/Ombi.Core/Services/IDatabaseConfigurationService.cs b/src/Ombi.Core/Services/IDatabaseConfigurationService.cs
new file mode 100644
index 000000000..3530bf913
--- /dev/null
+++ b/src/Ombi.Core/Services/IDatabaseConfigurationService.cs
@@ -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 ConfigureDatabase(string databaseType, string connectionString, CancellationToken token);
+}
\ No newline at end of file
diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs
index 8a5509963..027717bbe 100644
--- a/src/Ombi.DependencyInjection/IocExtensions.cs
+++ b/src/Ombi.DependencyInjection/IocExtensions.cs
@@ -107,6 +107,7 @@ namespace Ombi.DependencyInjection
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
@@ -236,6 +237,8 @@ namespace Ombi.DependencyInjection
services.AddScoped();
services.AddTransient();
services.AddTransient();
+ services.AddSingleton();
+ services.AddSingleton();
}
public static void RegisterJobs(this IServiceCollection services)
diff --git a/src/Ombi.Helpers/NotificationType.cs b/src/Ombi.Helpers/NotificationType.cs
index bd6bd6f87..274813531 100644
--- a/src/Ombi.Helpers/NotificationType.cs
+++ b/src/Ombi.Helpers/NotificationType.cs
@@ -14,6 +14,7 @@
IssueResolved = 9,
IssueComment = 10,
Newsletter = 11,
- PartiallyAvailable = 12
+ PartiallyAvailable = 12,
+ PlexWatchlistTokenExpired = 13
}
}
diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj
index 97c999497..0ca27a075 100644
--- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj
+++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj
@@ -2,6 +2,9 @@
net8.0
+
+ false
+
Debug;Release;NonUiBuild
@@ -13,7 +16,7 @@
-
+
diff --git a/src/Ombi.Schedule.Tests/PlexUserImporterTests.cs b/src/Ombi.Schedule.Tests/PlexUserImporterTests.cs
index 022190bd2..f03d0e7e8 100644
--- a/src/Ombi.Schedule.Tests/PlexUserImporterTests.cs
+++ b/src/Ombi.Schedule.Tests/PlexUserImporterTests.cs
@@ -182,8 +182,6 @@ namespace Ombi.Schedule.Tests
_mocker.Verify(x => x.UpdateAsync(It.IsAny()), Times.Never);
}
-
-
[Test]
public async Task Import_Doesnt_Import_Banned_Users()
{
@@ -247,7 +245,15 @@ namespace Ombi.Schedule.Tests
Id = "id",
Title = "title",
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>(x => x.AddToRoleAsync(It.Is(x => x.UserName == "plex"), OmbiRoles.RequestMovie))
.ReturnsAsync(IdentityResult.Success);
-
await _subject.Execute(null);
_mocker.Verify(x => x.CreateAsync(It.IsAny()), Times.Once);
@@ -306,7 +311,15 @@ namespace Ombi.Schedule.Tests
{
Email = "email",
Id = "id",
- Username = "plex"
+ Username = "plex",
+ Server = new PlexUserServer[]
+ {
+ new PlexUserServer
+ {
+ Id = "1",
+ ServerId = "123"
+ }
+ }
}
}
});
@@ -331,9 +344,9 @@ namespace Ombi.Schedule.Tests
ImportPlexAdmin = false,
ImportPlexUsers = true,
DefaultRoles = new List
- {
- OmbiRoles.RequestMovie
- }
+ {
+ OmbiRoles.RequestMovie
+ }
});
_mocker.Setup>(x => x.GetUsers(It.IsAny())).ReturnsAsync(new PlexUsers
{
@@ -343,7 +356,15 @@ namespace Ombi.Schedule.Tests
{
Email = "email",
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(x => x.DeleteUser(It.Is(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, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
+ _mocker.Setup>(x => x.GetUsers(It.IsAny())).ReturnsAsync(new PlexUsers
+ {
+ User = new UserFriends[]
+ {
+ new UserFriends
+ {
+ Email = "email",
+ Id = "NoServer",
+ Title = "title",
+ Username = "username",
+ Server = null
+ }
+ }
+ });
+
+ await _subject.Execute(null);
+
+ _mocker.Verify(x => x.CreateAsync(It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task Import_Skips_Users_With_Empty_Server_Array()
+ {
+ _mocker.Setup, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
+ _mocker.Setup>(x => x.GetUsers(It.IsAny())).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(x => x.CreateAsync(It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task Import_Creates_User_With_Server_Access()
+ {
+ _mocker.Setup, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new UserManagementSettings { ImportPlexAdmin = false, ImportPlexUsers = true });
+ _mocker.Setup>(x => x.GetUsers(It.IsAny())).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>(x => x.CreateAsync(It.Is(x =>
+ x.UserName == "username" &&
+ x.Email == "email" &&
+ x.ProviderUserId == "HasServer" &&
+ x.UserType == UserType.PlexUser)))
+ .ReturnsAsync(IdentityResult.Success);
+
+ await _subject.Execute(null);
+
+ _mocker.Verify(x => x.CreateAsync(It.Is(x =>
+ x.UserName == "username" &&
+ x.Email == "email" &&
+ x.ProviderUserId == "HasServer" &&
+ x.UserType == UserType.PlexUser)), Times.Once);
+ }
}
}
diff --git a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs
index 4af981363..87a1d5e28 100644
--- a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs
+++ b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs
@@ -20,6 +20,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
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
{
@@ -35,12 +40,15 @@ namespace Ombi.Schedule.Tests
public void Setup()
{
_mocker = new AutoMocker();
- var um = MockHelper.MockUserManager(new List { new OmbiUser { Id = "abc", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } });
+ var um = MockHelper.MockUserManager(new List { new OmbiUser { Id = "abc", Email = "email@email.com", UserType = UserType.PlexUser, MediaServerToken = "token1", UserName = "abc", NormalizedUserName = "ABC" } });
_mocker.Use(um);
_context = _mocker.GetMock();
_context.Setup(x => x.CancellationToken).Returns(CancellationToken.None);
+ // Mock the keep-alive service to return true by default
+ _mocker.Use(Mock.Of(s => s.KeepTokenAliveAsync(It.IsAny(), It.IsAny()) == Task.FromResult(true)));
_subject = _mocker.CreateInstance();
_mocker.Setup, IQueryable>(x => x.GetAll()).Returns(new List().AsQueryable().BuildMock());
+ _mocker.Setup(x => x.Notify(It.IsAny()));
}
[Test]
@@ -777,5 +785,99 @@ namespace Ombi.Schedule.Tests
_mocker.Verify>(x => x.GetAll(), Times.Once);
_mocker.Verify>(x => x.Add(It.IsAny()), Times.Once);
}
+
+ [Test]
+ public async Task AuthenticationError_NotificationsEnabled_WithEmail_SendsNotification()
+ {
+ _mocker.Setup, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
+ _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
+
+ // Act
+ await _subject.Execute(_context.Object);
+
+ // Assert
+ _mocker.Verify(x => x.Notify(It.Is(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, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = false });
+ _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
+
+ // Act
+ await _subject.Execute(_context.Object);
+
+ // Assert
+ _mocker.Verify(x => x.Notify(It.IsAny()), 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 { user });
+ _mocker.Use(um);
+
+ _mocker.Setup, Task>(x => x.GetSettingsAsync())
+ .ReturnsAsync(new PlexSettings { Enable = true, EnableWatchlistImport = true, NotifyOnWatchlistTokenExpiration = true });
+ _mocker.Setup>(x => x.GetWatchlist(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new PlexWatchlistContainer { AuthError = true });
+
+ _subject = _mocker.CreateInstance();
+
+ // Act
+ await _subject.Execute(_context.Object);
+
+ // Assert
+ _mocker.Verify(x => x.Notify(It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task SkipsUserIfTokenKeepAliveFails()
+ {
+ // Arrange: Set up the keep-alive service to return false (token invalid/expired)
+ var keepAliveMock = new Mock();
+ keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny(), It.IsAny())).ReturnsAsync(false);
+ _mocker.Use(keepAliveMock.Object);
+ _subject = _mocker.CreateInstance();
+ _mocker.Setup, Task>(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(), It.IsAny()), Times.Once);
+ _mocker.Verify(x => x.GetWatchlist(It.IsAny(), It.IsAny()), Times.Never);
+ _mocker.Verify(x => x.Notify(It.IsAny()), Times.Never); // or Times.Once if notification is expected
+ }
+ [Test]
+ public async Task CallsKeepAliveForEachPlexUser()
+ {
+ // Arrange: Multiple Plex users
+ var users = new List
+ {
+ 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();
+ keepAliveMock.Setup(x => x.KeepTokenAliveAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true);
+ _mocker.Use(keepAliveMock.Object);
+ _subject = _mocker.CreateInstance();
+ _mocker.Setup, Task>(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(), It.IsAny()), Times.Exactly(users.Count));
+ }
}
}
diff --git a/src/Ombi.Schedule.Tests/Properties/launchSettings.json b/src/Ombi.Schedule.Tests/Properties/launchSettings.json
deleted file mode 100644
index 57de33708..000000000
--- a/src/Ombi.Schedule.Tests/Properties/launchSettings.json
+++ /dev/null
@@ -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/"
- }
- }
-}
diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs
index 6c90dd20f..bf89e8fa0 100644
--- a/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs
+++ b/src/Ombi.Schedule/Jobs/Plex/PlexUserImporter.cs
@@ -120,6 +120,13 @@ namespace Ombi.Schedule.Jobs.Plex
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
if (userManagementSettings.BannedPlexUserIds.Contains(plexUser.Id))
{
diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
index dc1aa5d56..3de332879 100644
--- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
+++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs
@@ -22,6 +22,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
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
{
@@ -37,11 +42,13 @@ namespace Ombi.Schedule.Jobs.Plex
private readonly IExternalRepository _watchlistRepo;
private readonly IRepository _userError;
private readonly IMovieDbApi _movieDbApi;
+ private readonly INotificationHelper _notificationHelper;
+ private readonly IPlexTokenKeepAliveService _tokenKeepAliveService;
public PlexWatchlistImport(IPlexApi plexApi, ISettingsService settings, OmbiUserManager ombiUserManager,
IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, INotificationHubService notificationHubService,
ILogger logger, IExternalRepository watchlistRepo, IRepository userError,
- IMovieDbApi movieDbApi)
+ IMovieDbApi movieDbApi, INotificationHelper notificationHelper, IPlexTokenKeepAliveService tokenKeepAliveService)
{
_plexApi = plexApi;
_settings = settings;
@@ -53,6 +60,8 @@ namespace Ombi.Schedule.Jobs.Plex
_watchlistRepo = watchlistRepo;
_userError = userError;
_movieDbApi = movieDbApi;
+ _notificationHelper = notificationHelper;
+ _tokenKeepAliveService = tokenKeepAliveService;
}
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}");
+
+ // 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
+ {
+ { "UserName", user.UserName }
+ }
+ };
+ await _notificationHelper.Notify(notificationModel);
+ }
+ continue;
+ }
+
var watchlist = await _plexApi.GetWatchlist(user.MediaServerToken, context?.CancellationToken ?? CancellationToken.None);
if (watchlist?.AuthError ?? false)
{
@@ -99,6 +138,22 @@ namespace Ombi.Schedule.Jobs.Plex
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
+ {
+ { "UserName", user.UserName }
+ }
+ };
+ await _notificationHelper.Notify(notificationModel);
+ }
continue;
}
if (watchlist == null || !(watchlist.MediaContainer?.Metadata?.Any() ?? false))
diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs
index 6edfe0351..649f1d0c7 100644
--- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs
+++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs
@@ -9,6 +9,7 @@ namespace Ombi.Core.Settings.Models.External
public bool Enable { get; set; }
public bool EnableWatchlistImport { get; set; }
public bool MonitorAll { get; set; }
+ public bool NotifyOnWatchlistTokenExpiration { get; set; }
///
/// This is the ClientId for OAuth
///
diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs
index eea83a03d..d3540965f 100644
--- a/src/Ombi.Store/Context/OmbiContext.cs
+++ b/src/Ombi.Store/Context/OmbiContext.cs
@@ -217,6 +217,16 @@ namespace Ombi.Store.Context
Enabled = true,
};
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:
throw new ArgumentOutOfRangeException();
}
diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html
index a1b3bc81b..f8dbaf257 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html
+++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html
@@ -5,13 +5,17 @@
{{'Discovery.Tv' | translate}}
-@defer (when discoverResults.length > 0) {
+@defer (when discoverResults.length > 0; prefetch on idle) {
}
-@placeholder(minimum 500) {
-
+@placeholder(minimum 300) {
+
}
\ No newline at end of file
diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.scss b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.scss
index 81c559a83..9b62c9256 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.scss
+++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.scss
@@ -105,6 +105,30 @@
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){
::ng-deep .p-carousel-item{
flex: 1 0 200px !important;
diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts
index 36b8122ff..2463af3c4 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts
+++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts
@@ -43,7 +43,7 @@ export class CarouselListComponent implements OnInit {
get mediaTypeStorageKey() {
return "DiscoverOptions" + this.discoverType.toString();
};
- private amountToLoad = 17;
+ private amountToLoad = 10;
private currentlyLoaded = 0;
private baseUrl: string = "";
@@ -148,6 +148,7 @@ export class CarouselListComponent implements OnInit {
}
public async ngOnInit() {
+
this.is4kEnabled = this.featureFacade.is4kEnabled();
this.currentlyLoaded = 0;
const localDiscoverOptions = +this.storageService.get(this.mediaTypeStorageKey);
@@ -155,11 +156,15 @@ export class CarouselListComponent implements OnInit {
this.discoverOptions = DiscoverOption[DiscoverOption[localDiscoverOptions]];
}
- let currentIteration = 0;
- while (this.discoverResults.length <= 14 && currentIteration <= 3) {
- currentIteration++;
+ // Load initial data - just enough to fill the first carousel page
+ // This reduces initial API calls and improves loading performance
+ 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);
}
+
}
public async toggleChanged(event: MatButtonToggleChange) {
diff --git a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html
index 16a46c0d6..dc4e33be1 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html
+++ b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html
@@ -1,46 +1,108 @@
-
-
{{ 'Discovery.Genres' | translate }}
-
-
-
-
{{ 'Discovery.RecentlyRequestedTab' | translate }}
-
-
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.Genres' | translate }}
+
-
-
-
-
-
{{ 'Discovery.SeasonalTab' | translate }}
-
-
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.Genres' | translate }}
+
-
+ }
-
-
{{ 'Discovery.PopularTab' | translate }}
-
-
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.RecentlyRequestedTab' | translate }}
+
+
+
-
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.RecentlyRequestedTab' | translate }}
+
+
+ }
-
-
{{ 'Discovery.TrendingTab' | translate }}
-
-
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.SeasonalTab' | translate }}
+
+
+
-
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.SeasonalTab' | translate }}
+
+
+ }
-
-
{{ 'Discovery.UpcomingTab' | translate }}
-
-
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.PopularTab' | translate }}
+
+
+
-
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.PopularTab' | translate }}
+
+
+ }
+
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.TrendingTab' | translate }}
+
+
+
+
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.TrendingTab' | translate }}
+
+
+ }
+
+ @defer (on viewport; prefetch on idle) {
+
+
{{ 'Discovery.UpcomingTab' | translate }}
+
+
+
+
+ } @placeholder(minimum 300) {
+
+
{{ 'Discovery.UpcomingTab' | translate }}
+
+
+ }
diff --git a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.scss b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.scss
index d95586507..9ba892e01 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.scss
+++ b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.scss
@@ -9,4 +9,28 @@ h2{
margin-top:40px;
margin-left:40px;
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);
+ }
}
\ No newline at end of file
diff --git a/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.html b/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.html
index 6da28c744..a6db36865 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.html
+++ b/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.html
@@ -1,4 +1,4 @@
-@defer (when requests()) {
+@defer (when requests(); prefetch on idle) {
-}@placeholder(minimum 500) {
+}@placeholder(minimum 300) {
-
-
-
-
-
diff --git a/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.scss b/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.scss
index 01c68db4d..c7fef78f9 100644
--- a/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.scss
+++ b/src/Ombi/ClientApp/src/app/discover/components/recently-requested-list/recently-requested-list.component.scss
@@ -105,12 +105,32 @@
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){
::ng-deep .p-carousel-item{
flex: 1 0 200px !important;
}
-}
-
-.loading-container {
- margin-left: 10rem;
}
\ No newline at end of file
diff --git a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts
index 460a970fe..5f6085362 100644
--- a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts
+++ b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts
@@ -52,6 +52,7 @@ export enum NotificationType {
IssueComment = 10,
Newsletter = 11,
PartiallyAvailable = 12,
+ PlexWatchlistTokenExpired = 13
}
export interface IDiscordNotifcationSettings extends INotificationSettings {
diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts
index 532b8625f..008628c16 100644
--- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts
+++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts
@@ -114,6 +114,7 @@ export interface IPlexSettings extends ISettings {
enable: boolean;
enableWatchlistImport: boolean;
monitorAll: boolean;
+ notifyOnWatchlistTokenExpiration: boolean;
servers: IPlexServer[];
}
diff --git a/src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts b/src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts
index d7e902242..5c0cc5c3b 100644
--- a/src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts
+++ b/src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts
@@ -1,5 +1,6 @@
import { Pipe, PipeTransform } from "@angular/core";
import { FormatPipe } from 'ngx-date-fns';
+import { parseISO, format } from 'date-fns';
@Pipe({
name: "ombiDate",
@@ -10,8 +11,16 @@ export class OmbiDatePipe implements PipeTransform {
private FormatPipe: FormatPipe,
) {}
- public transform(value: string, format: string ) {
- const date = new Date(value);
- return this.FormatPipe.transform(date, format);
+ public transform(value: string, formatStr: string ) {
+ if (!value) {
+ 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);
}
}
diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html
index dd1ae2f87..03809d317 100644
--- a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html
+++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.html
@@ -1,38 +1,40 @@
-
-
- Watchlist User Errors
-
-
- 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.
-
-
- Successfully syncing the watchlist
-
- Authentication error syncing the watchlist
-
- Not enabled for user (They need to log into Ombi via Plex)
-
-
-
- Username
- {{element.userName}}
-
-
- Watchlist Sync Result
-
-
-
-
-
-
-
-
-
-
-
-
- Close
-
-
+
+
+
+ Watchlist User Errors
+
+
+
+ error_outline
+
+ 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.
+
+
+
+ check_circle Successfully syncing the watchlist
+ cancel Authentication error syncing the watchlist
+ person_off Not enabled for user (They need to log into Ombi via Plex)
+
+
+
+ Username
+ {{element.userName}}
+
+
+ Watchlist Sync Result
+
+ check_circle
+ cancel
+ person_off
+
+
+
+
+
+
+
+ Close
+
+
\ No newline at end of file
diff --git a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss
index f96dda122..29e8cae20 100644
--- a/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss
+++ b/src/Ombi/ClientApp/src/app/settings/plex/components/watchlist/plex-watchlist.component.scss
@@ -10,4 +10,96 @@
.key {
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;
}
\ No newline at end of file
diff --git a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html
index 4bfcbf1c5..3be3300fc 100644
--- a/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html
+++ b/src/Ombi/ClientApp/src/app/settings/plex/plex.component.html
@@ -1,137 +1,179 @@
-