From 5104497331c0519c551e1af6b3999f0da0d65058 Mon Sep 17 00:00:00 2001 From: David Federman Date: Tue, 2 Jun 2026 23:12:50 -0700 Subject: Reject unsafe plugin package names in installer --- .../Updates/InstallationManager.cs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index ef53e3b326..c8a2d98bf4 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -521,9 +521,27 @@ namespace Emby.Server.Implementations.Updates return; } + if (!IsValidPackageDirectoryName(package.Name)) + { + _logger.LogError("Refusing to install package with invalid name {PackageName}.", package.Name); + throw new InvalidDataException($"Plugin package name '{package.Name}' is not a valid directory name."); + } + // Always override the passed-in target (which is a file) and figure it out again string targetDir = Path.Combine(_appPaths.PluginsPath, package.Name); + var pluginsRoot = Path.TrimEndingDirectorySeparator(Path.GetFullPath(_appPaths.PluginsPath)); + var resolvedTarget = Path.GetFullPath(targetDir); + if (!resolvedTarget.StartsWith(pluginsRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogError( + "Refusing to install package {PackageName}: resolved target {Resolved} is outside plugins directory {Root}.", + package.Name, + resolvedTarget, + pluginsRoot); + throw new InvalidDataException($"Plugin package name '{package.Name}' resolves outside the plugins directory."); + } + using var response = await _httpClientFactory.CreateClient(NamedClient.Default) .GetAsync(new Uri(package.SourceUrl), cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); @@ -572,6 +590,31 @@ namespace Emby.Server.Implementations.Updates _pluginManager.ImportPluginFrom(targetDir); } + private static bool IsValidPackageDirectoryName(string? name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return false; + } + + if (name.Equals(".", StringComparison.Ordinal) || name.Equals("..", StringComparison.Ordinal)) + { + return false; + } + + if (name.Contains('/', StringComparison.Ordinal) || name.Contains('\\', StringComparison.Ordinal)) + { + return false; + } + + if (name.AsSpan().IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + return false; + } + + return true; + } + private async Task InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken) { LocalPlugin? plugin = _pluginManager.Plugins.FirstOrDefault(p => p.Id.Equals(package.Id) && p.Version.Equals(package.Version)) -- cgit v1.2.3 From 26a149a970ad1f88fbd6e1676a5098e4a63531fe Mon Sep 17 00:00:00 2001 From: David Federman Date: Wed, 3 Jun 2026 08:04:39 -0700 Subject: Address PR comment --- Emby.Server.Implementations/Updates/InstallationManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index c8a2d98bf4..110c388fbe 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -32,6 +32,8 @@ namespace Emby.Server.Implementations.Updates /// public class InstallationManager : IInstallationManager { + private static readonly char[] InvlidPackageNameChars = [.. Path.GetInvalidFileNameChars(), '/', '\\']; + /// /// The logger. /// @@ -602,12 +604,7 @@ namespace Emby.Server.Implementations.Updates return false; } - if (name.Contains('/', StringComparison.Ordinal) || name.Contains('\\', StringComparison.Ordinal)) - { - return false; - } - - if (name.AsSpan().IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + if (name.AsSpan().IndexOfAny(InvlidPackageNameChars) >= 0) { return false; } -- cgit v1.2.3 From 0ed27bad65aa48c4c39c74493a90c3c81795d5ab Mon Sep 17 00:00:00 2001 From: David Federman Date: Sat, 6 Jun 2026 21:55:30 -0700 Subject: Address PR comment --- Emby.Server.Implementations/Updates/InstallationManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 110c388fbe..6a60f7f5f6 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -32,7 +33,7 @@ namespace Emby.Server.Implementations.Updates /// public class InstallationManager : IInstallationManager { - private static readonly char[] InvlidPackageNameChars = [.. Path.GetInvalidFileNameChars(), '/', '\\']; + private static readonly SearchValues InvalidPackageNameChars = SearchValues.Create([.. Path.GetInvalidFileNameChars(), '/', '\\']); /// /// The logger. @@ -604,7 +605,7 @@ namespace Emby.Server.Implementations.Updates return false; } - if (name.AsSpan().IndexOfAny(InvlidPackageNameChars) >= 0) + if (name.IndexOfAny(InvalidPackageNameChars) >= 0) { return false; } -- cgit v1.2.3 From b9271eb19995056ab9e1c56ba810e963702c9254 Mon Sep 17 00:00:00 2001 From: theguymadmax Date: Mon, 15 Jun 2026 19:07:34 -0400 Subject: Skip parsing root-level folders in SeriesResolver --- Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 769d721665..94e48c59ec 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -57,6 +57,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } + if (args.Parent is not null && args.Parent.IsRoot) + { + return null; + } + var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path); var collectionType = args.GetCollectionType(); -- cgit v1.2.3 From 8d15529df7b66728ba87e9e9312f498cdb0f1dd3 Mon Sep 17 00:00:00 2001 From: AfmanS Date: Sat, 20 Jun 2026 07:07:05 -0400 Subject: Translated using Weblate (Portuguese (Portugal)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- Emby.Server.Implementations/Localization/Core/pt-PT.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index dd482d1e9b..ce7f6d120e 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -107,5 +107,6 @@ "TaskMoveTrickplayImagesDescription": "Move os ficheiros trickplay existentes de acordo com as definições da mediateca.", "CleanupUserDataTaskDescription": "Apaga todos os dados de utilizador (estados de reprodução, favoritos, etc) de arquivos média não presentes há 90 dias ou mais.", "CleanupUserDataTask": "Limpeza de dados de utilizador", - "Original": "Original" + "Original": "Original", + "LyricDownloadFailureFromForItem": "Erro ao descarregar letras de {0} para {1}" } -- cgit v1.2.3 From 11f642594d4c63c09fbb62c970c6a94272c7b271 Mon Sep 17 00:00:00 2001 From: Žiga Ules Date: Sat, 20 Jun 2026 05:34:09 -0400 Subject: Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- Emby.Server.Implementations/Localization/Core/sl-SI.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 8c8ed3254a..a1b5b714af 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -106,5 +106,7 @@ "TaskAudioNormalization": "Normalizacija zvoka", "TaskAudioNormalizationDescription": "Pregled datotek za podatke o normalizaciji zvoka.", "CleanupUserDataTask": "Čiščenje uporabniških podatkov", - "CleanupUserDataTaskDescription": "Izbriše vse uporabniške podatke (stanje ogleda, priljubljene itd.) za vsebine, ki že več kot 90 dni niso na voljo." + "CleanupUserDataTaskDescription": "Izbriše vse uporabniške podatke (stanje ogleda, priljubljene itd.) za vsebine, ki že več kot 90 dni niso na voljo.", + "LyricDownloadFailureFromForItem": "Besedila ni bilo mogoče prenesti iz {0} za {1}", + "Original": "Original" } -- cgit v1.2.3 From 7f7e4dfa404fcec23150651430d6df3911998e43 Mon Sep 17 00:00:00 2001 From: Jensen Date: Mon, 22 Jun 2026 00:52:24 -0400 Subject: Added translation using Weblate (English (United States)) --- Emby.Server.Implementations/Localization/Core/en_US.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/en_US.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/en_US.json b/Emby.Server.Implementations/Localization/Core/en_US.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/en_US.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From da2b994fff284ef35dcae87beaf9def9072aa18a Mon Sep 17 00:00:00 2001 From: Jensen Date: Mon, 22 Jun 2026 01:05:19 -0400 Subject: Translated using Weblate (English (United States)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en_US/ --- .../Localization/Core/en_US.json | 65 +++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/en_US.json b/Emby.Server.Implementations/Localization/Core/en_US.json index 0967ef424b..b093f73099 100644 --- a/Emby.Server.Implementations/Localization/Core/en_US.json +++ b/Emby.Server.Implementations/Localization/Core/en_US.json @@ -1 +1,64 @@ -{} +{ + "AppDeviceValues": "App: {0}, Device: {1}", + "Artists": "Artists", + "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "Books": "Books", + "ChapterNameValue": "Chapter {0}", + "Collections": "Collections", + "Default": "Default", + "External": "External", + "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "Favorites": "Favorites", + "Folders": "Folders", + "Forced": "Forced", + "Genres": "Genres", + "HeaderContinueWatching": "Continue Watching", + "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderFavoriteShows": "Favorite Shows", + "HeaderLiveTV": "Live TV", + "HeaderNextUp": "Next Up", + "HearingImpaired": "Hearing Impaired", + "HomeVideos": "Home Videos", + "Inherit": "Inherit", + "LabelIpAddressValue": "IP address: {0}", + "LabelRunningTimeValue": "Running time: {0}", + "Latest": "Latest", + "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}", + "MixedContent": "Mixed content", + "Movies": "Movies", + "Music": "Music", + "MusicVideos": "Music Videos", + "NameInstallFailed": "{0} installation failed", + "NameSeasonNumber": "Season {0}", + "NameSeasonUnknown": "Season Unknown", + "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionAudioPlayback": "Audio playback started", + "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionPluginError": "Plugin failure", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionUserLockedOut": "User locked out", + "NotificationOptionVideoPlayback": "Video playback started", + "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "Original": "Original", + "Photos": "Photos", + "PluginInstalledWithName": "{0} was installed", + "PluginUninstalledWithName": "{0} was uninstalled", + "PluginUpdatedWithName": "{0} was updated", + "ScheduledTaskFailedWithName": "{0} failed", + "Shows": "Shows", + "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} to {1}", + "TvShows": "TV Shows", + "Undefined": "Undefined", + "UserCreatedWithName": "User {0} has been created", + "UserDeletedWithName": "User {0} has been deleted" +} -- cgit v1.2.3 From 7f2cd5cf57471f8f103943ee117e44fcf4428343 Mon Sep 17 00:00:00 2001 From: engineer948 Date: Mon, 22 Jun 2026 17:09:25 -0400 Subject: Added translation using Weblate (Azerbaijani) --- Emby.Server.Implementations/Localization/Core/az.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/az.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/az.json b/Emby.Server.Implementations/Localization/Core/az.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/az.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 917244ab1dec5ea3aa4cb52392708b3555608c7f Mon Sep 17 00:00:00 2001 From: engineer948 Date: Mon, 22 Jun 2026 17:17:59 -0400 Subject: Translated using Weblate (Azerbaijani) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/az/ --- .../Localization/Core/az.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/az.json b/Emby.Server.Implementations/Localization/Core/az.json index 0967ef424b..6ab18c8534 100644 --- a/Emby.Server.Implementations/Localization/Core/az.json +++ b/Emby.Server.Implementations/Localization/Core/az.json @@ -1 +1,19 @@ -{} +{ + "Books": "Kitablar", + "HomeVideos": "Ev Videoları", + "Latest": "Ən son", + "MixedContent": "Qarışıq məzmun", + "Movies": "Filmlər", + "Music": "Musiqi", + "MusicVideos": "Musiqi Videoları", + "NameSeasonUnknown": "Mövsüm Naməlum", + "NewVersionIsAvailable": "Jellyfin Serverin yeni versiyası yükləmək üçün əlçatandır.", + "NotificationOptionApplicationUpdateAvailable": "Tətbiq yeniləməsi mövcuddur", + "NotificationOptionApplicationUpdateInstalled": "Tətbiq yeniləməsi quraşdırılıb", + "NotificationOptionAudioPlayback": "Audio oxutma başladı", + "NotificationOptionAudioPlaybackStopped": "Audio oxutma dayandırıldı", + "NotificationOptionCameraImageUploaded": "Kamera şəkli yükləndi", + "NotificationOptionInstallationFailed": "Quraşdırma uğursuzluğu", + "NotificationOptionNewLibraryContent": "Yeni məzmun əlavə edildi", + "NotificationOptionPluginError": "Plugin uğursuzluğu" +} -- cgit v1.2.3 From 987744529aadd3a5e0da60ae1e6dec0e6e8ea469 Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Tue, 23 Jun 2026 03:56:28 -0400 Subject: Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- Emby.Server.Implementations/Localization/Core/sk.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index afea835bd4..7ae8857e5d 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -106,5 +106,7 @@ "TaskDownloadMissingLyrics": "Stiahnuť chýbajúce texty piesní", "TaskDownloadMissingLyricsDescription": "Stiahne texty pre piesne", "CleanupUserDataTask": "Prečistiť používateľské dáta", - "CleanupUserDataTaskDescription": "Vyčistí všetky dáta používateľa (stav sledovania, stav obľúbených atď.) z médií, ktoré už neexistujú aspoň 90 dní." + "CleanupUserDataTaskDescription": "Vyčistí všetky dáta používateľa (stav sledovania, stav obľúbených atď.) z médií, ktoré už neexistujú aspoň 90 dní.", + "LyricDownloadFailureFromForItem": "Text piesne sa nepodarilo stiahnuť z {0} pre {1}", + "Original": "Originál" } -- cgit v1.2.3 From e26f4a10059feba50017a9b5dd1851a658e46298 Mon Sep 17 00:00:00 2001 From: theguymadmax Date: Wed, 24 Jun 2026 22:30:12 -0400 Subject: Fix Live TV tuner not releasing --- Emby.Server.Implementations/Session/SessionManager.cs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 18811ef3a9..19823dff37 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -343,6 +343,10 @@ namespace Emby.Server.Implementations.Session _activeLiveStreamSessions.TryRemove(liveStreamId, out _); } } + else + { + liveStreamNeedsToBeClosed = true; + } if (liveStreamNeedsToBeClosed) { -- cgit v1.2.3 From 1947296edd449e9a7244d18716fcb8ff0e9f0dc8 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 25 Jun 2026 19:32:36 +0200 Subject: Don't run heavy DB tasks while scan is running --- .../ScheduledTasks/Tasks/OptimizeDatabaseTask.cs | 16 +++++++++++++++- .../ScheduledTasks/Tasks/PeopleValidationTask.cs | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs index 92d7a3907a..8d133dc074 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Jellyfin.Database.Implementations; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -17,6 +18,7 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask private readonly ILogger _logger; private readonly ILocalizationManager _localization; private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider; + private readonly ILibraryManager _libraryManager; /// /// Initializes a new instance of the class. @@ -24,14 +26,17 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask /// Instance of the interface. /// Instance of the interface. /// Instance of the JellyfinDatabaseProvider that can be used for provider specific operations. + /// Instance of the interface. public OptimizeDatabaseTask( ILogger logger, ILocalizationManager localization, - IJellyfinDatabaseProvider jellyfinDatabaseProvider) + IJellyfinDatabaseProvider jellyfinDatabaseProvider, + ILibraryManager libraryManager) { _logger = logger; _localization = localization; _jellyfinDatabaseProvider = jellyfinDatabaseProvider; + _libraryManager = libraryManager; } /// @@ -68,6 +73,15 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask /// public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { + // Vacuuming/checkpointing requires an exclusive lock on the database. Running it while a library scan is in + // progress causes both operations to contend for the database and can stall the scan, so defer optimization + // until no scan is running. The task will run again on its next trigger. + if (_libraryManager.IsScanRunning) + { + _logger.LogInformation("Skipping database optimization because a library scan is currently running."); + return; + } + _logger.LogInformation("Optimizing and vacuuming jellyfin.db..."); try diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index 6e4e5c7808..2a38b8c446 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -71,6 +71,13 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask /// public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { + // People validation performs heavy database writes that contend with an active library scan. + // Defer it until the scan has finished; the task will run again on its next trigger. + if (_libraryManager.IsScanRunning) + { + return; + } + IProgress subProgress = new Progress((val) => progress.Report(val / 2)); await _libraryManager.ValidatePeopleAsync(subProgress, cancellationToken).ConfigureAwait(false); -- cgit v1.2.3 From b9db4566a74ec94bcd24e7333b9d0cc6156e2e25 Mon Sep 17 00:00:00 2001 From: cloudharps Date: Thu, 25 Jun 2026 02:57:41 -0400 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 5d64405d19..a210125d34 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -106,5 +106,7 @@ "TaskDownloadMissingLyrics": "누락된 가사 다운로드", "TaskDownloadMissingLyricsDescription": "가사 다운로드", "CleanupUserDataTask": "사용자 데이터 정리 작업", - "CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다." + "CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다.", + "LyricDownloadFailureFromForItem": "{1}에 대한 가사를 {0}에서 다운로드하지 못했습니다", + "Original": "원본" } -- cgit v1.2.3 From fa07a3abe89b6e0eb96a9f8d8a3eb57dea20ca2a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 26 Jun 2026 07:34:19 +0200 Subject: Skip backups whens can is running --- .../ScheduledTasks/Tasks/PeopleValidationTask.cs | 7 ++++++- .../FullSystemBackup/BackupService.cs | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs index 2a38b8c446..305f98790d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.ScheduledTasks.Tasks; @@ -20,6 +21,7 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IDbContextFactory _dbContextFactory; + private readonly ILogger _logger; /// /// Initializes a new instance of the class. @@ -27,11 +29,13 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization, IDbContextFactory dbContextFactory) + /// Instance of the interface. + public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization, IDbContextFactory dbContextFactory, ILogger logger) { _libraryManager = libraryManager; _localization = localization; _dbContextFactory = dbContextFactory; + _logger = logger; } /// @@ -75,6 +79,7 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask // Defer it until the scan has finished; the task will run again on its next trigger. if (_libraryManager.IsScanRunning) { + _logger.LogInformation("Skipping people validation because a library scan is currently running."); return; } diff --git a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs index a6dc5458ee..a534fa5fa0 100644 --- a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs +++ b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs @@ -12,6 +12,7 @@ using Jellyfin.Database.Implementations; using Jellyfin.Server.Implementations.StorageHelpers; using Jellyfin.Server.Implementations.SystemBackupService; using MediaBrowser.Controller; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.SystemBackupService; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -33,6 +34,7 @@ public class BackupService : IBackupService private readonly IServerApplicationPaths _applicationPaths; private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider; private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly ILibraryManager _libraryManager; private static readonly JsonSerializerOptions _serializerSettings = new JsonSerializerOptions(JsonSerializerDefaults.General) { AllowTrailingCommas = true, @@ -50,13 +52,15 @@ public class BackupService : IBackupService /// The application paths. /// The Jellyfin database Provider in use. /// The SystemManager. + /// Instance of the interface. public BackupService( ILogger logger, IDbContextFactory dbProvider, IServerApplicationHost applicationHost, IServerApplicationPaths applicationPaths, IJellyfinDatabaseProvider jellyfinDatabaseProvider, - IHostApplicationLifetime applicationLifetime) + IHostApplicationLifetime applicationLifetime, + ILibraryManager libraryManager) { _logger = logger; _dbProvider = dbProvider; @@ -64,6 +68,7 @@ public class BackupService : IBackupService _applicationPaths = applicationPaths; _jellyfinDatabaseProvider = jellyfinDatabaseProvider; _hostApplicationLifetime = applicationLifetime; + _libraryManager = libraryManager; } /// @@ -263,6 +268,14 @@ public class BackupService : IBackupService /// public async Task CreateBackupAsync(BackupOptionsDto backupOptions) { + // Creating a backup runs a database optimization and reads the entire database under a transaction, both of + // which heavily contend with an active library scan and could capture an inconsistent database state. + if (_libraryManager.IsScanRunning) + { + _logger.LogWarning("Cannot create a backup while a library scan is running."); + throw new InvalidOperationException("Cannot create a backup while a library scan is running. Please try again once the scan has finished."); + } + var manifest = new BackupManifest() { DateCreated = DateTime.UtcNow, -- cgit v1.2.3 From f398b6d08b46544f61523c6871624201a2b54dfc Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 26 Jun 2026 08:20:55 +0200 Subject: Fix localization lookup --- .../Localization/Core/en_US.json | 64 ---------------------- .../Localization/LocalizationManager.cs | 10 +++- .../Localization/LocalizationManagerTests.cs | 14 +++++ 3 files changed, 21 insertions(+), 67 deletions(-) delete mode 100644 Emby.Server.Implementations/Localization/Core/en_US.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/en_US.json b/Emby.Server.Implementations/Localization/Core/en_US.json deleted file mode 100644 index b093f73099..0000000000 --- a/Emby.Server.Implementations/Localization/Core/en_US.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "AppDeviceValues": "App: {0}, Device: {1}", - "Artists": "Artists", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "Books": "Books", - "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", - "Default": "Default", - "External": "External", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "Favorites": "Favorites", - "Folders": "Folders", - "Forced": "Forced", - "Genres": "Genres", - "HeaderContinueWatching": "Continue Watching", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", - "HearingImpaired": "Hearing Impaired", - "HomeVideos": "Home Videos", - "Inherit": "Inherit", - "LabelIpAddressValue": "IP address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}", - "MixedContent": "Mixed content", - "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music Videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "Original": "Original", - "Photos": "Photos", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", - "ScheduledTaskFailedWithName": "{0} failed", - "Shows": "Shows", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} to {1}", - "TvShows": "TV Shows", - "Undefined": "Undefined", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted" -} diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 843e35afcc..6971431155 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -566,11 +566,15 @@ namespace Emby.Server.Implementations.Localization private static string GetResourceFilename(string culture) { - var parts = culture.Split('-'); + // Region codes may use a '-' (BCP-47, e.g. "pt-BR") or '_' (e.g. "es_419", "ar_SA") separator. + // Normalize the casing (lower-case language, upper-case region) while preserving the separator + // so the result matches the embedded resource file name, which is case-sensitive. + var separatorIndex = culture.IndexOfAny(['-', '_']); - if (parts.Length == 2) + if (separatorIndex > 0) { - culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant(); + var separator = culture[separatorIndex]; + culture = culture[..separatorIndex].ToLowerInvariant() + separator + culture[(separatorIndex + 1)..].ToUpperInvariant(); } else { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 3b8fe5ca60..bdb726f06d 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -344,6 +344,20 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.NotEqual("Default", translated); } + [Fact] + public void GetLocalizedString_WithBcp47NormalizationToUppercaseRegion_ReturnsTranslation() + { + var localizationManager = Setup(new ServerConfiguration + { + UICulture = "en-US" + }); + + // he-IL normalizes to the underscore resource he_IL. The resource lookup is case-sensitive, + // so the region casing has to be preserved or the file is not found and we fall back to en-US. + var translated = localizationManager.GetLocalizedString("Books", "he-IL"); + Assert.Equal("ספרים", translated); + } + [Fact] public void GetServerLocalizedString_UsesServerCulture() { -- cgit v1.2.3 From c2cb18a9d1c936d069679052aa83ef7362849d91 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 26 Jun 2026 11:42:28 +0200 Subject: Fix local plugin registration --- Emby.Server.Implementations/ApplicationHost.cs | 11 +++++++++++ .../Books/ComicServiceRegistrator.cs | 23 ---------------------- 2 files changed, 11 insertions(+), 23 deletions(-) delete mode 100644 MediaBrowser.Providers/Books/ComicServiceRegistrator.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 14380c33bf..69e23bcb63 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -93,6 +93,9 @@ using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; +using MediaBrowser.Providers.Books; +using MediaBrowser.Providers.Books.ComicBookInfo; +using MediaBrowser.Providers.Books.ComicInfo; using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.ListenBrainz; @@ -496,6 +499,14 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + // register the generic local metadata provider for comic files + serviceCollection.AddSingleton(); + + // register the actual implementations of the local metadata provider for comic files + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(NetManager); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.Providers/Books/ComicServiceRegistrator.cs b/MediaBrowser.Providers/Books/ComicServiceRegistrator.cs deleted file mode 100644 index 0d096241d6..0000000000 --- a/MediaBrowser.Providers/Books/ComicServiceRegistrator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Providers.Books.ComicBookInfo; -using MediaBrowser.Providers.Books.ComicInfo; -using Microsoft.Extensions.DependencyInjection; - -namespace MediaBrowser.Providers.Books; - -/// -public class ComicServiceRegistrator : IPluginServiceRegistrator -{ - /// - public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost) - { - // register the generic local metadata provider for comic files - serviceCollection.AddSingleton(); - - // register the actual implementations of the local metadata provider for comic files - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - } -} -- cgit v1.2.3 From f2ed842b4be26966121945e7c46f00ed15023ed5 Mon Sep 17 00:00:00 2001 From: Manuel Cid Date: Fri, 26 Jun 2026 12:24:57 -0400 Subject: Translated using Weblate (Galician) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gl/ --- Emby.Server.Implementations/Localization/Core/gl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json index d3740130ee..a68db0076a 100644 --- a/Emby.Server.Implementations/Localization/Core/gl.json +++ b/Emby.Server.Implementations/Localization/Core/gl.json @@ -106,5 +106,6 @@ "TaskRefreshTrickplayImages": "Xerar miniaturas de previsualización", "TaskAudioNormalizationDescription": "Escanea ficheiros á procura de datos de normalización de volume.", "CleanupUserDataTask": "Tarefa de limpeza de datos dos usuarios", - "CleanupUserDataTaskDescription": "Limpa todos os datos do usuario (estado de visualización, de favorito etc.) dos medios ausentes polo menos 90 días." + "CleanupUserDataTaskDescription": "Limpa todos os datos do usuario (estado de visualización, de favorito etc.) dos medios ausentes polo menos 90 días.", + "Original": "Orixinal" } -- cgit v1.2.3