From 53a6a092b14b8b8bdbff95d066926d3dbe6951f4 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 30 Nov 2024 19:09:51 +0000 Subject: [PATCH 01/20] =?UTF-8?q?fix(translations):=20=F0=9F=8C=90=20New?= =?UTF-8?q?=20translations=20from=20Crowdin=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ombi/wwwroot/translations/ca.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/wwwroot/translations/ca.json b/src/Ombi/wwwroot/translations/ca.json index 2c7e21fc2..7ac957b11 100644 --- a/src/Ombi/wwwroot/translations/ca.json +++ b/src/Ombi/wwwroot/translations/ca.json @@ -159,7 +159,7 @@ "RequestedBy": "Sol·licitat per", "Status": "Estat", "RequestStatus": "Estat de la sol·licitud", - "Watched": "Watched", + "Watched": "Vist", "WatchedTooltip": "The user who made the request has watched it", "WatchedProgressTooltip": "Shows how much the user who made the request has watched it", "WatchedByUsersCount": "{{count}} users have watched this.", @@ -408,7 +408,7 @@ "Movies": "Pel·lícules", "Combined": "Combinat", "Tv": "TV", - "Genres": "Genres", + "Genres": "Gèneres", "CardDetails": { "Availability": "Disponibilitat", "Studio": "Estudi", From dbbfdd926f0808f6d16f0b2cd8b5406e6b610c82 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 13 May 2025 15:38:31 +0100 Subject: [PATCH 02/20] =?UTF-8?q?fix(translations):=20=F0=9F=8C=90=20New?= =?UTF-8?q?=20translations=20from=20Crowdin=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ombi/wwwroot/translations/nl.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index 4a3dab266..fb78ae68f 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -159,10 +159,10 @@ "RequestedBy": "Verzocht Door", "Status": "Status", "RequestStatus": "Aanvraagstatus", - "Watched": "Watched", - "WatchedTooltip": "The user who made the request has watched it", - "WatchedProgressTooltip": "Shows how much the user who made the request has watched it", - "WatchedByUsersCount": "{{count}} users have watched this.", + "Watched": "Bekeken", + "WatchedTooltip": "De gebruiker die het verzoek heeft ingediend, heeft het bekeken", + "WatchedProgressTooltip": "Laat zien hoeveel de gebruiker die het verzoek heeft gemaakt het heeft bekeken", + "WatchedByUsersCount": "{{count}} gebruikers hebben dit bekeken.", "Denied": " Geweigerd:", "TheatricalRelease": "Cinema Uitgave: {{date}}", "ReleaseDate": "Uitgekomen: {{date}}", @@ -225,7 +225,7 @@ "Denied": "Geselecteerde items succesvol afgekeurd" }, "SuccessfullyApproved": "Succesvol goedgekeurd", - "SuccessfullyDenied": "Successfully Denied", + "SuccessfullyDenied": "Succesvol Geweigerd", "SuccessfullyDeleted": "Verzoek succesvol verwijderd", "NowAvailable": "Verzoek is nu beschikbaar", "NowUnavailable": "Verzoek is nu niet beschikbaar", @@ -241,7 +241,7 @@ "NoPermissionsOnBehalf": "Je hebt niet de juiste rechten om namens gebruikers aan te vragen!", "NoPermissions": "Je hebt de juiste rechten niet!", "RequestDoesNotExist": "Verzoek bestaat niet", - "ChildRequestDoesNotExist": "Child Request does not exist", + "ChildRequestDoesNotExist": "Kindverzoek bestaat niet", "NoPermissionsRequestMovie": "Je bent niet gemachtigd om een film aan te vragen", "NoPermissionsRequestTV": "Je bent niet gemachtigd om een serie aan te vragen", "NoPermissionsRequestAlbum": "Je bent niet gemachtigd om een album aan te vragen", From f8658fe6d56488aa5caa68093245cbf021a31810 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 14 May 2025 21:51:41 +0100 Subject: [PATCH 03/20] fix(radarr): ensure RequestedUser is loaded when creating tags - Replace Find() with GetWithUser() in MovieRequestEngine methods to properly load RequestedUser navigation property - Add null check in GetOrCreateTag as a safety measure - Fix NullReferenceException when "Add the user as a tag" feature is enabled Fixes #5045 --- src/Ombi.Core/Engine/MovieRequestEngine.cs | 10 +++++----- src/Ombi.Core/Senders/MovieSender.cs | 11 ++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) 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/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 26da3465c..3b0017492 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 @@ -246,6 +249,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? From 08c9017a2c865a7da57e444f4798d51b9fc3575b Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Wed, 14 May 2025 20:53:39 +0000 Subject: [PATCH 04/20] chore(release): :rocket: v4.48.2 [skip ci] --- CHANGELOG.md | 21 +++++++++------------ version.json | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f394da58..3e5ecd445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2196,15 +2205,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index 484557399..34d9dd765 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.48.1" + "version": "4.48.2" } From ba6e708e189f52f2ff4ebc073fa38a4f53f1061c Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 14 May 2025 21:57:06 +0100 Subject: [PATCH 05/20] fix: Correct 4K movie request existence check When requesting a 4K movie, Ombi was incorrectly checking for existence in the base Radarr instance instead of the 4K instance. This caused "already exists" errors when trying to request 4K versions of movies that only existed in the standard instance. Changes: - Modified SendToRadarr to use the correct Radarr instance (4K or standard) when checking for existing movies - Added existenceCheckSettings to properly handle instance-specific checks - Maintains original settings for movie addition/update operations Fixes #4798 --- src/Ombi.Core/Senders/MovieSender.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 3b0017492..a7ae4a6e7 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -201,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) From 15a97794f6a108aa361c425fbc3d107e8cbb0b6e Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Wed, 14 May 2025 20:59:03 +0000 Subject: [PATCH 06/20] chore(release): :rocket: v4.48.3 [skip ci] --- CHANGELOG.md | 18 +++++++++--------- version.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e5ecd445..6c21c3693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2196,12 +2205,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index 34d9dd765..0650edbbe 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.48.2" + "version": "4.48.3" } From f88c5ad818fadea7064e7dfbe46f07eae855109a Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 14 May 2025 22:09:46 +0100 Subject: [PATCH 07/20] fix(ui): correct timezone handling in OmbiDatePipe - Replace native Date constructor with date-fns parseISO for proper UTC parsing - Use date-fns format function for consistent timezone conversion - Add null check for input value - Fix issue where request times were showing incorrect timezone offset This fixes GitHub issue #5102 where request times were showing different times than the host machine. --- src/Ombi/ClientApp/src/app/pipes/OmbiDatePipe.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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); } } From acb679f99de42283fb73a2d39a084ca29d078604 Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Wed, 14 May 2025 21:11:38 +0000 Subject: [PATCH 08/20] chore(release): :rocket: v4.48.4 [skip ci] --- CHANGELOG.md | 20 +++++++++++--------- version.json | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c21c3693..c13c21a2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [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) @@ -2196,12 +2207,3 @@ -# [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) - - - diff --git a/version.json b/version.json index 0650edbbe..b4aa9f40f 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.48.3" + "version": "4.48.4" } From c9ab4f4f9faa66dbf263da693db1eefcf68beeec Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 14 May 2025 22:16:34 +0100 Subject: [PATCH 09/20] fix: filter out excluded notification agents from user preferences The webhook notification field was inconsistently showing up for some users despite being excluded in the backend. This was happening because the frontend was displaying all notification preferences without filtering out the excluded agents. Changes: - Added excludedAgents array to match backend's excluded notification types - Filter notification preferences in both edit and create user flows - Prevents webhook, email, and mobile notification fields from appearing in user preferences This change aligns the frontend behavior with the backend's intended design where webhook notifications are managed globally rather than per-user. Fixes #5196 --- .../usermanagement-user.component.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts index 1110490b9..a2e1872f9 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement-user.component.ts @@ -37,6 +37,13 @@ export class UserManagementUserComponent implements OnInit { private appUrl: string = this.customizationFacade.appUrl(); private accessToken: string; + // List of excluded notification agents that should not be shown in user preferences + private readonly excludedAgents = [ + INotificationAgent.Email, + INotificationAgent.Mobile, + INotificationAgent.Webhook + ]; + constructor(private identityService: IdentityService, private notificationService: MessageService, private router: Router, @@ -74,9 +81,15 @@ export class UserManagementUserComponent implements OnInit { } }); if(this.edit) { - this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x); + this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => { + // Filter out excluded notification agents + this.notificationPreferences = x.filter(pref => !this.excludedAgents.includes(pref.agent)); + }); } else { - this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x); + this.identityService.getNotificationPreferences().subscribe(x => { + // Filter out excluded notification agents + this.notificationPreferences = x.filter(pref => !this.excludedAgents.includes(pref.agent)); + }); } this.sonarrService.getQualityProfilesWithoutSettings().subscribe(x => { this.sonarrQualities = x; From cfe2b6ac0f51b5c8b87306cc90e5509b46e8fbc5 Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Wed, 14 May 2025 21:18:32 +0000 Subject: [PATCH 10/20] chore(release): :rocket: v4.48.5 [skip ci] --- CHANGELOG.md | 18 +++++++++--------- version.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c13c21a2c..979453ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2198,12 +2207,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index b4aa9f40f..ead760bcd 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.48.4" + "version": "4.48.5" } From 067c029f42e9fd853d060fdb2093013b15ac14c0 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 11 Jul 2025 22:19:10 +0100 Subject: [PATCH 11/20] feat: Added the ability for the Watchlist to automatically refresh the users token. This will reduce the need for the user to log in --- src/Ombi.Api.Plex/IPlexApi.cs | 1 + src/Ombi.Api.Plex/PlexApi.cs | 24 +++++++++++ src/Ombi.DependencyInjection/IocExtensions.cs | 1 + .../PlexWatchlistImportTests.cs | 41 +++++++++++++++++++ .../Jobs/Plex/PlexWatchlistImport.cs | 35 +++++++++++++++- 5 files changed, 101 insertions(+), 1 deletion(-) 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/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index cc0d13aaa..2d2f0271d 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -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.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index caceb9b0e..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(); diff --git a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs index b1fec6d1b..87a1d5e28 100644 --- a/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs +++ b/src/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs @@ -24,6 +24,7 @@ using Ombi.Notifications.Models; using Ombi.Core.Notifications; using Ombi.Helpers; using Ombi.Core; +using Ombi.Core.Authentication; namespace Ombi.Schedule.Tests { @@ -43,6 +44,8 @@ namespace Ombi.Schedule.Tests _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())); @@ -838,5 +841,43 @@ namespace Ombi.Schedule.Tests // 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/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs index 947e54406..61151a56f 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs @@ -27,6 +27,7 @@ using Ombi.Core.Notifications; using Microsoft.AspNetCore.Identity; using Ombi.Store.Repository.Requests; using Ombi.Core; +using Ombi.Core.Authentication; namespace Ombi.Schedule.Jobs.Plex { @@ -43,11 +44,12 @@ namespace Ombi.Schedule.Jobs.Plex 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, INotificationHelper notificationHelper) + IMovieDbApi movieDbApi, INotificationHelper notificationHelper, IPlexTokenKeepAliveService tokenKeepAliveService) { _plexApi = plexApi; _settings = settings; @@ -60,6 +62,7 @@ namespace Ombi.Schedule.Jobs.Plex _userError = userError; _movieDbApi = movieDbApi; _notificationHelper = notificationHelper; + _tokenKeepAliveService = tokenKeepAliveService; } public async Task Execute(IJobExecutionContext context) @@ -97,6 +100,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) { From ed5bc3f873597484f46517d329720d31d2af1ce4 Mon Sep 17 00:00:00 2001 From: contrib-readme-bot Date: Fri, 11 Jul 2025 21:20:26 +0000 Subject: [PATCH 12/20] chore: :busts_in_silhouette: Updated Contributors [skip ci] --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0e9350d59..8fb37b8f8 100644 --- a/README.md +++ b/README.md @@ -122,10 +122,10 @@ Here are some of the features Ombi has: - - MattJeanes + + AmyJeanes
- Matt Jeanes + Amy Jeanes
From 3b2a0d84bee4596905b8974d6d5d817c070e4f38 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 11 Jul 2025 22:49:21 +0100 Subject: [PATCH 13/20] fix --- .../PlexTokenKeepAliveService.cs | 52 +++++++++++++++++++ .../Jobs/Plex/PlexWatchlistImport.cs | 1 - 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Ombi.Core/Authentication/PlexTokenKeepAliveService.cs 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.Schedule/Jobs/Plex/PlexWatchlistImport.cs b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs index 61151a56f..3de332879 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs @@ -27,7 +27,6 @@ using Ombi.Core.Notifications; using Microsoft.AspNetCore.Identity; using Ombi.Store.Repository.Requests; using Ombi.Core; -using Ombi.Core.Authentication; namespace Ombi.Schedule.Jobs.Plex { From 1eff48e58e94a41028bc53b6d5a3dd27d481c4fb Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Fri, 11 Jul 2025 21:51:32 +0000 Subject: [PATCH 14/20] chore(release): :rocket: v4.49.0 [skip ci] --- CHANGELOG.md | 19 +++++++++---------- version.json | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 979453ca0..5f23b6c98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# [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) @@ -2197,13 +2206,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index ead760bcd..e6fc87d8b 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.48.5" + "version": "4.49.0" } From cee40146ee02f7fb79e2019d6fe2f9d5c5dbdfc8 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 12 Jul 2025 22:25:31 +0100 Subject: [PATCH 15/20] fix(auth): Fixed an issue where refreshing the page as a power user would stop the application from loading #5242 --- src/Ombi/Controllers/V1/SettingsController.cs | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index ad714780b..a23d56124 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -40,7 +40,6 @@ namespace Ombi.Controllers.V1 /// /// The Settings Controller /// - [Admin] [ApiV1] [Produces("application/json")] [ApiController] @@ -78,6 +77,7 @@ namespace Ombi.Controllers.V1 /// Gets the Ombi settings. ///
/// + [Admin] [HttpGet("ombi")] public async Task OmbiSettings() { @@ -110,6 +110,7 @@ namespace Ombi.Controllers.V1 /// /// The ombi. /// + [Admin] [HttpPost("ombi")] public async Task OmbiSettings([FromBody]OmbiSettings ombi) { @@ -145,6 +146,7 @@ namespace Ombi.Controllers.V1 return model; } + [Admin] [HttpPost("ombi/resetApi")] public async Task ResetApiKey() { @@ -159,6 +161,7 @@ namespace Ombi.Controllers.V1 /// Gets the Plex Settings. /// /// + [Admin] [HttpGet("plex")] public async Task PlexSettings() { @@ -185,6 +188,7 @@ namespace Ombi.Controllers.V1 /// /// The plex. /// + [Admin] [HttpPost("plex")] public async Task PlexSettings([FromBody]PlexSettings plex) { @@ -207,6 +211,7 @@ namespace Ombi.Controllers.V1 /// Gets the Emby Settings. /// /// + [Admin] [HttpGet("emby")] public async Task EmbySettings() { @@ -218,6 +223,7 @@ namespace Ombi.Controllers.V1 /// /// The emby. /// + [Admin] [HttpPost("emby")] public async Task EmbySettings([FromBody]EmbySettings emby) { @@ -243,6 +249,7 @@ namespace Ombi.Controllers.V1 /// Gets the Jellyfin Settings. /// /// + [Admin] [HttpGet("jellyfin")] public async Task JellyfinSettings() { @@ -254,6 +261,7 @@ namespace Ombi.Controllers.V1 /// /// The jellyfin. /// + [Admin] [HttpPost("jellyfin")] public async Task JellyfinSettings([FromBody]JellyfinSettings jellyfin) { @@ -291,6 +299,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("landingpage")] public async Task LandingPageSettings([FromBody]LandingPageSettings settings) { @@ -326,6 +335,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("customization")] public async Task CustomizationSettings([FromBody]CustomizationSettings settings) { @@ -344,6 +354,7 @@ namespace Ombi.Controllers.V1 /// Get's the preset themes available /// /// + [Admin] [HttpGet("themes")] public async Task> GetThemes() { @@ -389,6 +400,7 @@ namespace Ombi.Controllers.V1 /// The settings. /// [HttpPost("sonarr")] + [Admin] public async Task SonarrSettings([FromBody]SonarrSettings settings) { var result = await Save(settings); @@ -418,6 +430,7 @@ namespace Ombi.Controllers.V1 /// Gets the Lidarr Settings. /// /// + [Admin] [HttpGet("lidarr")] public async Task LidarrSettings() { @@ -441,6 +454,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("lidarr")] public async Task LidarrSettings([FromBody]LidarrSettings settings) { @@ -457,6 +471,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("authentication")] public async Task AuthenticationsSettings([FromBody]AuthenticationSettings settings) { @@ -479,6 +494,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("radarr")] public async Task RadarrSettings([FromBody]RadarrCombinedModel settings) { @@ -500,6 +516,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("Update")] public async Task UpdateSettings([FromBody]UpdateSettings settings) { @@ -510,6 +527,7 @@ namespace Ombi.Controllers.V1 /// Gets the UserManagement Settings. /// /// + [Admin] [HttpGet("UserManagement")] public async Task UserManagementSettings() { @@ -521,6 +539,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("UserManagement")] public async Task UserManagementSettings([FromBody]UserManagementSettings settings) { @@ -531,6 +550,7 @@ namespace Ombi.Controllers.V1 /// Gets the Update Settings. /// /// + [Admin] [HttpGet("Update")] public async Task UpdateSettings() { @@ -543,6 +563,7 @@ namespace Ombi.Controllers.V1 /// Gets the CouchPotatoSettings Settings. /// /// + [Admin] [HttpGet("CouchPotato")] public async Task CouchPotatoSettings() { @@ -554,6 +575,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("CouchPotato")] public async Task CouchPotatoSettings([FromBody]CouchPotatoSettings settings) { @@ -564,6 +586,7 @@ namespace Ombi.Controllers.V1 /// Gets the DogNzbSettings Settings. /// /// + [Admin] [HttpGet("DogNzb")] public async Task DogNzbSettings() { @@ -575,6 +598,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("DogNzb")] public async Task DogNzbSettings([FromBody]DogNzbSettings settings) { @@ -586,6 +610,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("SickRage")] public async Task SickRageSettings([FromBody]SickRageSettings settings) { @@ -596,6 +621,7 @@ namespace Ombi.Controllers.V1 /// Gets the SickRage Settings. /// /// + [Admin] [HttpGet("SickRage")] public async Task SickRageSettings() { @@ -606,6 +632,7 @@ namespace Ombi.Controllers.V1 /// Gets the JobSettings Settings. /// /// + [Admin] [HttpGet("jobs")] public async Task JobSettings() { @@ -638,6 +665,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. /// + [Admin] [HttpPost("jobs")] public async Task JobSettings([FromBody]JobSettings settings) { @@ -681,6 +709,7 @@ namespace Ombi.Controllers.V1 } [HttpPost("testcron")] + [Admin] public CronTestModel TestCron([FromBody] CronViewModelBody body) { var model = new CronTestModel(); @@ -714,6 +743,7 @@ namespace Ombi.Controllers.V1 /// The settings. /// [HttpPost("Issues")] + [Admin] public async Task IssueSettings([FromBody]IssueSettings settings) { return await Save(settings); @@ -744,6 +774,7 @@ namespace Ombi.Controllers.V1 /// The settings. /// [HttpPost("vote")] + [Admin] public async Task VoteSettings([FromBody]VoteSettings settings) { return await Save(settings); @@ -754,6 +785,7 @@ namespace Ombi.Controllers.V1 /// /// [HttpGet("vote")] + [Admin] public async Task VoteSettings() { return await Get(); @@ -772,6 +804,7 @@ namespace Ombi.Controllers.V1 /// /// The settings. [HttpPost("themoviedb")] + [Admin] public async Task TheMovieDbSettings([FromBody]TheMovieDbSettings settings) { return await Save(settings); @@ -780,6 +813,7 @@ namespace Ombi.Controllers.V1 /// /// Get The Movie DB settings. /// + [Admin] [HttpGet("themoviedb")] public async Task TheMovieDbSettings() { @@ -791,6 +825,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/email")] public async Task EmailNotificationSettings([FromBody] EmailNotificationsViewModel model) { @@ -808,6 +843,7 @@ namespace Ombi.Controllers.V1 /// Gets the Email Notification Settings. /// /// + [Admin] [HttpGet("notifications/email")] public async Task EmailNotificationSettings() { @@ -838,6 +874,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/discord")] public async Task DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model) { @@ -855,6 +892,7 @@ namespace Ombi.Controllers.V1 /// Gets the discord Notification Settings. /// /// + [Admin] [HttpGet("notifications/discord")] public async Task DiscordNotificationSettings() { @@ -873,6 +911,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/telegram")] public async Task TelegramNotificationSettings([FromBody] TelegramNotificationsViewModel model) { @@ -890,6 +929,7 @@ namespace Ombi.Controllers.V1 /// Gets the telegram Notification Settings. /// /// + [Admin] [HttpGet("notifications/telegram")] public async Task TelegramNotificationSettings() { @@ -907,6 +947,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/pushbullet")] public async Task PushbulletNotificationSettings([FromBody] PushbulletNotificationViewModel model) { @@ -924,6 +965,7 @@ namespace Ombi.Controllers.V1 /// Gets the pushbullet Notification Settings. /// /// + [Admin] [HttpGet("notifications/pushbullet")] public async Task PushbulletNotificationSettings() { @@ -941,6 +983,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/pushover")] public async Task PushoverNotificationSettings([FromBody] PushoverNotificationViewModel model) { @@ -958,6 +1001,7 @@ namespace Ombi.Controllers.V1 /// Gets the pushover Notification Settings. /// /// + [Admin] [HttpGet("notifications/pushover")] public async Task PushoverNotificationSettings() { @@ -976,6 +1020,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/slack")] public async Task SlacktNotificationSettings([FromBody] SlackNotificationsViewModel model) { @@ -993,6 +1038,7 @@ namespace Ombi.Controllers.V1 /// Gets the slack Notification Settings. /// /// + [Admin] [HttpGet("notifications/slack")] public async Task SlackNotificationSettings() { @@ -1010,6 +1056,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/mattermost")] public async Task MattermostNotificationSettings([FromBody] MattermostNotificationsViewModel model) { @@ -1027,6 +1074,7 @@ namespace Ombi.Controllers.V1 /// Gets the Mattermost Notification Settings. /// /// + [Admin] [HttpGet("notifications/mattermost")] public async Task MattermostNotificationSettings() { @@ -1043,6 +1091,7 @@ namespace Ombi.Controllers.V1 /// Gets the Twilio Notification Settings. /// /// + [Admin] [HttpGet("notifications/twilio")] public async Task TwilioNotificationSettings() { @@ -1064,6 +1113,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/twilio")] public async Task TwilioNotificationSettings([FromBody] TwilioSettingsViewModel model) { @@ -1082,6 +1132,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/mobile")] public async Task MobileNotificationSettings([FromBody] MobileNotificationsViewModel model) { @@ -1099,6 +1150,7 @@ namespace Ombi.Controllers.V1 /// Gets the Mobile Notification Settings. /// /// + [Admin] [HttpGet("notifications/mobile")] public async Task MobileNotificationSettings() { @@ -1116,6 +1168,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/gotify")] public async Task GotifyNotificationSettings([FromBody] GotifyNotificationViewModel model) { @@ -1133,6 +1186,7 @@ namespace Ombi.Controllers.V1 /// Gets the gotify Notification Settings. /// /// + [Admin] [HttpGet("notifications/gotify")] public async Task GotifyNotificationSettings() { @@ -1150,6 +1204,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/webhook")] public async Task WebhookNotificationSettings([FromBody] WebhookNotificationViewModel model) { @@ -1163,6 +1218,7 @@ namespace Ombi.Controllers.V1 /// Gets the webhook notification settings. /// /// + [Admin] [HttpGet("notifications/webhook")] public async Task WebhookNotificationSettings() { @@ -1177,6 +1233,7 @@ namespace Ombi.Controllers.V1 /// /// The model. /// + [Admin] [HttpPost("notifications/newsletter")] public async Task NewsletterSettings([FromBody] NewsletterNotificationViewModel model) { @@ -1191,6 +1248,7 @@ namespace Ombi.Controllers.V1 } [ApiExplorerSettings(IgnoreApi = true)] + [Admin] [HttpPost("notifications/newsletterdatabase")] public async Task UpdateNewsletterDatabase() { @@ -1201,6 +1259,7 @@ namespace Ombi.Controllers.V1 /// Gets the Newsletter Notification Settings. /// /// + [Admin] [HttpGet("notifications/newsletter")] public async Task NewsletterSettings() { From 2519cca9f60f7d967b8f6ae251fb0dc65ce211be Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Sat, 12 Jul 2025 21:27:34 +0000 Subject: [PATCH 16/20] chore(release): :rocket: v4.49.1 [skip ci] --- CHANGELOG.md | 18 +++++++++--------- version.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f23b6c98..8a894dc11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2197,12 +2206,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index e6fc87d8b..1535ec55c 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.49.0" + "version": "4.49.1" } From 97d5167db6c9f915021f32b96b281d7db3741d7f Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 12 Jul 2025 22:35:11 +0100 Subject: [PATCH 17/20] perf(discover): :zap: Improve the loading performance on the discover page --- .../carousel-list.component.html | 10 +- .../carousel-list.component.scss | 24 ++++ .../carousel-list/carousel-list.component.ts | 13 +- .../discover/discover.component.html | 134 +++++++++++++----- .../discover/discover.component.scss | 24 ++++ .../recently-requested-list.component.html | 18 +-- .../recently-requested-list.component.scss | 28 +++- 7 files changed, 189 insertions(+), 62 deletions(-) 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) {
@@ -13,21 +13,9 @@
-}@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 From d2be48a9211c53b776d61eba3ce50e6d7c11255e Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Sat, 12 Jul 2025 21:47:40 +0000 Subject: [PATCH 18/20] chore(release): :rocket: v4.49.2 [skip ci] --- CHANGELOG.md | 18 +++++++++--------- version.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a894dc11..090ac2cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2197,12 +2206,3 @@ -## [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)) - - - diff --git a/version.json b/version.json index 1535ec55c..5c3a995e4 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.49.1" + "version": "4.49.2" } From 11fd7a5fc853da75974a16bf4fdecd72a836f54b Mon Sep 17 00:00:00 2001 From: emmatherock Date: Thu, 14 Aug 2025 21:17:10 -0300 Subject: [PATCH 19/20] fix(plex-api): update Plex Watchlist URL --- src/Ombi.Api.Plex/PlexApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index 2d2f0271d..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 From b72f47470c892686d8c0641048a34961db459664 Mon Sep 17 00:00:00 2001 From: Conventional Changelog Action Date: Sun, 17 Aug 2025 16:01:24 +0000 Subject: [PATCH 20/20] chore(release): :rocket: v4.49.3 [skip ci] --- CHANGELOG.md | 13 +++++++++---- version.json | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 090ac2cd0..115942b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [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) @@ -2202,7 +2211,3 @@ -## [4.43.5](https://github.com/Ombi-app/Ombi/compare/v4.43.4...v4.43.5) (2023-08-24) - - - diff --git a/version.json b/version.json index 5c3a995e4..dc01ed894 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "4.49.2" + "version": "4.49.3" }