From f58b4860f7d295ea39ec64668e133579ac6154f9 Mon Sep 17 00:00:00 2001 From: desibooklover Date: Wed, 22 Oct 2025 15:09:11 -0400 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 813b18ad4..80db975cc 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -129,5 +129,12 @@ "TaskAudioNormalization": "श्रव्य सामान्यीकरण", "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें", "TaskDownloadMissingLyrics": "लापता गानों के बोल डाउनलोड करेँ", - "TaskDownloadMissingLyricsDescription": "गानों के बोल डाउनलोड करता है" + "TaskDownloadMissingLyricsDescription": "गानों के बोल डाउनलोड करता है", + "TaskExtractMediaSegments": "मीडिया सेगमेंट स्कैन", + "TaskExtractMediaSegmentsDescription": "मीडियासेगमेंट सक्षम प्लगइन्स से मीडिया सेगमेंट निकालता है या प्राप्त करता है।", + "TaskMoveTrickplayImages": "ट्रिकप्ले छवि स्थान माइग्रेट करें", + "TaskMoveTrickplayImagesDescription": "लाइब्रेरी सेटिंग्स के अनुसार मौजूदा ट्रिकप्ले फ़ाइलों को स्थानांतरित करता है।", + "TaskCleanCollectionsAndPlaylistsDescription": "संग्रहों और प्लेलिस्टों से उन आइटमों को हटाता है जो अब मौजूद नहीं हैं।", + "TaskCleanCollectionsAndPlaylists": "संग्रह और प्लेलिस्ट साफ़ करें", + "CleanupUserDataTask": "यूज़र डेटा की सफाई करता है।" } -- cgit v1.2.3 From d167d59c23765d2cc403fec778b53ea1b3dbe40d Mon Sep 17 00:00:00 2001 From: desibooklover Date: Wed, 22 Oct 2025 15:07:28 -0400 Subject: Translated using Weblate (Punjabi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pa/ --- Emby.Server.Implementations/Localization/Core/pa.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pa.json b/Emby.Server.Implementations/Localization/Core/pa.json index 6062d9700..ced9204b4 100644 --- a/Emby.Server.Implementations/Localization/Core/pa.json +++ b/Emby.Server.Implementations/Localization/Core/pa.json @@ -134,6 +134,8 @@ "TaskCleanCollectionsAndPlaylistsDescription": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਵਿੱਚੋਂ ਉਹ ਆਈਟਮ ਹਟਾਉਂਦਾ ਹੈ ਜੋ ਹੁਣ ਮੌਜੂਦ ਨਹੀਂ ਹਨ।", "TaskCleanCollectionsAndPlaylists": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਨੂੰ ਸਾਫ ਕਰੋ", "TaskAudioNormalization": "ਆਵਾਜ਼ ਸਧਾਰਣੀਕਰਨ", - "TaskRefreshTrickplayImagesDescription": "ਚਲ ਰਹੀ ਲਾਇਬ੍ਰੇਰੀਆਂ ਵਿੱਚ ਵੀਡੀਓਜ਼ ਲਈ ਟ੍ਰਿਕਪਲੇ ਪ੍ਰੀਵਿਊ ਬਣਾਉਂਦਾ ਹੈ।", - "TaskKeyframeExtractorDescription": "ਕੀ-ਫ੍ਰੇਮਜ਼ ਨੂੰ ਵੀਡੀਓ ਫਾਈਲਾਂ ਵਿੱਚੋਂ ਨਿਕਾਲਦਾ ਹੈ ਤਾਂ ਜੋ ਹੋਰ ਜ਼ਿਆਦਾ ਸਟਿਕ ਹੋਣ ਵਾਲੀਆਂ HLS ਪਲੇਲਿਸਟਾਂ ਬਣਾਈਆਂ ਜਾ ਸਕਣ। ਇਹ ਕੰਮ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲ ਸਕਦਾ ਹੈ।" + "TaskRefreshTrickplayImagesDescription": "ਵੀਡੀਓ ਲਈ ਟ੍ਰਿਕਪਲੇ ਪ੍ਰੀਵਿਊ ਬਣਾਉਂਦਾ ਹੈ (ਜੇ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਹੈ)।", + "TaskKeyframeExtractorDescription": "ਕੀ-ਫ੍ਰੇਮਜ਼ ਨੂੰ ਵੀਡੀਓ ਫਾਈਲਾਂ ਵਿੱਚੋਂ ਨਿਕਾਲਦਾ ਹੈ ਤਾਂ ਜੋ ਹੋਰ ਜ਼ਿਆਦਾ ਸਟਿਕ ਹੋਣ ਵਾਲੀਆਂ HLS ਪਲੇਲਿਸਟਾਂ ਬਣਾਈਆਂ ਜਾ ਸਕਣ। ਇਹ ਕੰਮ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲ ਸਕਦਾ ਹੈ।", + "CleanupUserDataTaskDescription": "ਘੱਟੋ-ਘੱਟ 90 ਦਿਨਾਂ ਤੋਂ ਮੌਜੂਦ ਨਾ ਹੋਣ ਵਾਲੇ ਮੀਡੀਆ ਤੋਂ ਸਾਰੇ ਉਪਭੋਗਤਾ ਡੇਟਾ (ਵਾਚ ਸਟੇਟ, ਮਨਪਸੰਦ ਸਟੇਟਸ ਆਦਿ) ਨੂੰ ਸਾਫ਼ ਕਰਦਾ ਹੈ।", + "CleanupUserDataTask": "ਯੂਜ਼ਰ ਡਾਟਾ ਸਾਫ਼ ਕਰਨ ਦਾ ਕੰਮ" } -- cgit v1.2.3 From c5affbbf71f9b8582d7ac9c88b294f50cc1a4fc7 Mon Sep 17 00:00:00 2001 From: HanHwanHo Date: Fri, 24 Oct 2025 16:54:43 -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 efc9f61dd..3d1b1ed27 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -136,5 +136,7 @@ "TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션", "TaskMoveTrickplayImagesDescription": "추출된 트릭플레이 이미지를 라이브러리 설정에 따라 이동합니다.", "TaskDownloadMissingLyrics": "누락된 가사 다운로드", - "TaskDownloadMissingLyricsDescription": "가사 다운로드" + "TaskDownloadMissingLyricsDescription": "가사 다운로드", + "CleanupUserDataTask": "사용자 데이터 정리 작업", + "CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다." } -- cgit v1.2.3 From cdc8325c7bb4d10a0f28a58135dcb59d512f2f09 Mon Sep 17 00:00:00 2001 From: Battseren Badral Date: Sat, 25 Oct 2025 13:20:45 -0400 Subject: Translated using Weblate (Mongolian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/ --- Emby.Server.Implementations/Localization/Core/mn.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json index 240059a3b..9202b65de 100644 --- a/Emby.Server.Implementations/Localization/Core/mn.json +++ b/Emby.Server.Implementations/Localization/Core/mn.json @@ -54,7 +54,7 @@ "TaskDownloadMissingSubtitles": "Алга болсон хадмал орчуулгыг татах", "External": "Гадны", "HeaderFavoriteArtists": "Дуртай уран бүтээлчид", - "HeaderFavoriteEpisodes": "Дуртай ангиуд", + "HeaderFavoriteEpisodes": "Хайртай Ангиуд", "HeaderFavoriteShows": "Дуртай нэвтрүүлэг", "HeaderFavoriteSongs": "Дуртай дуу", "AppDeviceValues": "Aпп: {0}, Төхөөрөмж: {1}", @@ -71,7 +71,7 @@ "Forced": "Хүчээр", "HeaderAlbumArtists": "Цомгийн уран бүтээлчид", "HeaderFavoriteAlbums": "Дуртай цомгууд", - "HeaderLiveTV": "Шууд", + "HeaderLiveTV": "Шууд ТВ", "HeaderRecordingGroups": "Бичлэгийн бүлгүүд", "HearingImpaired": "Сонсголын бэрхшээлтэй", "HomeVideos": "Үндсэн дүрсүүд", -- cgit v1.2.3 From efd659412f96ed120866dab350705e1735c085ae Mon Sep 17 00:00:00 2001 From: kreaxv Date: Mon, 27 Oct 2025 05:36:58 -0400 Subject: Translated using Weblate (Mongolian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/ --- Emby.Server.Implementations/Localization/Core/mn.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json index 9202b65de..cf39df706 100644 --- a/Emby.Server.Implementations/Localization/Core/mn.json +++ b/Emby.Server.Implementations/Localization/Core/mn.json @@ -3,7 +3,7 @@ "HeaderNextUp": "Дараа нь", "HeaderContinueWatching": "Үргэлжлүүлэн үзэх", "Songs": "Дуунууд", - "Playlists": "Playlist-ууд", + "Playlists": "Тоглуулах жагсаалтууд", "Movies": "Кинонууд", "Latest": "Сүүлийн үеийн", "Genres": "Төрлүүд", @@ -54,7 +54,7 @@ "TaskDownloadMissingSubtitles": "Алга болсон хадмал орчуулгыг татах", "External": "Гадны", "HeaderFavoriteArtists": "Дуртай уран бүтээлчид", - "HeaderFavoriteEpisodes": "Хайртай Ангиуд", + "HeaderFavoriteEpisodes": "Дуртай ангиуд", "HeaderFavoriteShows": "Дуртай нэвтрүүлэг", "HeaderFavoriteSongs": "Дуртай дуу", "AppDeviceValues": "Aпп: {0}, Төхөөрөмж: {1}", @@ -111,7 +111,7 @@ "Shows": "Шоу", "Sync": "Дахин", "System": "Систем", - "TvShows": "ТВ нэвтрүүлгүүд", + "TvShows": "Цуварлууд", "Undefined": "Танисангүй", "User": "Хэрэглэгч", "UserCreatedWithName": "Хэрэглэгч {0}-г үүсгэлээ", -- cgit v1.2.3 From cee16d47cb515f268fe38b27f387a1e1a044b2a7 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 27 Oct 2025 15:43:06 -0400 Subject: Backport pull request #15054 from jellyfin/release-10.11.z Speed-up trickplay migration Original-merge: ca830d5be7c7a173f91ae7521d43cb47484718f1 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index c9630b894..1510e537d 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -152,6 +152,10 @@ namespace Emby.Server.Implementations.IO /// public void MoveDirectory(string source, string destination) { + // Make sure parent directory of target exists + var parent = Directory.GetParent(destination); + parent?.Create(); + try { Directory.Move(source, destination); -- cgit v1.2.3 From 4c1c160990f82583742c129048ff3db375f01017 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:43:17 -0400 Subject: Backport pull request #15133 from jellyfin/release-10.11.z Play selected song first with instant mix Original-merge: 1520a697ad43f3f023608f8012cce1f52926b5fe Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Library/MusicManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index e0c8ae371..e19ad3ef6 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -28,7 +28,9 @@ namespace Emby.Server.Implementations.Library public IReadOnlyList GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions) { - return GetInstantMixFromGenres(item.Genres, user, dtoOptions); + var instantMixItems = GetInstantMixFromGenres(item.Genres, user, dtoOptions); + + return [item, .. instantMixItems.Where(i => !i.Id.Equals(item.Id))]; } /// -- cgit v1.2.3 From 6514196e8d975f0ba3e904c4ea7638d73ea603a4 Mon Sep 17 00:00:00 2001 From: CeruleanRed <64965209+CeruleanRed@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:43:21 -0400 Subject: Backport pull request #15176 from jellyfin/release-10.11.z Only save chapters that are within the runtime of the video file Original-merge: 442af96ed9c7b9cfadf46e85e8119ac0476408e0 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Chapters/ChapterManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Chapters/ChapterManager.cs b/Emby.Server.Implementations/Chapters/ChapterManager.cs index fea05931d..d09ed30ae 100644 --- a/Emby.Server.Implementations/Chapters/ChapterManager.cs +++ b/Emby.Server.Implementations/Chapters/ChapterManager.cs @@ -223,7 +223,7 @@ public class ChapterManager : IChapterManager if (saveChapters && changesMade) { - _chapterRepository.SaveChapters(video.Id, chapters); + SaveChapters(video, chapters); } DeleteDeadImages(currentImages, chapters); @@ -234,7 +234,9 @@ public class ChapterManager : IChapterManager /// public void SaveChapters(Video video, IReadOnlyList chapters) { - _chapterRepository.SaveChapters(video.Id, chapters); + // Remove any chapters that are outside of the runtime of the video + var validChapters = chapters.Where(c => c.StartPositionTicks < video.RunTimeTicks).ToList(); + _chapterRepository.SaveChapters(video.Id, validChapters); } /// -- cgit v1.2.3 From 4dc826644d213ee47f4181009be3c928448b0879 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 27 Oct 2025 15:43:26 -0400 Subject: Backport pull request #15197 from jellyfin/release-10.11.z Filter plugins by id instead of name Original-merge: 5691eee4f16402dfe528787666eef13678faaba0 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Updates/InstallationManager.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 678475b31..5ff400160 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -223,15 +223,14 @@ namespace Emby.Server.Implementations.Updates Guid id = default, Version? specificVersion = null) { - if (name is not null) - { - availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - } - if (!id.IsEmpty()) { availablePackages = availablePackages.Where(x => x.Id.Equals(id)); } + else if (name is not null) + { + availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + } if (specificVersion is not null) { -- cgit v1.2.3 From 423c2654c087e9c23fafe6766ccc7168b6b2dd3a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 27 Oct 2025 15:43:27 -0400 Subject: Backport pull request #15209 from jellyfin/release-10.11.z Improve symlink handling Original-merge: e5656af1f2e740c6e4f78f613d47d37567940ed8 Merged-by: crobibero Backported-by: Bond_009 --- .../IO/ManagedFileSystem.cs | 47 +++---- .../Library/DotIgnoreIgnoreRule.cs | 9 +- .../SymlinkFollowingPhysicalFileResultExecutor.cs | 151 --------------------- Jellyfin.Server/Startup.cs | 5 - 4 files changed, 27 insertions(+), 185 deletions(-) delete mode 100644 Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 1510e537d..97e89ca3d 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -252,47 +252,40 @@ namespace Emby.Server.Implementations.IO { result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory; - // if (!result.IsDirectory) - // { - // result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden; - // } - if (info is FileInfo fileInfo) { - result.Length = fileInfo.Length; - - // Issue #2354 get the size of files behind symbolic links. Also Enum.HasFlag is bad as it boxes! - if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) + result.CreationTimeUtc = GetCreationTimeUtc(info); + result.LastWriteTimeUtc = GetLastWriteTimeUtc(info); + if (fileInfo.LinkTarget is not null) { try { - using (var fileHandle = File.OpenHandle(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + var targetFileInfo = (FileInfo?)fileInfo.ResolveLinkTarget(returnFinalTarget: true); + if (targetFileInfo is not null) { - result.Length = RandomAccess.GetLength(fileHandle); + result.Exists = targetFileInfo.Exists; + if (result.Exists) + { + result.Length = targetFileInfo.Length; + result.CreationTimeUtc = GetCreationTimeUtc(targetFileInfo); + result.LastWriteTimeUtc = GetLastWriteTimeUtc(targetFileInfo); + } + } + else + { + result.Exists = false; } - } - catch (FileNotFoundException ex) - { - // Dangling symlinks cannot be detected before opening the file unfortunately... - _logger.LogError(ex, "Reading the file size of the symlink at {Path} failed. Marking the file as not existing.", fileInfo.FullName); - result.Exists = false; } catch (UnauthorizedAccessException ex) { _logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fileInfo.FullName); } - catch (IOException ex) - { - // IOException generally means the file is not accessible due to filesystem issues - // Catch this exception and mark the file as not exist to ignore it - _logger.LogError(ex, "Reading the file at {Path} failed due to an IO Exception. Marking the file as not existing", fileInfo.FullName); - result.Exists = false; - } + } + else + { + result.Length = fileInfo.Length; } } - - result.CreationTimeUtc = GetCreationTimeUtc(info); - result.LastWriteTimeUtc = GetLastWriteTimeUtc(info); } else { diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index bafe3ad43..959acd475 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -51,8 +51,7 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule } // Fast path in case the ignore files isn't a symlink and is empty - if ((dirIgnoreFile.Attributes & FileAttributes.ReparsePoint) == 0 - && dirIgnoreFile.Length == 0) + if (dirIgnoreFile.LinkTarget is null && dirIgnoreFile.Length == 0) { return true; } @@ -93,6 +92,12 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule private static string GetFileContent(FileInfo dirIgnoreFile) { + dirIgnoreFile = (FileInfo?)dirIgnoreFile.ResolveLinkTarget(returnFinalTarget: true) ?? dirIgnoreFile; + if (!dirIgnoreFile.Exists) + { + return string.Empty; + } + using (var reader = dirIgnoreFile.OpenText()) { return reader.ReadToEnd(); diff --git a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs deleted file mode 100644 index 910b5c467..000000000 --- a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs +++ /dev/null @@ -1,151 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) .NET Foundation and Contributors -// -// All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace Jellyfin.Server.Infrastructure -{ - /// - public class SymlinkFollowingPhysicalFileResultExecutor : PhysicalFileResultExecutor - { - /// - /// Initializes a new instance of the class. - /// - /// An instance of the interface. - public SymlinkFollowingPhysicalFileResultExecutor(ILoggerFactory loggerFactory) : base(loggerFactory) - { - } - - /// - protected override FileMetadata GetFileInfo(string path) - { - var fileInfo = new FileInfo(path); - var length = fileInfo.Length; - // This may or may not be fixed in .NET 6, but looks like it will not https://github.com/dotnet/aspnetcore/issues/34371 - if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) - { - using var fileHandle = File.OpenHandle(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - length = RandomAccess.GetLength(fileHandle); - } - - return new FileMetadata - { - Exists = fileInfo.Exists, - Length = length, - LastModified = fileInfo.LastWriteTimeUtc - }; - } - - /// - protected override async Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue? range, long rangeLength) - { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(result); - - if (range is not null && rangeLength == 0) - { - return; - } - - // It's a bit of wasted IO to perform this check again, but non-symlinks shouldn't use this code - if (!IsSymLink(result.FileName)) - { - await base.WriteFileAsync(context, result, range, rangeLength).ConfigureAwait(false); - return; - } - - var response = context.HttpContext.Response; - - if (range is not null) - { - await SendFileAsync( - result.FileName, - response, - offset: range.From ?? 0L, - count: rangeLength).ConfigureAwait(false); - return; - } - - await SendFileAsync( - result.FileName, - response, - offset: 0, - count: null).ConfigureAwait(false); - } - - private async Task SendFileAsync(string filePath, HttpResponse response, long offset, long? count, CancellationToken cancellationToken = default) - { - var fileInfo = GetFileInfo(filePath); - if (offset < 0 || offset > fileInfo.Length) - { - throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty); - } - - if (count.HasValue - && (count.Value < 0 || count.Value > fileInfo.Length - offset)) - { - throw new ArgumentOutOfRangeException(nameof(count), count, string.Empty); - } - - // Copied from SendFileFallback.SendFileAsync - const int BufferSize = 1024 * 16; - - var useRequestAborted = !cancellationToken.CanBeCanceled; - var localCancel = useRequestAborted ? response.HttpContext.RequestAborted : cancellationToken; - - var fileStream = new FileStream( - filePath, - FileMode.Open, - FileAccess.Read, - FileShare.ReadWrite, - bufferSize: BufferSize, - options: FileOptions.Asynchronous | FileOptions.SequentialScan); - await using (fileStream.ConfigureAwait(false)) - { - try - { - localCancel.ThrowIfCancellationRequested(); - fileStream.Seek(offset, SeekOrigin.Begin); - await StreamCopyOperation - .CopyToAsync(fileStream, response.Body, count, BufferSize, localCancel) - .ConfigureAwait(true); - } - catch (OperationCanceledException) when (useRequestAborted) - { - } - } - } - - private static bool IsSymLink(string path) => (File.GetAttributes(path) & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; - } -} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index aa8f6dd1c..5032b2aec 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -16,15 +16,12 @@ using Jellyfin.Networking.HappyEyeballs; using Jellyfin.Server.Extensions; using Jellyfin.Server.HealthChecks; using Jellyfin.Server.Implementations.Extensions; -using Jellyfin.Server.Infrastructure; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Extensions; using MediaBrowser.XbmcMetadata; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -69,8 +66,6 @@ namespace Jellyfin.Server options.HttpsPort = _serverApplicationHost.HttpsPort; }); - // TODO remove once this is fixed upstream https://github.com/dotnet/aspnetcore/issues/34371 - services.AddSingleton, SymlinkFollowingPhysicalFileResultExecutor>(); services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetworkConfiguration()); services.AddJellyfinDbContext(_serverApplicationHost.ConfigurationManager, _configuration); services.AddJellyfinApiSwagger(); -- cgit v1.2.3 From 7d778d7befd271f08d202494c92bc9c3666c18ec Mon Sep 17 00:00:00 2001 From: Pascal Wiesmann Date: Mon, 27 Oct 2025 18:03:16 -0400 Subject: Translated using Weblate (Alemannic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gsw/ --- Emby.Server.Implementations/Localization/Core/gsw.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index f847d83d1..e1ee8cf7c 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -11,7 +11,7 @@ "Collections": "Sammlungen", "DeviceOfflineWithName": "{0} wurde getrennt", "DeviceOnlineWithName": "{0} ist verbunden", - "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", + "FailedLoginAttemptWithUserName": "Fählgschlagene Ameldeversuech vo {0}", "Favorites": "Favorite", "Folders": "Ordner", "Genres": "Genre", -- cgit v1.2.3 From f92eca3efb2b01b5c5b4ab862ede2620bdf214c9 Mon Sep 17 00:00:00 2001 From: Battseren Badral Date: Tue, 28 Oct 2025 14:43:19 -0400 Subject: Translated using Weblate (Mongolian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/ --- Emby.Server.Implementations/Localization/Core/mn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json index cf39df706..ec553f5be 100644 --- a/Emby.Server.Implementations/Localization/Core/mn.json +++ b/Emby.Server.Implementations/Localization/Core/mn.json @@ -111,7 +111,7 @@ "Shows": "Шоу", "Sync": "Дахин", "System": "Систем", - "TvShows": "Цуварлууд", + "TvShows": "ТВ нэвтрүүлгүүд", "Undefined": "Танисангүй", "User": "Хэрэглэгч", "UserCreatedWithName": "Хэрэглэгч {0}-г үүсгэлээ", -- cgit v1.2.3 From f21fe9f95ea8f8b0da5f7ce315feab4c0b4925dd Mon Sep 17 00:00:00 2001 From: Jacky He Date: Wed, 29 Oct 2025 17:13:58 -0400 Subject: Translated using Weblate (Chinese (Traditional Han script, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 39141d841..5e17e4647 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -137,5 +137,6 @@ "TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。", "TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。", "TaskMoveTrickplayImages": "轉移 Trickplay 影像位置", - "CleanupUserDataTask": "用戶資料清理工作" + "CleanupUserDataTask": "用戶資料清理工作", + "CleanupUserDataTaskDescription": "從用戶數據中清除已經被刪除超過 90 日的媒體相關資料。" } -- cgit v1.2.3 From 573ce9ceaac41d35b9e9431457e66ce06bf17e77 Mon Sep 17 00:00:00 2001 From: Battseren Badral Date: Wed, 29 Oct 2025 16:20:27 -0400 Subject: Translated using Weblate (Mongolian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mn/ --- Emby.Server.Implementations/Localization/Core/mn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json index ec553f5be..a684ff204 100644 --- a/Emby.Server.Implementations/Localization/Core/mn.json +++ b/Emby.Server.Implementations/Localization/Core/mn.json @@ -109,7 +109,7 @@ "ScheduledTaskStartedWithName": "{0}-г эхлүүлэв", "ServerNameNeedsToBeRestarted": "{0}-г дахин асаана уу", "Shows": "Шоу", - "Sync": "Дахин", + "Sync": "Синхрончлох", "System": "Систем", "TvShows": "ТВ нэвтрүүлгүүд", "Undefined": "Танисангүй", -- cgit v1.2.3 From 23929a3e709f4324d49271c02b0b047e1149e860 Mon Sep 17 00:00:00 2001 From: Jacky He Date: Thu, 30 Oct 2025 12:24:01 -0400 Subject: Translated using Weblate (Chinese (Traditional Han script, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 5e17e4647..60af2274e 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -23,7 +23,7 @@ "HeaderFavoriteShows": "最愛的節目", "HeaderFavoriteSongs": "最愛的歌曲", "HeaderLiveTV": "電視直播", - "HeaderNextUp": "接著播放", + "HeaderNextUp": "繼續觀看", "HeaderRecordingGroups": "錄製組", "HomeVideos": "家庭影片", "Inherit": "繼承", -- cgit v1.2.3 From ffe82be7a7a2ea6ab6d0e543f58ecd01df4085ef Mon Sep 17 00:00:00 2001 From: Jacky He Date: Fri, 31 Oct 2025 17:12:15 -0400 Subject: Translated using Weblate (Chinese (Traditional Han script, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 60af2274e..c8800e256 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -127,8 +127,8 @@ "HearingImpaired": "聽力障礙", "TaskRefreshTrickplayImages": "建立 Trickplay 圖像", "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。", - "TaskExtractMediaSegments": "掃描媒體段落", - "TaskExtractMediaSegmentsDescription": "從MediaSegment中被允許的插件獲取媒體段落。", + "TaskExtractMediaSegments": "掃描媒體分段資訊", + "TaskExtractMediaSegmentsDescription": "從允許MediaSegment 功能的插件中獲取媒體片段。", "TaskDownloadMissingLyrics": "下載欠缺歌詞", "TaskDownloadMissingLyricsDescription": "下載歌詞", "TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單", -- cgit v1.2.3 From f693c9d39f00317e33153061a0c406aa8a607555 Mon Sep 17 00:00:00 2001 From: Diogo Coelho Date: Thu, 6 Nov 2025 04:13:50 -0500 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 | 2 +- 1 file changed, 1 insertion(+), 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 f188822d6..c3eba362d 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -5,7 +5,7 @@ "Artists": "Artistas", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", "Books": "Livros", - "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", + "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "Channels": "Canais", "ChapterNameValue": "Capítulo {0}", "Collections": "Coleções", -- cgit v1.2.3 From b216a27bfcb2476bb346e8f003dec3b6cbc91991 Mon Sep 17 00:00:00 2001 From: Grant Alexander Date: Wed, 12 Nov 2025 17:32:35 -0500 Subject: Translated using Weblate (English (Pirate)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en@pirate/ --- Emby.Server.Implementations/Localization/Core/pr.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json index 9076b9c87..7b1cd886f 100644 --- a/Emby.Server.Implementations/Localization/Core/pr.json +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -16,7 +16,7 @@ "Collections": "Barrels", "ItemAddedWithName": "{0} is now with yer treasure", "Default": "Normal-like", - "FailedLoginAttemptWithUserName": "Ye failed to get in, try from {0}", + "FailedLoginAttemptWithUserName": "Ye failed to enter from {0}", "Favorites": "Finest Loot", "ItemRemovedWithName": "{0} was taken from yer treasure", "LabelIpAddressValue": "Ship's coordinates: {0}", @@ -113,5 +113,10 @@ "TaskCleanCache": "Sweep the Cache Chest", "TaskRefreshChapterImages": "Claim chapter portraits", "TaskRefreshChapterImagesDescription": "Paints wee portraits fer videos that own chapters.", - "TaskRefreshLibrary": "Scan the Treasure Trove" + "TaskRefreshLibrary": "Scan the Treasure Trove", + "TasksChannelsCategory": "Channels of the Internet", + "TaskRefreshTrickplayImages": "Summon the picture tricks", + "TaskRefreshTrickplayImagesDescription": "Summons picture trick previews for videos in ye enabled book roost", + "TaskUpdatePlugins": "Resummon yer Plugins", + "TaskCleanTranscode": "Swab Ye Transcode Directory" } -- cgit v1.2.3 From d1722936c0a453c5b70bca96a8f148b11d2e0688 Mon Sep 17 00:00:00 2001 From: hoanghuy309 Date: Fri, 14 Nov 2025 23:56:11 -0500 Subject: Translated using Weblate (Vietnamese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/ --- Emby.Server.Implementations/Localization/Core/vi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index d1c5166cb..3f4bf1f7f 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -39,7 +39,7 @@ "TasksMaintenanceCategory": "Bảo Trì", "VersionNumber": "Phiên Bản {0}", "ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn", - "UserStoppedPlayingItemWithValues": "{0} đã phát xong {1} trên {2}", + "UserStoppedPlayingItemWithValues": "{0} đã kết thúc phát {1} trên {2}", "UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}", "UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}", "UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}", -- cgit v1.2.3 From 06fb300cff5d63404a888209ab82b192b9e46be4 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:08:45 -0500 Subject: Backport pull request #14955 from jellyfin/release-10.11.z Fix tmdbid not detected in single movie folder Original-merge: def5956cd1afe8848c0e232fa477720c4158832f Merged-by: crobibero Backported-by: Bond_009 --- .../Library/Resolvers/Movies/MovieResolver.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 333c8c34b..98e8f5350 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -369,13 +369,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies // We need to only look at the name of this actual item (not parents) var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path.AsSpan()) : Path.GetFileName(item.ContainingFolderPath.AsSpan()); - if (!justName.IsEmpty) + var tmdbid = justName.GetAttributeValue("tmdbid"); + + // If not in a mixed folder and ID not found in folder path, check filename + if (string.IsNullOrEmpty(tmdbid) && !item.IsInMixedFolder) { - // Check for TMDb id - var tmdbid = justName.GetAttributeValue("tmdbid"); - item.TrySetProviderId(MetadataProvider.Tmdb, tmdbid); + tmdbid = Path.GetFileName(item.Path.AsSpan()).GetAttributeValue("tmdbid"); } + item.TrySetProviderId(MetadataProvider.Tmdb, tmdbid); + if (!string.IsNullOrEmpty(item.Path)) { // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name) -- cgit v1.2.3 From 5ea3910af96ead74d9267ec239e47a798a33e78f Mon Sep 17 00:00:00 2001 From: revam Date: Mon, 17 Nov 2025 14:08:47 -0500 Subject: Backport pull request #15263 from jellyfin/release-10.11.z Resolve symlinks for static media source infos Original-merge: 3b2d64995aab63ebaa6832c059a3cc0bdebe90dc Merged-by: crobibero Backported-by: Bond_009 --- .../IO/ManagedFileSystem.cs | 3 +- .../Library/DotIgnoreIgnoreRule.cs | 3 +- .../Trickplay/TrickplayManager.cs | 4 +- MediaBrowser.Controller/Entities/BaseItem.cs | 12 +++- MediaBrowser.Controller/IO/FileSystemHelper.cs | 74 ++++++++++++++++++++++ 5 files changed, 91 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 97e89ca3d..fad97344b 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Security; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; @@ -260,7 +261,7 @@ namespace Emby.Server.Implementations.IO { try { - var targetFileInfo = (FileInfo?)fileInfo.ResolveLinkTarget(returnFinalTarget: true); + var targetFileInfo = FileSystemHelper.ResolveLinkTarget(fileInfo, returnFinalTarget: true); if (targetFileInfo is not null) { result.Exists = targetFileInfo.Exists; diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index 959acd475..e53502046 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -1,6 +1,7 @@ using System; using System.IO; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.IO; @@ -92,7 +93,7 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule private static string GetFileContent(FileInfo dirIgnoreFile) { - dirIgnoreFile = (FileInfo?)dirIgnoreFile.ResolveLinkTarget(returnFinalTarget: true) ?? dirIgnoreFile; + dirIgnoreFile = FileSystemHelper.ResolveLinkTarget(dirIgnoreFile, returnFinalTarget: true) ?? dirIgnoreFile; if (!dirIgnoreFile.Exists) { return string.Empty; diff --git a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs index 6f2d2a107..4505a377c 100644 --- a/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs +++ b/Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs @@ -254,10 +254,10 @@ public class TrickplayManager : ITrickplayManager } // We support video backdrops, but we should not generate trickplay images for them - var parentDirectory = Directory.GetParent(mediaPath); + var parentDirectory = Directory.GetParent(video.Path); if (parentDirectory is not null && string.Equals(parentDirectory.Name, "backdrops", StringComparison.OrdinalIgnoreCase)) { - _logger.LogDebug("Ignoring backdrop media found at {Path} for item {ItemID}", mediaPath, video.Id); + _logger.LogDebug("Ignoring backdrop media found at {Path} for item {ItemID}", video.Path, video.Id); return; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 4989f0f3f..3c46d53e5 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -24,6 +24,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaSegments; using MediaBrowser.Controller.Persistence; @@ -1127,6 +1128,15 @@ namespace MediaBrowser.Controller.Entities var protocol = item.PathProtocol; + // Resolve the item path so everywhere we use the media source it will always point to + // the correct path even if symlinks are in use. Calling ResolveLinkTarget on a non-link + // path will return null, so it's safe to check for all paths. + var itemPath = item.Path; + if (protocol is MediaProtocol.File && FileSystemHelper.ResolveLinkTarget(itemPath, returnFinalTarget: true) is { Exists: true } linkInfo) + { + itemPath = linkInfo.FullName; + } + var info = new MediaSourceInfo { Id = item.Id.ToString("N", CultureInfo.InvariantCulture), @@ -1134,7 +1144,7 @@ namespace MediaBrowser.Controller.Entities MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id), Name = GetMediaSourceName(item), - Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path, + Path = enablePathSubstitution ? GetMappedPath(item, itemPath, protocol) : itemPath, RunTimeTicks = item.RunTimeTicks, Container = item.Container, Size = item.Size, diff --git a/MediaBrowser.Controller/IO/FileSystemHelper.cs b/MediaBrowser.Controller/IO/FileSystemHelper.cs index 1a33c3aa8..324aea7e3 100644 --- a/MediaBrowser.Controller/IO/FileSystemHelper.cs +++ b/MediaBrowser.Controller/IO/FileSystemHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Model.IO; @@ -61,4 +62,77 @@ public static class FileSystemHelper } } } + + /// + /// Gets the target of the specified file link. + /// + /// + /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128. + /// + /// The path of the file link. + /// true to follow links to the final target; false to return the immediate next link. + /// + /// A if the is a link, regardless of if the target exists; otherwise, null. + /// + public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false) + { + // Check if the file exists so the native resolve handler won't throw at us. + if (!File.Exists(linkPath)) + { + return null; + } + + if (!returnFinalTarget) + { + return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo; + } + + if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo) + { + return null; + } + + var currentPath = targetInfo.FullName; + var visited = new HashSet(StringComparer.Ordinal) { linkPath, currentPath }; + while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo) + { + var targetPath = linkInfo.FullName; + + // If an infinite loop is detected, return the file info for the + // first link in the loop we encountered. + if (!visited.Add(targetPath)) + { + return new FileInfo(targetPath); + } + + targetInfo = linkInfo; + currentPath = targetPath; + + // Exit if the target doesn't exist, so the native resolve handler won't throw at us. + if (!targetInfo.Exists) + { + break; + } + } + + return targetInfo; + } + + /// + /// Gets the target of the specified file link. + /// + /// + /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128. + /// + /// The file info of the file link. + /// true to follow links to the final target; false to return the immediate next link. + /// + /// A if the is a link, regardless of if the target exists; otherwise, null. + /// + public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false) + { + ArgumentNullException.ThrowIfNull(fileInfo); + + return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget); + } } -- cgit v1.2.3 From e34e7a1d0b9d1a1ed121fcf4feea441571a5c13b Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:08:57 -0500 Subject: Backport pull request #15423 from jellyfin/release-10.11.z Invalidate parent folder's cache on deletion/creation Original-merge: 49efd68fc7ef4b70b38151a177502bbdb3adede0 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Library/LibraryManager.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a400cb092..cab87e53d 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -457,6 +457,12 @@ namespace Emby.Server.Implementations.Library _cache.TryRemove(child.Id, out _); } + if (parent is Folder folder) + { + folder.Children = null; + folder.UserData = null; + } + ReportItemRemoved(item, parent); } @@ -1993,6 +1999,12 @@ namespace Emby.Server.Implementations.Library RegisterItem(item); } + if (parent is Folder folder) + { + folder.Children = null; + folder.UserData = null; + } + if (ItemAdded is not null) { foreach (var item in items) @@ -2150,6 +2162,12 @@ namespace Emby.Server.Implementations.Library _itemRepository.SaveItems(items, cancellationToken); + if (parent is Folder folder) + { + folder.Children = null; + folder.UserData = null; + } + if (ItemUpdated is not null) { foreach (var item in items) -- cgit v1.2.3 From e51680cf56f423c0401dad87e030be48337af5cb Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:09:02 -0500 Subject: Backport pull request #15462 from jellyfin/release-10.11.z Fix NullReferenceException in GetPathProtocol when path is null Original-merge: 7c1063177f5647e07c634d36694f0bdec8fe4ff1 Merged-by: joshuaboniface Backported-by: Bond_009 --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 750346169..c667fb060 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -226,6 +226,11 @@ namespace Emby.Server.Implementations.Library /// > public MediaProtocol GetPathProtocol(string path) { + if (string.IsNullOrEmpty(path)) + { + return MediaProtocol.File; + } + if (path.StartsWith("Rtsp", StringComparison.OrdinalIgnoreCase)) { return MediaProtocol.Rtsp; -- cgit v1.2.3 From d7f628677e45cca6a4c34b57ff37a4d81e19277f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 17 Nov 2025 14:09:03 -0500 Subject: Backport pull request #15466 from jellyfin/release-10.11.z Don't error out when searching for marker files fails Original-merge: f4a846aa4dcffb3be7b701f806b24cb8dd6b7c5d Merged-by: crobibero Backported-by: Bond_009 --- .../AppBase/BaseApplicationPaths.cs | 14 ++++++++++++-- MediaBrowser.Common/Configuration/IApplicationPaths.cs | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index c69bcfef7..de722332a 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -107,10 +107,20 @@ namespace Emby.Server.Implementations.AppBase private void CheckOrCreateMarker(string path, string markerName, bool recursive = false) { - var otherMarkers = GetMarkers(path, recursive).FirstOrDefault(e => Path.GetFileName(e) != markerName); + string? otherMarkers = null; + try + { + otherMarkers = GetMarkers(path, recursive).FirstOrDefault(e => !Path.GetFileName(e.AsSpan()).Equals(markerName, StringComparison.OrdinalIgnoreCase)); + } + catch + { + // Error while checking for marker files, assume none exist and keep going + // TODO: add some logging + } + if (otherMarkers is not null) { - throw new InvalidOperationException($"Exepected to find only {markerName} but found marker for {otherMarkers}."); + throw new InvalidOperationException($"Expected to find only {markerName} but found marker for {otherMarkers}."); } var markerPath = Path.Combine(path, markerName); diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index 6d1a72b04..3a6197490 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -103,11 +103,11 @@ namespace MediaBrowser.Common.Configuration void MakeSanityCheckOrThrow(); /// - /// Checks and creates the given path and adds it with a marker file if non existant. + /// Checks and creates the given path and adds it with a marker file if non existent. /// /// The path to check. /// The common marker file name. - /// Check for other settings paths recursivly. + /// Check for other settings paths recursively. void CreateAndCheckMarker(string path, string markerName, bool recursive = false); } } -- cgit v1.2.3 From a08b6ac2664003cb89804ce192f9ae3f3acb4be9 Mon Sep 17 00:00:00 2001 From: CBPJ Date: Mon, 17 Nov 2025 14:09:07 -0500 Subject: Backport pull request #15487 from jellyfin/release-10.11.z Fix gitignore-style not working properly on windows. Original-merge: 435bb14bb266916e9c6f100c4324a94c36126e06 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index e53502046..46e60dbaa 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Resolvers; @@ -88,6 +89,13 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule var ignore = new Ignore.Ignore(); ignore.Add(ignoreRules); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Mitigate the problem of the Ignore library not handling Windows paths correctly. + // See https://github.com/jellyfin/jellyfin/issues/15484 + return ignore.IsIgnored(fileInfo.FullName.NormalizePath('/')); + } + return ignore.IsIgnored(fileInfo.FullName); } -- cgit v1.2.3 From 5b3f29946b5628b975578c1ef4a15a3248cd650a Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:09:09 -0500 Subject: Backport pull request #15501 from jellyfin/release-10.11.z Fix .ignore handling for directories Original-merge: e8150428b62668e062a3432960f98684d3b352cb Merged-by: crobibero Backported-by: Bond_009 --- .../Library/DotIgnoreIgnoreRule.cs | 97 +++++++++------------- 1 file changed, 39 insertions(+), 58 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index 46e60dbaa..473ff8e1d 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Resolvers; @@ -13,28 +12,24 @@ namespace Emby.Server.Implementations.Library; /// public class DotIgnoreIgnoreRule : IResolverIgnoreRule { + private static readonly bool IsWindows = OperatingSystem.IsWindows(); + private static FileInfo? FindIgnoreFile(DirectoryInfo directory) { - var ignoreFile = new FileInfo(Path.Join(directory.FullName, ".ignore")); - if (ignoreFile.Exists) - { - return ignoreFile; - } - - var parentDir = directory.Parent; - if (parentDir is null) + for (var current = directory; current is not null; current = current.Parent) { - return null; + var ignorePath = Path.Join(current.FullName, ".ignore"); + if (File.Exists(ignorePath)) + { + return new FileInfo(ignorePath); + } } - return FindIgnoreFile(parentDir); + return null; } /// - public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) - { - return IsIgnored(fileInfo, parent); - } + public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent); /// /// Checks whether or not the file is ignored. @@ -44,72 +39,58 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule /// True if the file should be ignored. public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent) { - if (fileInfo.IsDirectory) - { - var dirIgnoreFile = FindIgnoreFile(new DirectoryInfo(fileInfo.FullName)); - if (dirIgnoreFile is null) - { - return false; - } - - // Fast path in case the ignore files isn't a symlink and is empty - if (dirIgnoreFile.LinkTarget is null && dirIgnoreFile.Length == 0) - { - return true; - } - - // ignore the directory only if the .ignore file is empty - // evaluate individual files otherwise - return string.IsNullOrWhiteSpace(GetFileContent(dirIgnoreFile)); - } + var searchDirectory = fileInfo.IsDirectory + ? new DirectoryInfo(fileInfo.FullName) + : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty); - var parentDirPath = Path.GetDirectoryName(fileInfo.FullName); - if (string.IsNullOrEmpty(parentDirPath)) + if (string.IsNullOrEmpty(searchDirectory.FullName)) { return false; } - var folder = new DirectoryInfo(parentDirPath); - var ignoreFile = FindIgnoreFile(folder); + var ignoreFile = FindIgnoreFile(searchDirectory); if (ignoreFile is null) { return false; } - string ignoreFileString = GetFileContent(ignoreFile); - - if (string.IsNullOrWhiteSpace(ignoreFileString)) + // Fast path in case the ignore files isn't a symlink and is empty + if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0) { // Ignore directory if we just have the file return true; } + var content = GetFileContent(ignoreFile); + return string.IsNullOrWhiteSpace(content) + || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory); + } + + private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory) + { // If file has content, base ignoring off the content .gitignore-style rules - var ignoreRules = ignoreFileString.Split('\n', StringSplitOptions.RemoveEmptyEntries); + var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); var ignore = new Ignore.Ignore(); - ignore.Add(ignoreRules); + ignore.Add(rules); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // Mitigate the problem of the Ignore library not handling Windows paths correctly. + // See https://github.com/jellyfin/jellyfin/issues/15484 + var pathToCheck = IsWindows ? path.NormalizePath('/') : path; + + // Add trailing slash for directories to match "folder/" + if (isDirectory) { - // Mitigate the problem of the Ignore library not handling Windows paths correctly. - // See https://github.com/jellyfin/jellyfin/issues/15484 - return ignore.IsIgnored(fileInfo.FullName.NormalizePath('/')); + pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/"); } - return ignore.IsIgnored(fileInfo.FullName); + return ignore.IsIgnored(pathToCheck); } - private static string GetFileContent(FileInfo dirIgnoreFile) + private static string GetFileContent(FileInfo ignoreFile) { - dirIgnoreFile = FileSystemHelper.ResolveLinkTarget(dirIgnoreFile, returnFinalTarget: true) ?? dirIgnoreFile; - if (!dirIgnoreFile.Exists) - { - return string.Empty; - } - - using (var reader = dirIgnoreFile.OpenText()) - { - return reader.ReadToEnd(); - } + ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile; + return ignoreFile.Exists + ? File.ReadAllText(ignoreFile.FullName) + : string.Empty; } } -- cgit v1.2.3 From 7e25089c0868d780a8285028590c7e866fe7895b Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:09:12 -0500 Subject: Backport pull request #15508 from jellyfin/release-10.11.z Fix playlist DateCreated and DateLastMediaAdded not being set Original-merge: 078f9584ed3622eed3516488026cbb6e42242bba Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 1 + MediaBrowser.Providers/Manager/MetadataService.cs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index c9d76df0b..1577c5c9c 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -244,6 +244,7 @@ namespace Emby.Server.Implementations.Playlists // Update the playlist in the repository playlist.LinkedChildren = [.. playlist.LinkedChildren, .. childrenToAdd]; + playlist.DateLastMediaAdded = DateTime.UtcNow; await UpdatePlaylistInternal(playlist).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 21767af84..c8435aa68 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -340,7 +340,10 @@ namespace MediaBrowser.Providers.Manager item.DateModified = info.LastWriteTimeUtc; if (ServerConfigurationManager.GetMetadataConfiguration().UseFileCreationTimeForDateAdded) { - item.DateCreated = info.CreationTimeUtc; + if (info.CreationTimeUtc > DateTime.MinValue) + { + item.DateCreated = info.CreationTimeUtc; + } } if (item is Video video) -- cgit v1.2.3 From 55dbff8f30a26f44d5099601cee016d58b90307f Mon Sep 17 00:00:00 2001 From: Rufis72 Date: Mon, 17 Nov 2025 23:43:06 -0500 Subject: Translated using Weblate (English (Pirate)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en@pirate/ --- Emby.Server.Implementations/Localization/Core/pr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json index 7b1cd886f..fee7e65f1 100644 --- a/Emby.Server.Implementations/Localization/Core/pr.json +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -114,7 +114,7 @@ "TaskRefreshChapterImages": "Claim chapter portraits", "TaskRefreshChapterImagesDescription": "Paints wee portraits fer videos that own chapters.", "TaskRefreshLibrary": "Scan the Treasure Trove", - "TasksChannelsCategory": "Channels of the Internet", + "TasksChannelsCategory": "Channels o' thy Internet", "TaskRefreshTrickplayImages": "Summon the picture tricks", "TaskRefreshTrickplayImagesDescription": "Summons picture trick previews for videos in ye enabled book roost", "TaskUpdatePlugins": "Resummon yer Plugins", -- cgit v1.2.3 From c08e81c52b7787deab799cb7ac96cfdedc2818b7 Mon Sep 17 00:00:00 2001 From: rimasx Date: Sun, 23 Nov 2025 00:57:01 -0500 Subject: Translated using Weblate (Estonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/ --- Emby.Server.Implementations/Localization/Core/et.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index a3f9dc2f8..2e692009b 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -137,5 +137,5 @@ "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meediasegmendid MediaSegment'i lubavatest pluginatest.", "TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht", "CleanupUserDataTask": "Puhasta kasutajaandmed", - "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mis pole enam vähemalt 90 päeva saadaval olnud." + "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud." } -- cgit v1.2.3 From 80e1e4294761a7a85bb405d3d1184121114b506b Mon Sep 17 00:00:00 2001 From: Martín Date: Fri, 28 Nov 2025 15:01:20 -0500 Subject: Added translation using Weblate (Occitan) --- Emby.Server.Implementations/Localization/Core/oc.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/oc.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/oc.json b/Emby.Server.Implementations/Localization/Core/oc.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/oc.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 691c194152df841e4ebd753b1c3a0d75e5d13e79 Mon Sep 17 00:00:00 2001 From: Hasan Abdulaal Date: Tue, 2 Dec 2025 06:11:42 -0500 Subject: Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index a92148caf..24ed116f3 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -16,7 +16,7 @@ "Folders": "المجلدات", "Genres": "التصنيفات", "HeaderAlbumArtists": "فناني الألبوم", - "HeaderContinueWatching": "إستئناف المشاهدة", + "HeaderContinueWatching": "أكمل المشاهدة", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteEpisodes": "الحلقات المفضلة", -- cgit v1.2.3 From e4daaf0d8330ab1e8abadcb927b03b5ded08895a Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:04:17 -0500 Subject: Backport pull request #15548 from jellyfin/release-10.11.z Fix NullReferenceException in filesystem path comparison Original-merge: 5ae444d96d473ba42c4a812c3f366b0faa6ebef4 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index fad97344b..4d68cb444 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -497,8 +497,17 @@ namespace Emby.Server.Implementations.IO /// public virtual bool AreEqual(string path1, string path2) { - return Path.TrimEndingDirectorySeparator(path1).Equals( - Path.TrimEndingDirectorySeparator(path2), + if (string.IsNullOrWhiteSpace(path1) || string.IsNullOrWhiteSpace(path2)) + { + return false; + } + + var normalized1 = Path.TrimEndingDirectorySeparator(path1); + var normalized2 = Path.TrimEndingDirectorySeparator(path2); + + return string.Equals( + normalized1, + normalized2, _isEnvironmentCaseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } -- cgit v1.2.3 From 873f1d9e8344d6c205e15d1be1c3af5514351a54 Mon Sep 17 00:00:00 2001 From: Furqaan Dawood Date: Thu, 4 Dec 2025 09:41:47 -0500 Subject: Added translation using Weblate (Swahili) --- Emby.Server.Implementations/Localization/Core/sw.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/sw.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sw.json b/Emby.Server.Implementations/Localization/Core/sw.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/sw.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 8b2a8b94b6361e31eff58078225cf78d8a6c3fb1 Mon Sep 17 00:00:00 2001 From: evan314159 <110177090+evan314159@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:15:46 +0800 Subject: avoid Take(0) when limit == 0 (#14608) Co-authored-by: Evan --- .../Library/SearchEngine.cs | 2 +- Emby.Server.Implementations/TV/TVSeriesManager.cs | 2 +- .../Devices/DeviceManager.cs | 2 +- .../Item/BaseItemRepository.cs | 44 +++++++++------------- .../Entities/UserViewBuilder.cs | 2 +- src/Jellyfin.LiveTv/Channels/ChannelManager.cs | 9 ++--- src/Jellyfin.LiveTv/LiveTvManager.cs | 4 +- 7 files changed, 26 insertions(+), 39 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 9d81b835c..c68211859 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Library results = results.GetRange(query.StartIndex.Value, totalRecordCount - query.StartIndex.Value); } - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count)); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index ee2e18f73..cd98dbe86 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -266,7 +266,7 @@ namespace Emby.Server.Implementations.TV items = items.Skip(query.StartIndex.Value); } - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { items = items.Take(query.Limit.Value); } diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 51a118645..bcf348f8c 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -158,7 +158,7 @@ namespace Jellyfin.Server.Implementations.Devices devices = devices.Skip(query.Skip.Value); } - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { devices = devices.Take(query.Limit.Value); } diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index 57d874e59..dfe46ef8f 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -250,7 +250,7 @@ public sealed class BaseItemRepository public QueryResult GetItems(InternalItemsQuery filter) { ArgumentNullException.ThrowIfNull(filter); - if (!filter.EnableTotalRecordCount || (!filter.Limit.HasValue && (filter.StartIndex ?? 0) == 0)) + if (!filter.EnableTotalRecordCount || ((filter.Limit ?? 0) == 0 && (filter.StartIndex ?? 0) == 0)) { var returnList = GetItemList(filter); return new QueryResult( @@ -326,7 +326,7 @@ public sealed class BaseItemRepository .OrderByDescending(g => g.MaxDateCreated) .Select(g => g); - if (filter.Limit.HasValue) + if (filter.Limit.HasValue && filter.Limit.Value > 0) { subqueryGrouped = subqueryGrouped.Take(filter.Limit.Value); } @@ -367,7 +367,7 @@ public sealed class BaseItemRepository .OrderByDescending(g => g.LastPlayedDate) .Select(g => g.Key!); - if (filter.Limit.HasValue) + if (filter.Limit.HasValue && filter.Limit.Value > 0) { query = query.Take(filter.Limit.Value); } @@ -425,19 +425,14 @@ public sealed class BaseItemRepository private IQueryable ApplyQueryPaging(IQueryable dbQuery, InternalItemsQuery filter) { - if (filter.Limit.HasValue || filter.StartIndex.HasValue) + if (filter.StartIndex.HasValue && filter.StartIndex.Value > 0) { - var offset = filter.StartIndex ?? 0; - - if (offset > 0) - { - dbQuery = dbQuery.Skip(offset); - } + dbQuery = dbQuery.Skip(filter.StartIndex.Value); + } - if (filter.Limit.HasValue) - { - dbQuery = dbQuery.Take(filter.Limit.Value); - } + if (filter.Limit.HasValue && filter.Limit.Value > 0) + { + dbQuery = dbQuery.Take(filter.Limit.Value); } return dbQuery; @@ -1190,7 +1185,7 @@ public sealed class BaseItemRepository { ArgumentNullException.ThrowIfNull(filter); - if (!filter.Limit.HasValue) + if (!(filter.Limit.HasValue && filter.Limit.Value > 0)) { filter.EnableTotalRecordCount = false; } @@ -1269,19 +1264,14 @@ public sealed class BaseItemRepository result.TotalRecordCount = query.Count(); } - if (filter.Limit.HasValue || filter.StartIndex.HasValue) + if (filter.StartIndex.HasValue && filter.StartIndex.Value > 0) { - var offset = filter.StartIndex ?? 0; - - if (offset > 0) - { - query = query.Skip(offset); - } + query = query.Skip(filter.StartIndex.Value); + } - if (filter.Limit.HasValue) - { - query = query.Take(filter.Limit.Value); - } + if (filter.Limit.HasValue && filter.Limit.Value > 0) + { + query = query.Take(filter.Limit.Value); } IQueryable? itemCountQuery = null; @@ -1362,7 +1352,7 @@ public sealed class BaseItemRepository private static void PrepareFilterQuery(InternalItemsQuery query) { - if (query.Limit.HasValue && query.EnableGroupByMetadataKey) + if (query.Limit.HasValue && query.Limit.Value > 0 && query.EnableGroupByMetadataKey) { query.Limit = query.Limit.Value + 4; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 4f9e9261b..bed7554b1 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -455,7 +455,7 @@ namespace MediaBrowser.Controller.Entities var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); var totalCount = itemsArray.Length; - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray(); } diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs index 8ee129a57..2b8e5a0a0 100644 --- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -240,12 +240,9 @@ namespace Jellyfin.LiveTv.Channels var all = channels; var totalCount = all.Count; - if (query.StartIndex.HasValue || query.Limit.HasValue) - { - int startIndex = query.StartIndex ?? 0; - int count = query.Limit is null ? totalCount - startIndex : Math.Min(query.Limit.Value, totalCount - startIndex); - all = all.GetRange(startIndex, count); - } + int startIndex = query.StartIndex ?? 0; + int count = (query.Limit ?? 0) > 0 ? Math.Min(query.Limit.Value, totalCount - startIndex) : totalCount - startIndex; + all = all.GetRange(query.StartIndex ?? 0, count); if (query.RefreshLatestChannelItems) { diff --git a/src/Jellyfin.LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs index 53bc6751f..1d18ade9d 100644 --- a/src/Jellyfin.LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -287,7 +287,7 @@ namespace Jellyfin.LiveTv GenreIds = query.GenreIds }; - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { internalQuery.Limit = Math.Max(query.Limit.Value * 4, 200); } @@ -305,7 +305,7 @@ namespace Jellyfin.LiveTv IEnumerable programs = orderedPrograms; - if (query.Limit.HasValue) + if (query.Limit.HasValue && query.Limit.Value > 0) { programs = programs.Take(query.Limit.Value); } -- cgit v1.2.3 From 25aef7fabffb0297c2830b23393b99496ddfeba4 Mon Sep 17 00:00:00 2001 From: Tom O'Neill Date: Tue, 9 Dec 2025 13:18:07 -0500 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 09246bd11..350b039c5 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -23,7 +23,7 @@ "HeaderFavoriteShows": "Favoriete series", "HeaderFavoriteSongs": "Favoriete nummers", "HeaderLiveTV": "Live-tv", - "HeaderNextUp": "Als volgende", + "HeaderNextUp": "Volgende", "HeaderRecordingGroups": "Opnamegroepen", "HomeVideos": "Homevideo's", "Inherit": "Erven", -- cgit v1.2.3 From 0f85120c5e9ba2df624e15e1b3c82b2d771f4a0b Mon Sep 17 00:00:00 2001 From: Bas <44002186+854562@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:20:47 -0500 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 350b039c5..09246bd11 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -23,7 +23,7 @@ "HeaderFavoriteShows": "Favoriete series", "HeaderFavoriteSongs": "Favoriete nummers", "HeaderLiveTV": "Live-tv", - "HeaderNextUp": "Volgende", + "HeaderNextUp": "Als volgende", "HeaderRecordingGroups": "Opnamegroepen", "HomeVideos": "Homevideo's", "Inherit": "Erven", -- cgit v1.2.3 From 053cc9406d5b801b0ede26f428ccc4202a6df78f Mon Sep 17 00:00:00 2001 From: Veldermon-rbg Date: Tue, 9 Dec 2025 19:29:33 -0500 Subject: Added translation using Weblate (Maori) --- Emby.Server.Implementations/Localization/Core/mi.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/mi.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mi.json b/Emby.Server.Implementations/Localization/Core/mi.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/mi.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From d70e0fe9cf29a647bf67e171d0da9c2b264e8970 Mon Sep 17 00:00:00 2001 From: Veldermon-rbg Date: Tue, 9 Dec 2025 19:43:50 -0500 Subject: Translated using Weblate (Maori) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mi/ --- Emby.Server.Implementations/Localization/Core/mi.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mi.json b/Emby.Server.Implementations/Localization/Core/mi.json index 0967ef424..3b20abb36 100644 --- a/Emby.Server.Implementations/Localization/Core/mi.json +++ b/Emby.Server.Implementations/Localization/Core/mi.json @@ -1 +1,9 @@ -{} +{ + "Albums": "Pukaemi", + "AppDeviceValues": "Taupānga: {0}, Pūrere: {1}", + "Application": "Taupānga", + "Artists": "Kaiwaiata", + "AuthenticationSucceededWithUserName": "{0} has been successfully authenticated", + "Books": "Ngā pukapuka", + "CameraImageUploadedFrom": "Kua tuku ake he whakaahua kāmera hou mai i {0}" +} -- cgit v1.2.3 From acb9da6f93829eaef356d3effd1872798c778adb Mon Sep 17 00:00:00 2001 From: Nilesh Patel Date: Wed, 10 Dec 2025 12:23:05 -0800 Subject: Add curly brace and parentheses support for parsing attribute values from paths --- .../Library/PathExtensions.cs | 24 +++++++++++++++------- .../Library/PathExtensionsTests.cs | 19 +++++++++++++---- 2 files changed, 32 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 21e7079d8..fc63251ad 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -37,15 +37,25 @@ namespace Emby.Server.Implementations.Library while (attributeIndex > -1 && attributeIndex < maxIndex) { var attributeEnd = attributeIndex + attribute.Length; - if (attributeIndex > 0 - && str[attributeIndex - 1] == '[' - && (str[attributeEnd] == '=' || str[attributeEnd] == '-')) + if (attributeIndex > 0) { - var closingIndex = str[attributeEnd..].IndexOf(']'); - // Must be at least 1 character before the closing bracket. - if (closingIndex > 1) + var attributeOpener = str[attributeIndex - 1]; + var attributeCloser = attributeOpener switch { - return str[(attributeEnd + 1)..(attributeEnd + closingIndex)].Trim().ToString(); + '[' => ']', + '(' => ')', + '{' => '}', + _ => '\0' + }; + if (attributeCloser != '\0' && (str[attributeEnd] == '=' || str[attributeEnd] == '-')) + { + var closingIndex = str[attributeEnd..].IndexOf(attributeCloser); + + // Must be at least 1 character before the closing bracket. + if (closingIndex > 1) + { + return str[(attributeEnd + 1)..(attributeEnd + closingIndex)].Trim().ToString(); + } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index 940e3c2b1..74cd303ba 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -11,21 +11,29 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son [imdbid-tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son {imdbid=tt10985510}", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son (imdbid-tt10985510)", "imdbid", "tt10985510")] [InlineData("Superman: Red Son", "imdbid", null)] - [InlineData("Superman: Red Son", "something", null)] [InlineData("Superman: Red Son [imdbid1=tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")] - [InlineData("Superman: Red Son [imdbid1-tt11111111][imdbid=tt10985510]", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son {imdbid1=tt11111111}(imdbid=tt10985510)", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son (imdbid1-tt11111111)[imdbid=tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son [tmdbid=618355][imdbid=tt10985510]", "imdbid", "tt10985510")] - [InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "imdbid", "tt10985510")] - [InlineData("Superman: Red Son [tmdbid-618355][imdbid-tt10985510]", "tmdbid", "618355")] + [InlineData("Superman: Red Son [tmdbid-618355]{imdbid-tt10985510}", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son (tmdbid-618355)[imdbid-tt10985510]", "tmdbid", "618355")] [InlineData("Superman: Red Son [providera-id=1]", "providera-id", "1")] [InlineData("Superman: Red Son [providerb-id=2]", "providerb-id", "2")] [InlineData("Superman: Red Son [providera id=4]", "providera id", "4")] [InlineData("Superman: Red Son [providerb id=5]", "providerb id", "5")] [InlineData("Superman: Red Son [tmdbid=3]", "tmdbid", "3")] [InlineData("Superman: Red Son [tvdbid-6]", "tvdbid", "6")] + [InlineData("Superman: Red Son {tmdbid=3)", "tmdbid", "3")] + [InlineData("Superman: Red Son (tvdbid-6}", "tvdbid", "6")] [InlineData("[tmdbid=618355]", "tmdbid", "618355")] + [InlineData("{tmdbid=618355}", "tmdbid", "618355")] + [InlineData("(tmdbid=618355)", "tmdbid", "618355")] [InlineData("[tmdbid-618355]", "tmdbid", "618355")] + [InlineData("{tmdbid-618355)", "tmdbid", null)] + [InlineData("[tmdbid-618355}", "tmdbid", null)] [InlineData("tmdbid=111111][tmdbid=618355]", "tmdbid", "618355")] [InlineData("[tmdbid=618355]tmdbid=111111]", "tmdbid", "618355")] [InlineData("tmdbid=618355]", "tmdbid", null)] @@ -36,6 +44,9 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("[tmdbid=][imdbid=tt10985510]", "tmdbid", null)] [InlineData("[tmdbid-][imdbid-tt10985510]", "tmdbid", null)] [InlineData("Superman: Red Son [tmdbid-618355][tmdbid=1234567]", "tmdbid", "618355")] + [InlineData("{tmdbid=}{imdbid=tt10985510}", "tmdbid", null)] + [InlineData("(tmdbid-)(imdbid-tt10985510)", "tmdbid", null)] + [InlineData("Superman: Red Son {tmdbid-618355}{tmdbid=1234567}", "tmdbid", "618355")] public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult) { Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute)); -- cgit v1.2.3 From dd480f96cdd341c8a986eb4cb7133eff3d535f63 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 14 Dec 2025 00:29:28 +0900 Subject: parse more information from book filenames (#15655) --- Emby.Naming/Book/BookFileNameParser.cs | 75 ++++++++++++++++++++++ Emby.Naming/Book/BookFileNameParserResult.cs | 41 ++++++++++++ .../Library/Resolvers/Books/BookResolver.cs | 34 ++++++---- 3 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 Emby.Naming/Book/BookFileNameParser.cs create mode 100644 Emby.Naming/Book/BookFileNameParserResult.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Book/BookFileNameParser.cs b/Emby.Naming/Book/BookFileNameParser.cs new file mode 100644 index 000000000..28625f16d --- /dev/null +++ b/Emby.Naming/Book/BookFileNameParser.cs @@ -0,0 +1,75 @@ +using System.Text.RegularExpressions; + +namespace Emby.Naming.Book +{ + /// + /// Helper class to retrieve basic metadata from a book filename. + /// + public static class BookFileNameParser + { + private const string NameMatchGroup = "name"; + private const string IndexMatchGroup = "index"; + private const string YearMatchGroup = "year"; + private const string SeriesNameMatchGroup = "seriesName"; + + private static readonly Regex[] _nameMatches = + [ + // seriesName (seriesYear) #index (of count) (year) where only seriesName and index are required + new Regex(@"^(?.+?)((\s\((?[0-9]{4})\))?)\s#(?[0-9]+)((\s\(of\s(?[0-9]+)\))?)((\s\((?[0-9]{4})\))?)$"), + new Regex(@"^(?.+?)\s\((?.+?),\s#(?[0-9]+)\)((\s\((?[0-9]{4})\))?)$"), + new Regex(@"^(?[0-9]+)\s\-\s(?.+?)((\s\((?[0-9]{4})\))?)$"), + new Regex(@"(?.*)\((?[0-9]{4})\)"), + // last resort matches the whole string as the name + new Regex(@"(?.*)") + ]; + + /// + /// Parse a filename name to retrieve the book name, series name, index, and year. + /// + /// Book filename to parse for information. + /// Returns object. + public static BookFileNameParserResult Parse(string? name) + { + var result = new BookFileNameParserResult(); + + if (name == null) + { + return result; + } + + foreach (var regex in _nameMatches) + { + var match = regex.Match(name); + + if (!match.Success) + { + continue; + } + + if (match.Groups.TryGetValue(NameMatchGroup, out Group? nameGroup) && nameGroup.Success) + { + result.Name = nameGroup.Value.Trim(); + } + + if (match.Groups.TryGetValue(IndexMatchGroup, out Group? indexGroup) && indexGroup.Success && int.TryParse(indexGroup.Value, out var index)) + { + result.Index = index; + } + + if (match.Groups.TryGetValue(YearMatchGroup, out Group? yearGroup) && yearGroup.Success && int.TryParse(yearGroup.Value, out var year)) + { + result.Year = year; + } + + if (match.Groups.TryGetValue(SeriesNameMatchGroup, out Group? seriesGroup) && seriesGroup.Success) + { + result.SeriesName = seriesGroup.Value.Trim(); + } + + break; + } + + return result; + } + } +} diff --git a/Emby.Naming/Book/BookFileNameParserResult.cs b/Emby.Naming/Book/BookFileNameParserResult.cs new file mode 100644 index 000000000..f29716b9e --- /dev/null +++ b/Emby.Naming/Book/BookFileNameParserResult.cs @@ -0,0 +1,41 @@ +using System; + +namespace Emby.Naming.Book +{ + /// + /// Data object used to pass metadata parsed from a book filename. + /// + public class BookFileNameParserResult + { + /// + /// Initializes a new instance of the class. + /// + public BookFileNameParserResult() + { + Name = null; + Index = null; + Year = null; + SeriesName = null; + } + + /// + /// Gets or sets the name of the book. + /// + public string? Name { get; set; } + + /// + /// Gets or sets the book index. + /// + public int? Index { get; set; } + + /// + /// Gets or sets the publication year. + /// + public int? Year { get; set; } + + /// + /// Gets or sets the series name. + /// + public string? SeriesName { get; set; } + } +} diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 464a548ab..1e885aad6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -5,12 +5,12 @@ using System; using System.IO; using System.Linq; +using Emby.Naming.Book; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; -using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers.Books { @@ -35,17 +35,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books var extension = Path.GetExtension(args.Path.AsSpan()); - if (_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) + if (!_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) { - // It's a book - return new Book - { - Path = args.Path, - IsInMixedFolder = true - }; + return null; } - return null; + var result = BookFileNameParser.Parse(Path.GetFileNameWithoutExtension(args.Path)); + + return new Book + { + Path = args.Path, + Name = result.Name ?? string.Empty, + IndexNumber = result.Index, + ProductionYear = result.Year, + SeriesName = result.SeriesName ?? Path.GetFileName(Path.GetDirectoryName(args.Path)), + IsInMixedFolder = true, + }; } private Book GetBook(ItemResolveArgs args) @@ -59,15 +64,22 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books StringComparison.OrdinalIgnoreCase); }).ToList(); - // Don't return a Book if there is more (or less) than one document in the directory + // directory is only considered a book when it contains exactly one supported file + // other library structures with multiple books to a directory will get picked up as individual files if (bookFiles.Count != 1) { return null; } + var result = BookFileNameParser.Parse(Path.GetFileName(args.Path)); + return new Book { - Path = bookFiles[0].FullName + Path = bookFiles[0].FullName, + Name = result.Name ?? string.Empty, + IndexNumber = result.Index, + ProductionYear = result.Year, + SeriesName = result.SeriesName ?? string.Empty, }; } } -- cgit v1.2.3 From 771b0a7eabc7c3082dd5b328f121417385a1fc99 Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Sat, 13 Dec 2025 08:43:49 -0700 Subject: Library: Async the SaveImages function (#15718) --- .../Library/LibraryManager.cs | 2 +- .../Item/BaseItemRepository.cs | 32 +++++++++++++++------- .../Persistence/IItemRepository.cs | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index cab87e53d..30c3e89b4 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2143,7 +2143,7 @@ namespace Emby.Server.Implementations.Library item.ValidateImages(); - _itemRepository.SaveImages(item); + await _itemRepository.SaveImagesAsync(item).ConfigureAwait(false); RegisterItem(item); } diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index dfe46ef8f..9851d53c4 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -547,22 +547,34 @@ public sealed class BaseItemRepository } /// - public void SaveImages(BaseItemDto item) + public async Task SaveImagesAsync(BaseItemDto item, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(item); - var images = item.ImageInfos.Select(e => Map(item.Id, e)); - using var context = _dbProvider.CreateDbContext(); + var images = item.ImageInfos.Select(e => Map(item.Id, e)).ToArray(); - if (!context.BaseItems.Any(bi => bi.Id == item.Id)) + var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + await using (context.ConfigureAwait(false)) { - _logger.LogWarning("Unable to save ImageInfo for non existing BaseItem"); - return; - } + if (!await context.BaseItems + .AnyAsync(bi => bi.Id == item.Id, cancellationToken) + .ConfigureAwait(false)) + { + _logger.LogWarning("Unable to save ImageInfo for non existing BaseItem"); + return; + } - context.BaseItemImageInfos.Where(e => e.ItemId == item.Id).ExecuteDelete(); - context.BaseItemImageInfos.AddRange(images); - context.SaveChanges(); + await context.BaseItemImageInfos + .Where(e => e.ItemId == item.Id) + .ExecuteDeleteAsync(cancellationToken) + .ConfigureAwait(false); + + await context.BaseItemImageInfos + .AddRangeAsync(images, cancellationToken) + .ConfigureAwait(false); + + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } } /// diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 0026ab2b5..00c492742 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -33,7 +33,7 @@ public interface IItemRepository /// The cancellation token. void SaveItems(IReadOnlyList items, CancellationToken cancellationToken); - void SaveImages(BaseItem item); + Task SaveImagesAsync(BaseItem item, CancellationToken cancellationToken = default); /// /// Retrieves the item. -- cgit v1.2.3 From 12a2f7c1a59d6024db36d01f96db683377af222f Mon Sep 17 00:00:00 2001 From: João Moura Date: Tue, 16 Dec 2025 10:23:15 -0500 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (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 c3eba362d..17284854f 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -125,8 +125,8 @@ "TaskKeyframeExtractor": "Extrator de Quadros-chave", "External": "Externo", "HearingImpaired": "Surdo", - "TaskRefreshTrickplayImages": "Gerar imagens de truques", - "TaskRefreshTrickplayImagesDescription": "Cria vizualizações de truques para videos nas librarias ativas.", + "TaskRefreshTrickplayImages": "Gerar Imagens de Trickplay", + "TaskRefreshTrickplayImagesDescription": "Cria ficheiros de trickplay para vídeos nas bibliotecas ativas.", "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.", "TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução", "TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.", -- cgit v1.2.3 From 59d574edb7aca3a7a6ffdd139cc56111d7804ffc Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 17 Dec 2025 17:11:14 -0500 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- .../Localization/Core/de.json | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index e60d03e46..8d86b2da1 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -9,9 +9,9 @@ "Channels": "Kanäle", "ChapterNameValue": "Kapitel {0}", "Collections": "Sammlungen", - "DeviceOfflineWithName": "{0} hat die Verbindung getrennt", - "DeviceOnlineWithName": "{0} ist verbunden", - "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", + "DeviceOfflineWithName": "{0} ist offline", + "DeviceOnlineWithName": "{0} ist online", + "FailedLoginAttemptWithUserName": "Anmeldung von {0} fehlgeschlagen", "Favorites": "Favoriten", "Folders": "Verzeichnisse", "Genres": "Genres", @@ -21,7 +21,7 @@ "HeaderFavoriteArtists": "Lieblingsinterpreten", "HeaderFavoriteEpisodes": "Lieblingsepisoden", "HeaderFavoriteShows": "Lieblingsserien", - "HeaderFavoriteSongs": "Lieblingslieder", + "HeaderFavoriteSongs": "Lieblingssongs", "HeaderLiveTV": "Live TV", "HeaderNextUp": "Als Nächstes", "HeaderRecordingGroups": "Aufnahme-Gruppen", @@ -46,7 +46,7 @@ "NewVersionIsAvailable": "Eine neue Jellyfin-Serverversion steht zum Download bereit.", "NotificationOptionApplicationUpdateAvailable": "Anwendungsaktualisierung verfügbar", "NotificationOptionApplicationUpdateInstalled": "Anwendungsaktualisierung installiert", - "NotificationOptionAudioPlayback": "Audiowiedergabe gestartet", + "NotificationOptionAudioPlayback": "Audio wird abgespielt", "NotificationOptionAudioPlaybackStopped": "Audiowiedergabe gestoppt", "NotificationOptionCameraImageUploaded": "Foto hochgeladen", "NotificationOptionInstallationFailed": "Installation fehlgeschlagen", @@ -57,11 +57,11 @@ "NotificationOptionPluginUpdateInstalled": "Pluginaktualisierung installiert", "NotificationOptionServerRestartRequired": "Serverneustart notwendig", "NotificationOptionTaskFailed": "Geplante Aufgabe fehlgeschlagen", - "NotificationOptionUserLockedOut": "Benutzer ausgeschlossen", - "NotificationOptionVideoPlayback": "Videowiedergabe gestartet", + "NotificationOptionUserLockedOut": "Benutzer gesperrt", + "NotificationOptionVideoPlayback": "Video wird abgespielt", "NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt", "Photos": "Fotos", - "Playlists": "Wiedergabelisten", + "Playlists": "Playlists", "Plugin": "Plugin", "PluginInstalledWithName": "{0} wurde installiert", "PluginUninstalledWithName": "{0} wurde deinstalliert", @@ -82,7 +82,7 @@ "UserCreatedWithName": "Benutzer {0} wurde erstellt", "UserDeletedWithName": "Benutzer {0} wurde gelöscht", "UserDownloadingItemWithValues": "{0} lädt {1} herunter", - "UserLockedOutWithName": "Benutzer {0} wurde ausgeschlossen", + "UserLockedOutWithName": "Benutzer {0} wurde gesperrt", "UserOfflineFromDevice": "{0} wurde getrennt von {1}", "UserOnlineFromDevice": "{0} ist online von {1}", "UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert", @@ -96,26 +96,26 @@ "TaskDownloadMissingSubtitles": "Fehlende Untertitel herunterladen", "TaskRefreshChannelsDescription": "Aktualisiert Internet-Kanal-Informationen.", "TaskRefreshChannels": "Kanäle aktualisieren", - "TaskCleanTranscodeDescription": "Löscht Transkodierungsdateien, die älter als einen Tag sind.", - "TaskCleanTranscode": "Transkodierungs-Verzeichnis aufräumen", + "TaskCleanTranscodeDescription": "Löscht temporäre Videodateien, die älter als 24 Stunden sind.", + "TaskCleanTranscode": "Temporäre Videodateien löschen", "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.", "TaskUpdatePlugins": "Plugins aktualisieren", "TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.", "TaskRefreshPeople": "Personen aktualisieren", "TaskCleanLogsDescription": "Lösche Log-Dateien, die älter als {0} Tage sind.", - "TaskCleanLogs": "Log-Verzeichnis aufräumen", - "TaskRefreshLibraryDescription": "Durchsucht alle Bibliotheken nach neu hinzugefügten Dateien und aktualisiert Metadaten.", + "TaskCleanLogs": "Alte Protokolle löschen", + "TaskRefreshLibraryDescription": "Sucht nach neuen Dateien und aktualisiert Infos zu deinen Medien.", "TaskRefreshLibrary": "Medien-Bibliothek scannen", - "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videos, die Kapitel besitzen.", - "TaskRefreshChapterImages": "Kapitel-Bilder extrahieren", - "TaskCleanCacheDescription": "Löscht vom System nicht mehr benötigte Zwischenspeicherdateien.", - "TaskCleanCache": "Zwischenspeicher-Verzeichnis aufräumen", + "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videokapitel.", + "TaskRefreshChapterImages": "Kapitelvorschauen erstellen", + "TaskCleanCacheDescription": "Entfernt nicht mehr benötigte Cache-Dateien.", + "TaskCleanCache": "Cache leeren", "TasksChannelsCategory": "Internet-Kanäle", "TasksApplicationCategory": "Anwendung", "TasksLibraryCategory": "Bibliothek", "TasksMaintenanceCategory": "Wartung", "TaskCleanActivityLogDescription": "Löscht Aktivitätsprotokolleinträge, die älter als das konfigurierte Alter sind.", - "TaskCleanActivityLog": "Aktivitätsprotokolle aufräumen", + "TaskCleanActivityLog": "Aktivitätsverlauf bereinigen", "Undefined": "Undefiniert", "Forced": "Erzwungen", "Default": "Standard", -- cgit v1.2.3 From ee676fd568961e9a363cd3ed4f33d307dca7b20e Mon Sep 17 00:00:00 2001 From: Fabrizio Mansilla Date: Thu, 18 Dec 2025 23:07:35 -0500 Subject: Translated using Weblate (Spanish (Argentina)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_AR/ --- Emby.Server.Implementations/Localization/Core/es-AR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index 012f793a6..1f8af4c8a 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -70,7 +70,7 @@ "ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskStartedWithName": "{0} iniciado", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", - "Shows": "Programas", + "Shows": "Series", "Songs": "Canciones", "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", -- cgit v1.2.3 From d446fde0091b3b101b2735f21dd24e49f0fd5660 Mon Sep 17 00:00:00 2001 From: Kityn Date: Thu, 18 Dec 2025 01:43:23 -0500 Subject: Translated using Weblate (Polish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/ --- Emby.Server.Implementations/Localization/Core/pl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index 3555ea4ae..8ca22ac04 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -125,8 +125,8 @@ "TaskKeyframeExtractorDescription": "Wyodrębnia klatki kluczowe z plików wideo w celu utworzenia bardziej precyzyjnych list odtwarzania HLS. To zadanie może trwać przez długi czas.", "TaskKeyframeExtractor": "Ekstraktor klatek kluczowych", "HearingImpaired": "Niedosłyszący", - "TaskRefreshTrickplayImages": "Generuj obrazy trickplay", - "TaskRefreshTrickplayImagesDescription": "Tworzy podglądy trickplay dla filmów we włączonych bibliotekach.", + "TaskRefreshTrickplayImages": "Generuj obrazy Trickplay", + "TaskRefreshTrickplayImagesDescription": "Tworzy podglądy Trickplay dla filmów we włączonych bibliotekach.", "TaskCleanCollectionsAndPlaylistsDescription": "Usuwa elementy z kolekcji i list odtwarzania, które już nie istnieją.", "TaskCleanCollectionsAndPlaylists": "Oczyść kolekcje i listy odtwarzania", "TaskAudioNormalization": "Normalizacja dźwięku", -- cgit v1.2.3 From 0e73a56a457cd5b91673da2e42304066aadffbdb Mon Sep 17 00:00:00 2001 From: Translation expert Date: Thu, 25 Dec 2025 12:20:18 -0500 Subject: Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 24ed116f3..d09a7884e 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -2,13 +2,13 @@ "Albums": "ألبومات", "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "تطبيق", - "Artists": "الفنانون", + "Artists": "فنانون", "AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}", "Books": "الكتب", "CameraImageUploadedFrom": "رُفعت صورة الكاميرا الجديدة من {0}", "Channels": "القنوات", "ChapterNameValue": "الفصل {0}", - "Collections": "المجموعات", + "Collections": "مجموعات", "DeviceOfflineWithName": "قُطِع الاتصال ب{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فاشلة من {0}", @@ -16,7 +16,7 @@ "Folders": "المجلدات", "Genres": "التصنيفات", "HeaderAlbumArtists": "فناني الألبوم", - "HeaderContinueWatching": "أكمل المشاهدة", + "HeaderContinueWatching": "متابعة المشاهدة", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteEpisodes": "الحلقات المفضلة", -- cgit v1.2.3 From c30654c33c0561ee7f3b7d096038f9604a111003 Mon Sep 17 00:00:00 2001 From: MrPlow Date: Sat, 27 Dec 2025 02:16:02 -0500 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 8d86b2da1..278d78ae4 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -96,20 +96,20 @@ "TaskDownloadMissingSubtitles": "Fehlende Untertitel herunterladen", "TaskRefreshChannelsDescription": "Aktualisiert Internet-Kanal-Informationen.", "TaskRefreshChannels": "Kanäle aktualisieren", - "TaskCleanTranscodeDescription": "Löscht temporäre Videodateien, die älter als 24 Stunden sind.", - "TaskCleanTranscode": "Temporäre Videodateien löschen", + "TaskCleanTranscodeDescription": "Löscht Transkodierungsdateien, die älter als einen Tag sind.", + "TaskCleanTranscode": "Transkodierungsverzeichnis leeren", "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.", "TaskUpdatePlugins": "Plugins aktualisieren", "TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.", "TaskRefreshPeople": "Personen aktualisieren", "TaskCleanLogsDescription": "Lösche Log-Dateien, die älter als {0} Tage sind.", - "TaskCleanLogs": "Alte Protokolle löschen", - "TaskRefreshLibraryDescription": "Sucht nach neuen Dateien und aktualisiert Infos zu deinen Medien.", + "TaskCleanLogs": "Protokollverzeichnis leeren", + "TaskRefreshLibraryDescription": "Durchsucht deine Medienbibliothek nach neuen Dateien und aktualisiert Metadaten.", "TaskRefreshLibrary": "Medien-Bibliothek scannen", "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videokapitel.", "TaskRefreshChapterImages": "Kapitelvorschauen erstellen", - "TaskCleanCacheDescription": "Entfernt nicht mehr benötigte Cache-Dateien.", - "TaskCleanCache": "Cache leeren", + "TaskCleanCacheDescription": "Löscht Cache-Dateien, die vom System nicht mehr benötigt werden.", + "TaskCleanCache": "Cache-Verzeichnis leeren", "TasksChannelsCategory": "Internet-Kanäle", "TasksApplicationCategory": "Anwendung", "TasksLibraryCategory": "Bibliothek", -- cgit v1.2.3 From 55570043759bcfa3c76df7e94d3303257171c970 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 28 Dec 2025 07:22:17 -0500 Subject: Backport pull request #15689 from jellyfin/release-10.11.z Use original name for MusicAritist matching Original-merge: 4c5a3fbff34a603ff0344e0b42d07bc17f31f92c Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Library/LibraryManager.cs | 1 + Jellyfin.Server.Implementations/Item/BaseItemRepository.cs | 11 +++++++++-- MediaBrowser.Controller/Entities/InternalItemsQuery.cs | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 30c3e89b4..f35d85f65 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1058,6 +1058,7 @@ namespace Emby.Server.Implementations.Library { IncludeItemTypes = [BaseItemKind.MusicArtist], Name = name, + UseRawName = true, DtoOptions = options }).Cast() .OrderBy(i => i.IsAccessedByName ? 1 : 0) diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index 6b060430e..98072918c 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -1987,8 +1987,15 @@ public sealed class BaseItemRepository if (!string.IsNullOrWhiteSpace(filter.Name)) { - var cleanName = GetCleanValue(filter.Name); - baseQuery = baseQuery.Where(e => e.CleanName == cleanName); + if (filter.UseRawName == true) + { + baseQuery = baseQuery.Where(e => e.Name == filter.Name); + } + else + { + var cleanName = GetCleanValue(filter.Name); + baseQuery = baseQuery.Where(e => e.CleanName == cleanName); + } } // These are the same, for now diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index b32b64f5d..076a59292 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -125,6 +125,8 @@ namespace MediaBrowser.Controller.Entities public string? Name { get; set; } + public bool? UseRawName { get; set; } + public string? Person { get; set; } public Guid[] PersonIds { get; set; } -- cgit v1.2.3 From b9cf26db2f3d219340a17951c289a230d0ccf31a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 28 Dec 2025 07:22:20 -0500 Subject: Backport pull request #15746 from jellyfin/release-10.11.z Skip invalid ignore rules Original-merge: 6e60634c9f078cc01e343b07a0a6b2a5c230478c Merged-by: crobibero Backported-by: Bond_009 --- .../Library/DotIgnoreIgnoreRule.cs | 48 +++++++++++- .../Library/DotIgnoreIgnoreRuleTest.cs | 87 +++++++++++++++++----- 2 files changed, 115 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index 473ff8e1d..ef5d24c70 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Resolvers; @@ -70,12 +71,55 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule { // If file has content, base ignoring off the content .gitignore-style rules var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + return CheckIgnoreRules(path, rules, isDirectory); + } + + /// + /// Checks whether a path should be ignored based on an array of ignore rules. + /// + /// The path to check. + /// The array of ignore rules. + /// Whether the path is a directory. + /// True if the path should be ignored. + internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory) + => CheckIgnoreRules(path, rules, isDirectory, IsWindows); + + /// + /// Checks whether a path should be ignored based on an array of ignore rules. + /// + /// The path to check. + /// The array of ignore rules. + /// Whether the path is a directory. + /// Whether to normalize backslashes to forward slashes (for Windows paths). + /// True if the path should be ignored. + internal static bool CheckIgnoreRules(string path, string[] rules, bool isDirectory, bool normalizePath) + { var ignore = new Ignore.Ignore(); - ignore.Add(rules); + + // Add each rule individually to catch and skip invalid patterns + var validRulesAdded = 0; + foreach (var rule in rules) + { + try + { + ignore.Add(rule); + validRulesAdded++; + } + catch (RegexParseException) + { + // Ignore invalid patterns + } + } + + // If no valid rules were added, fall back to ignoring everything (like an empty .ignore file) + if (validRulesAdded == 0) + { + return true; + } // Mitigate the problem of the Ignore library not handling Windows paths correctly. // See https://github.com/jellyfin/jellyfin/issues/15484 - var pathToCheck = IsWindows ? path.NormalizePath('/') : path; + var pathToCheck = normalizePath ? path.NormalizePath('/') : path; // Add trailing slash for directories to match "folder/" if (isDirectory) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs index d677c9f09..a7bbef7ed 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/DotIgnoreIgnoreRuleTest.cs @@ -1,30 +1,81 @@ +using Emby.Server.Implementations.Library; using Xunit; namespace Jellyfin.Server.Implementations.Tests.Library; public class DotIgnoreIgnoreRuleTest { - [Fact] - public void Test() + private static readonly string[] _rule1 = ["SPs"]; + private static readonly string[] _rule2 = ["SPs", "!thebestshot.mkv"]; + private static readonly string[] _rule3 = ["*.txt", @"{\colortbl;\red255\green255\blue255;}", "videos/", @"\invalid\escape\sequence", "*.mkv"]; + private static readonly string[] _rule4 = [@"{\colortbl;\red255\green255\blue255;}", @"\invalid\escape\sequence"]; + + public static TheoryData CheckIgnoreRulesTestData => + new() + { + // Basic pattern matching + { _rule1, "f:/cd/sps/ffffff.mkv", false, true }, + { _rule1, "cd/sps/ffffff.mkv", false, true }, + { _rule1, "/cd/sps/ffffff.mkv", false, true }, + + // Negate pattern + { _rule2, "f:/cd/sps/ffffff.mkv", false, true }, + { _rule2, "cd/sps/ffffff.mkv", false, true }, + { _rule2, "/cd/sps/ffffff.mkv", false, true }, + { _rule2, "f:/cd/sps/thebestshot.mkv", false, false }, + { _rule2, "cd/sps/thebestshot.mkv", false, false }, + { _rule2, "/cd/sps/thebestshot.mkv", false, false }, + + // Mixed valid and invalid patterns - skips invalid, applies valid + { _rule3, "test.txt", false, true }, + { _rule3, "videos/movie.mp4", false, true }, + { _rule3, "movie.mkv", false, true }, + { _rule3, "test.mp3", false, false }, + + // Only invalid patterns - falls back to ignore all + { _rule4, "any-file.txt", false, true }, + { _rule4, "any/path/to/file.mkv", false, true }, + }; + + public static TheoryData WindowsPathNormalizationTestData => + new() + { + // Windows paths with backslashes - should match when normalizePath is true + { _rule1, @"C:\cd\sps\ffffff.mkv", false, true }, + { _rule1, @"D:\media\sps\movie.mkv", false, true }, + { _rule1, @"\\server\share\sps\file.mkv", false, true }, + + // Negate pattern with Windows paths + { _rule2, @"C:\cd\sps\ffffff.mkv", false, true }, + { _rule2, @"C:\cd\sps\thebestshot.mkv", false, false }, + + // Directory matching with Windows paths + { _rule3, @"C:\videos\movie.mp4", false, true }, + { _rule3, @"D:\documents\test.txt", false, true }, + { _rule3, @"E:\music\song.mp3", false, false }, + }; + + [Theory] + [MemberData(nameof(CheckIgnoreRulesTestData))] + public void CheckIgnoreRules_ReturnsExpectedResult(string[] rules, string path, bool isDirectory, bool expectedIgnored) + { + Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory)); + } + + [Theory] + [MemberData(nameof(WindowsPathNormalizationTestData))] + public void CheckIgnoreRules_WithWindowsPaths_NormalizesBackslashes(string[] rules, string path, bool isDirectory, bool expectedIgnored) { - var ignore = new Ignore.Ignore(); - ignore.Add("SPs"); - Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv")); - Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv")); - Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv")); + // With normalizePath=true, backslashes should be converted to forward slashes + Assert.Equal(expectedIgnored, DotIgnoreIgnoreRule.CheckIgnoreRules(path, rules, isDirectory, normalizePath: true)); } - [Fact] - public void TestNegatePattern() + [Theory] + [InlineData(@"C:\cd\sps\ffffff.mkv")] + [InlineData(@"D:\media\sps\movie.mkv")] + public void CheckIgnoreRules_WithWindowsPaths_WithoutNormalization_DoesNotMatch(string path) { - var ignore = new Ignore.Ignore(); - ignore.Add("SPs"); - ignore.Add("!thebestshot.mkv"); - Assert.True(ignore.IsIgnored("f:/cd/sps/ffffff.mkv")); - Assert.True(ignore.IsIgnored("cd/sps/ffffff.mkv")); - Assert.True(ignore.IsIgnored("/cd/sps/ffffff.mkv")); - Assert.True(!ignore.IsIgnored("f:/cd/sps/thebestshot.mkv")); - Assert.True(!ignore.IsIgnored("cd/sps/thebestshot.mkv")); - Assert.True(!ignore.IsIgnored("/cd/sps/thebestshot.mkv")); + // Without normalization, Windows paths with backslashes won't match patterns expecting forward slashes + Assert.False(DotIgnoreIgnoreRule.CheckIgnoreRules(path, _rule1, isDirectory: false, normalizePath: false)); } } -- cgit v1.2.3 From 3c802a75054f316e04a5d914036f3164e8bf7e87 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 28 Dec 2025 07:22:30 -0500 Subject: Backport pull request #15793 from jellyfin/release-10.11.z Prefer US rating on fallback Original-merge: 156761405e7fd5308474a7e6301839ae7c694dfa Merged-by: crobibero Backported-by: Bond_009 --- .../Localization/LocalizationManager.cs | 10 +++++++--- .../Localization/LocalizationManagerTests.cs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index b4c65ad85..d99ad4665 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -311,15 +311,19 @@ namespace Emby.Server.Implementations.Localization else { // Fall back to server default language for ratings check - // If it has no ratings, use the US ratings - var ratingsDictionary = GetParentalRatingsDictionary() ?? GetParentalRatingsDictionary("us"); + var ratingsDictionary = GetParentalRatingsDictionary(); if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRatingScore? value)) { return value; } } - // If we don't find anything, check all ratings systems + // If we don't find anything, check all ratings systems, starting with US + if (_allParentalRatings.TryGetValue("us", out var usRatings) && usRatings.TryGetValue(rating, out var usValue)) + { + return usValue; + } + foreach (var dictionary in _allParentalRatings.Values) { if (dictionary.TryGetValue(rating, out var value)) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 6d6bba4fc..e60522bf7 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -203,6 +203,25 @@ namespace Jellyfin.Server.Implementations.Tests.Localization Assert.Null(localizationManager.GetRatingScore(value)); } + [Theory] + [InlineData("TV-MA", "DE", 17, 1)] // US-only rating, DE country code + [InlineData("PG-13", "FR", 13, 0)] // US-only rating, FR country code + [InlineData("R", "JP", 17, 0)] // US-only rating, JP country code + public async Task GetRatingScore_FallbackPrioritizesUS_Success(string rating, string countryCode, int expectedScore, int? expectedSubScore) + { + var localizationManager = Setup(new ServerConfiguration() + { + MetadataCountryCode = countryCode + }); + await localizationManager.LoadAll(); + + var score = localizationManager.GetRatingScore(rating); + + Assert.NotNull(score); + Assert.Equal(expectedScore, score.Score); + Assert.Equal(expectedSubScore, score.SubScore); + } + [Theory] [InlineData("Default", "Default")] [InlineData("HeaderLiveTV", "Live TV")] -- cgit v1.2.3 From 45e881c93e694a174409fcec6743feef9ccf0b70 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 28 Dec 2025 07:22:33 -0500 Subject: Backport pull request #15826 from jellyfin/release-10.11.z add CultureDto cache Original-merge: 1805f2259f44aba0ca97ff0de2ad0b0a3614fa03 Merged-by: crobibero Backported-by: Bond_009 --- .../Localization/LocalizationManager.cs | 35 +++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index d99ad4665..bc80c2b40 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -38,6 +38,7 @@ namespace Emby.Server.Implementations.Localization private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private readonly ConcurrentDictionary _cultureCache = new(StringComparer.OrdinalIgnoreCase); private List _cultures = []; private FrozenDictionary _iso6392BtoT = null!; @@ -161,6 +162,7 @@ namespace Emby.Server.Implementations.Localization list.Add(new CultureDto(name, displayname, twoCharName, threeLetterNames)); } + _cultureCache.Clear(); _cultures = list; _iso6392BtoT = iso6392BtoTdict.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); } @@ -169,20 +171,31 @@ namespace Emby.Server.Implementations.Localization /// public CultureDto? FindLanguageInfo(string language) { - // TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs - for (var i = 0; i < _cultures.Count; i++) + if (string.IsNullOrEmpty(language)) { - var culture = _cultures[i]; - if (language.Equals(culture.DisplayName, StringComparison.OrdinalIgnoreCase) - || language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase) - || culture.ThreeLetterISOLanguageNames.Contains(language, StringComparison.OrdinalIgnoreCase) - || language.Equals(culture.TwoLetterISOLanguageName, StringComparison.OrdinalIgnoreCase)) - { - return culture; - } + return null; } - return default; + return _cultureCache.GetOrAdd( + language, + static (lang, cultures) => + { + // TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs + for (var i = 0; i < cultures.Count; i++) + { + var culture = cultures[i]; + if (lang.Equals(culture.DisplayName, StringComparison.OrdinalIgnoreCase) + || lang.Equals(culture.Name, StringComparison.OrdinalIgnoreCase) + || culture.ThreeLetterISOLanguageNames.Contains(lang, StringComparison.OrdinalIgnoreCase) + || lang.Equals(culture.TwoLetterISOLanguageName, StringComparison.OrdinalIgnoreCase)) + { + return culture; + } + } + + return null; + }, + _cultures); } /// -- cgit v1.2.3 From 75f1276119b44e3d6264439acb3bc00d6956f253 Mon Sep 17 00:00:00 2001 From: Joker Date: Mon, 29 Dec 2025 05:23:28 -0500 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 278d78ae4..14c42d3c0 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -61,7 +61,7 @@ "NotificationOptionVideoPlayback": "Video wird abgespielt", "NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt", "Photos": "Fotos", - "Playlists": "Playlists", + "Playlists": "Wiedergabeliste", "Plugin": "Plugin", "PluginInstalledWithName": "{0} wurde installiert", "PluginUninstalledWithName": "{0} wurde deinstalliert", -- cgit v1.2.3 From 2f62a8bb396da66cd6cc51d739308f8f4c703716 Mon Sep 17 00:00:00 2001 From: MrPlow Date: Mon, 29 Dec 2025 10:10:56 -0500 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 14c42d3c0..0b042c8fe 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -61,7 +61,7 @@ "NotificationOptionVideoPlayback": "Video wird abgespielt", "NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt", "Photos": "Fotos", - "Playlists": "Wiedergabeliste", + "Playlists": "Wiedergabelisten", "Plugin": "Plugin", "PluginInstalledWithName": "{0} wurde installiert", "PluginUninstalledWithName": "{0} wurde deinstalliert", -- cgit v1.2.3 From d28ee6d71415b4c1f5c158f30f427b6952b8d65b Mon Sep 17 00:00:00 2001 From: SamuWhale Date: Mon, 29 Dec 2025 08:44:22 -0500 Subject: Translated using Weblate (Thai) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/th/ --- Emby.Server.Implementations/Localization/Core/th.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json index 113e4f30f..65ddb55e9 100644 --- a/Emby.Server.Implementations/Localization/Core/th.json +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -135,5 +135,7 @@ "TaskExtractMediaSegments": "การสแกนส่วนของสื่อมีเดีย", "TaskMoveTrickplayImagesDescription": "ย้ายไฟล์ Trickplay ตามการตั้งค่าของไลบรารี", "TaskExtractMediaSegmentsDescription": "แยกหรือดึงส่วนของสื่อจากปลั๊กอินที่เปิดใช้งาน MediaSegment", - "TaskMoveTrickplayImages": "ย้ายตำแหน่งเก็บภาพตัวอย่าง Trickplay" + "TaskMoveTrickplayImages": "ย้ายตำแหน่งเก็บภาพตัวอย่าง Trickplay", + "CleanupUserDataTask": "ส่วนงานล้างข้อมูลผู้ใช้", + "CleanupUserDataTaskDescription": "ล้างข้อมูลผู้ใช้ทั้งหมด (สถานะการรับชม สถานะรายการโปรด ฯลฯ) จากสื่อที่ไม่ได้ใช้งานแล้วอย่างน้อย 90 วัน" } -- cgit v1.2.3 From f08657ab27c2f7db1200a83fa62c3c0aa6b12f67 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 31 Dec 2025 14:20:41 +0000 Subject: SessionManager: Log when playback is started --- Emby.Server.Implementations/Session/SessionManager.cs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index cf2ca047c..f52e0f6c6 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -793,6 +793,15 @@ namespace Emby.Server.Implementations.Session PlaySessionId = info.PlaySessionId }; + if (info.Item is not null) + { + _logger.LogInformation( + "Playback started reported by app {0} {1} playing {2}", + session.Client, + session.ApplicationVersion, + info.Item.Name); + } + await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false); // Nothing to save here -- cgit v1.2.3 From b564a43d9c81a5cc032a2def2ab2aac44215a398 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 31 Dec 2025 14:30:25 +0000 Subject: SessionManager: Log usernames in playback messages --- Emby.Server.Implementations/Session/SessionManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index f52e0f6c6..7109f3e4f 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -796,7 +796,8 @@ namespace Emby.Server.Implementations.Session if (info.Item is not null) { _logger.LogInformation( - "Playback started reported by app {0} {1} playing {2}", + "Playback started for user {0} reported by app {1} {2} playing {3}", + session.UserName, session.Client, session.ApplicationVersion, info.Item.Name); @@ -1069,7 +1070,8 @@ namespace Emby.Server.Implementations.Session var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown"; _logger.LogInformation( - "Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms", + "Playback stopped for user {0} reported by app {1} {2} playing {3}. Stopped at {4} ms", + session.UserName, session.Client, session.ApplicationVersion, info.Item.Name, -- cgit v1.2.3 From 3c77758b32b5e8ad6517728eb6a3fe25e498b272 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 31 Dec 2025 20:51:37 +0000 Subject: SessionManager: Improved wording of playback messages --- Emby.Server.Implementations/Session/SessionManager.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 7109f3e4f..bbe23f8df 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -796,11 +796,11 @@ namespace Emby.Server.Implementations.Session if (info.Item is not null) { _logger.LogInformation( - "Playback started for user {0} reported by app {1} {2} playing {3}", + "User {0} started playback of '{1}' ({2} {3})", session.UserName, + info.Item.Name, session.Client, - session.ApplicationVersion, - info.Item.Name); + session.ApplicationVersion); } await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false); @@ -1070,12 +1070,12 @@ namespace Emby.Server.Implementations.Session var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown"; _logger.LogInformation( - "Playback stopped for user {0} reported by app {1} {2} playing {3}. Stopped at {4} ms", + "User {0} stopped playback of '{1}' at {2}ms ({3} {4})", session.UserName, - session.Client, - session.ApplicationVersion, info.Item.Name, - msString); + msString, + session.Client, + session.ApplicationVersion); } if (info.NowPlayingQueue is not null) -- cgit v1.2.3 From bfae788a44d7c5640bd23c6f433533ef16f30107 Mon Sep 17 00:00:00 2001 From: Dzmitry Zubialevich Date: Fri, 2 Jan 2026 05:40:57 -0500 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 29847048c..4d769efc3 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -95,7 +95,7 @@ "ServerNameNeedsToBeRestarted": "{0} патрабуе перазапуску", "Shows": "Шоу", "StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.", - "SubtitleDownloadFailureFromForItem": "Не атрымалася спампаваць субтытры з {0} для {1}", + "SubtitleDownloadFailureFromForItem": "Субцітры для {1} не ўдалося спампаваць з {0}", "TvShows": "Тэлепраграма", "Undefined": "Нявызначана", "UserLockedOutWithName": "Карыстальнік {0} быў заблакіраваны", @@ -114,7 +114,7 @@ "TaskCleanCacheDescription": "Выдаляе файлы кэша, якія больш не патрэбныя сістэме.", "TaskRefreshChapterImages": "Вынуць выявы раздзелаў", "TaskRefreshLibrary": "Сканаваць бібліятэку", - "TaskRefreshLibraryDescription": "Скануе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метададзеныя.", + "TaskRefreshLibraryDescription": "Сканіруе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метаданыя.", "TaskCleanLogs": "Ачысціць журнал", "TaskRefreshPeople": "Абнавіць выканаўцаў", "TaskRefreshPeopleDescription": "Абнаўленне метаданых для акцёраў і рэжысёраў у вашай медыятэцы.", @@ -137,5 +137,5 @@ "TaskExtractMediaSegments": "Сканіраванне медыя-сегмента", "TaskMoveTrickplayImages": "Перанесці месцазнаходжанне выявы Trickplay", "CleanupUserDataTask": "Задача па ачыстцы дадзеных карыстальніка", - "CleanupUserDataTaskDescription": "Ачысьціць усе дадзеныя карыстальніка (стан прагляду, абранае і г.д.) для медыяфайлаў, што адсутнічаюць больш за 90 дзён." + "CleanupUserDataTaskDescription": "Ачышчае ўсе даныя карыстальніка (стан прагляду, абранае і г.д.) для медыяфайлаў, што адсутнічаюць больш за 90 дзён." } -- cgit v1.2.3 From 4138214ac326618635248b6f84b5338166b68b1d Mon Sep 17 00:00:00 2001 From: Dzmitry Zubialevich Date: Mon, 5 Jan 2026 04:34:05 -0500 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 4d769efc3..62ada96c0 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -16,7 +16,7 @@ "Collections": "Калекцыі", "Default": "Па змаўчанні", "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}", - "Folders": "Тэчкі", + "Folders": "Папкі", "Favorites": "Абранае", "External": "Знешні", "Genres": "Жанры", @@ -104,7 +104,7 @@ "UserStartedPlayingItemWithValues": "{0} прайграваецца {1} на {2}", "UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}", "ValueHasBeenAddedToLibrary": "{0} быў дададзены ў вашу медыятэку", - "ValueSpecialEpisodeName": "Спецэпізод - {0}", + "ValueSpecialEpisodeName": "Спецвыпуск - {0}", "VersionNumber": "Версія {0}", "TasksMaintenanceCategory": "Абслугоўванне", "TasksLibraryCategory": "Бібліятэка", -- cgit v1.2.3 From 244757c92cae8dc1cb12dfb4a4e976bbfd7e751d Mon Sep 17 00:00:00 2001 From: ZeusCraft10 Date: Mon, 5 Jan 2026 23:03:22 -0500 Subject: Fix KeyNotFoundException in CryptographyProvider.Verify When a password hash is missing the 'iterations' parameter, Verify now throws a descriptive FormatException instead of KeyNotFoundException. - Extract GetIterationsParameter() helper method to avoid code duplication - Provide distinct error messages for missing vs invalid parameters - Add comprehensive unit tests for CryptographyProvider --- CONTRIBUTORS.md | 1 + .../Cryptography/CryptographyProvider.cs | 27 +++++- .../Cryptography/CryptographyProviderTests.cs | 102 +++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs (limited to 'Emby.Server.Implementations') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 0fd509f84..171509382 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -209,6 +209,7 @@ - [Kirill Nikiforov](https://github.com/allmazz) - [bjorntp](https://github.com/bjorntp) - [martenumberto](https://github.com/martenumberto) + - [ZeusCraft10](https://github.com/ZeusCraft10) # Emby Contributors diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs index 5380c45d8..0381c4d35 100644 --- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs @@ -39,22 +39,24 @@ namespace Emby.Server.Implementations.Cryptography { if (string.Equals(hash.Id, "PBKDF2", StringComparison.Ordinal)) { + var iterations = GetIterationsParameter(hash); return hash.Hash.SequenceEqual( Rfc2898DeriveBytes.Pbkdf2( password, hash.Salt, - int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture), + iterations, HashAlgorithmName.SHA1, 32)); } if (string.Equals(hash.Id, "PBKDF2-SHA512", StringComparison.Ordinal)) { + var iterations = GetIterationsParameter(hash); return hash.Hash.SequenceEqual( Rfc2898DeriveBytes.Pbkdf2( password, hash.Salt, - int.Parse(hash.Parameters["iterations"], CultureInfo.InvariantCulture), + iterations, HashAlgorithmName.SHA512, DefaultOutputLength)); } @@ -62,6 +64,27 @@ namespace Emby.Server.Implementations.Cryptography throw new NotSupportedException($"Can't verify hash with id: {hash.Id}"); } + /// + /// Extracts and validates the iterations parameter from a password hash. + /// + /// The password hash containing parameters. + /// The number of iterations. + /// Thrown when iterations parameter is missing or invalid. + private static int GetIterationsParameter(PasswordHash hash) + { + if (!hash.Parameters.TryGetValue("iterations", out var iterationsStr)) + { + throw new FormatException($"Password hash with id '{hash.Id}' is missing required 'iterations' parameter."); + } + + if (!int.TryParse(iterationsStr, CultureInfo.InvariantCulture, out var iterations)) + { + throw new FormatException($"Password hash with id '{hash.Id}' has invalid 'iterations' parameter: '{iterationsStr}'."); + } + + return iterations; + } + /// public byte[] GenerateSalt() => GenerateSalt(DefaultSaltLength); diff --git a/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs new file mode 100644 index 000000000..052bdf740 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Cryptography/CryptographyProviderTests.cs @@ -0,0 +1,102 @@ +using System; +using Emby.Server.Implementations.Cryptography; +using MediaBrowser.Model.Cryptography; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Cryptography; + +public class CryptographyProviderTests +{ + private readonly CryptographyProvider _sut = new(); + + [Fact] + public void CreatePasswordHash_WithPassword_ReturnsHashWithIterations() + { + var hash = _sut.CreatePasswordHash("testpassword"); + + Assert.Equal("PBKDF2-SHA512", hash.Id); + Assert.True(hash.Parameters.ContainsKey("iterations")); + Assert.NotEmpty(hash.Salt.ToArray()); + Assert.NotEmpty(hash.Hash.ToArray()); + } + + [Fact] + public void Verify_WithValidPassword_ReturnsTrue() + { + const string password = "testpassword"; + var hash = _sut.CreatePasswordHash(password); + + Assert.True(_sut.Verify(hash, password)); + } + + [Fact] + public void Verify_WithWrongPassword_ReturnsFalse() + { + var hash = _sut.CreatePasswordHash("correctpassword"); + + Assert.False(_sut.Verify(hash, "wrongpassword")); + } + + [Fact] + public void Verify_PBKDF2_MissingIterations_ThrowsFormatException() + { + var hash = PasswordHash.Parse("$PBKDF2$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"); + + var exception = Assert.Throws(() => _sut.Verify(hash, "password")); + Assert.Contains("missing required 'iterations' parameter", exception.Message, StringComparison.Ordinal); + } + + [Fact] + public void Verify_PBKDF2SHA512_MissingIterations_ThrowsFormatException() + { + var hash = PasswordHash.Parse("$PBKDF2-SHA512$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"); + + var exception = Assert.Throws(() => _sut.Verify(hash, "password")); + Assert.Contains("missing required 'iterations' parameter", exception.Message, StringComparison.Ordinal); + } + + [Fact] + public void Verify_PBKDF2_InvalidIterationsFormat_ThrowsFormatException() + { + var hash = PasswordHash.Parse("$PBKDF2$iterations=abc$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"); + + var exception = Assert.Throws(() => _sut.Verify(hash, "password")); + Assert.Contains("invalid 'iterations' parameter", exception.Message, StringComparison.Ordinal); + } + + [Fact] + public void Verify_PBKDF2SHA512_InvalidIterationsFormat_ThrowsFormatException() + { + var hash = PasswordHash.Parse("$PBKDF2-SHA512$iterations=notanumber$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"); + + var exception = Assert.Throws(() => _sut.Verify(hash, "password")); + Assert.Contains("invalid 'iterations' parameter", exception.Message, StringComparison.Ordinal); + } + + [Fact] + public void Verify_UnsupportedHashId_ThrowsNotSupportedException() + { + var hash = PasswordHash.Parse("$UNKNOWN$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D"); + + Assert.Throws(() => _sut.Verify(hash, "password")); + } + + [Fact] + public void GenerateSalt_ReturnsNonEmptyArray() + { + var salt = _sut.GenerateSalt(); + + Assert.NotEmpty(salt); + } + + [Theory] + [InlineData(16)] + [InlineData(32)] + [InlineData(64)] + public void GenerateSalt_WithLength_ReturnsArrayOfSpecifiedLength(int length) + { + var salt = _sut.GenerateSalt(length); + + Assert.Equal(length, salt.Length); + } +} -- cgit v1.2.3 From e233eee07b318ee59d528e715ad0ba490013b1db Mon Sep 17 00:00:00 2001 From: SilentSkies Date: Fri, 9 Jan 2026 17:14:09 -0500 Subject: Translated using Weblate (Welsh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cy/ --- Emby.Server.Implementations/Localization/Core/cy.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json index 794a8e4ce..e1ec9d22b 100644 --- a/Emby.Server.Implementations/Localization/Core/cy.json +++ b/Emby.Server.Implementations/Localization/Core/cy.json @@ -8,7 +8,7 @@ "CameraImageUploadedFrom": "Mae delwedd camera newydd wedi'i lanlwytho o {0}", "Books": "Llyfrau", "AuthenticationSucceededWithUserName": "{0} wedi’i ddilysu’n llwyddiannus", - "Artists": "Artistiaid", + "Artists": "Crewyr", "AppDeviceValues": "Ap: {0}, Dyfais: {1}", "Albums": "Albwmau", "Genres": "Genres", @@ -67,7 +67,7 @@ "NotificationOptionAudioPlayback": "Dechreuwyd chwarae sain", "MessageServerConfigurationUpdated": "Mae gosodiadau gweinydd wedi'i ddiweddaru", "MessageNamedServerConfigurationUpdatedWithValue": "Mae adran gosodiadau gweinydd {0} wedi'i diweddaru", - "FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu gan {0}", + "FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu o {0}", "ValueHasBeenAddedToLibrary": "{0} wedi'i hychwanegu at eich llyfrgell gyfryngau", "UserStoppedPlayingItemWithValues": "{0} wedi gorffen chwarae {1} ar {2}", "UserStartedPlayingItemWithValues": "{0} yn chwarae {1} ar {2}", @@ -123,5 +123,8 @@ "TaskRefreshChapterImages": "Echdynnu Lluniau Pennod", "TaskCleanCacheDescription": "Dileu ffeiliau cache nad oes eu hangen ar y system mwyach.", "TaskCleanCache": "Gwaghau Ffolder Cache", - "HearingImpaired": "Nam ar y clyw" + "HearingImpaired": "Nam ar y clyw", + "TaskAudioNormalization": "Gwastatau Sain", + "TaskAudioNormalizationDescription": "Yn sganio ffeiliau am ddata gwastatau sain.", + "TaskRefreshTrickplayImages": "Creuwch lluniau Trickplay" } -- cgit v1.2.3 From 22ee5113d0273031094fa5b9dc83fb29d46c88c2 Mon Sep 17 00:00:00 2001 From: SilentSkies Date: Fri, 9 Jan 2026 17:22:26 -0500 Subject: Translated using Weblate (Welsh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cy/ --- Emby.Server.Implementations/Localization/Core/cy.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json index e1ec9d22b..3f10b6adc 100644 --- a/Emby.Server.Implementations/Localization/Core/cy.json +++ b/Emby.Server.Implementations/Localization/Core/cy.json @@ -126,5 +126,8 @@ "HearingImpaired": "Nam ar y clyw", "TaskAudioNormalization": "Gwastatau Sain", "TaskAudioNormalizationDescription": "Yn sganio ffeiliau am ddata gwastatau sain.", - "TaskRefreshTrickplayImages": "Creuwch lluniau Trickplay" + "TaskRefreshTrickplayImages": "Creuwch lluniau Trickplay", + "TaskRefreshTrickplayImagesDescription": "Creu rhagolygon Trickplay ar gyfer fideos mewn llyfrgelloedd gweithredol.", + "TaskDownloadMissingLyrics": "Lawrlwytho geiriau coll", + "TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon" } -- cgit v1.2.3 From c4f4dcc181f300f0b74b2e4b259c9fdedb4f71ef Mon Sep 17 00:00:00 2001 From: SilentSkies Date: Fri, 9 Jan 2026 17:33:27 -0500 Subject: Translated using Weblate (Welsh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cy/ --- Emby.Server.Implementations/Localization/Core/cy.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json index 3f10b6adc..d9ebd13f0 100644 --- a/Emby.Server.Implementations/Localization/Core/cy.json +++ b/Emby.Server.Implementations/Localization/Core/cy.json @@ -129,5 +129,8 @@ "TaskRefreshTrickplayImages": "Creuwch lluniau Trickplay", "TaskRefreshTrickplayImagesDescription": "Creu rhagolygon Trickplay ar gyfer fideos mewn llyfrgelloedd gweithredol.", "TaskDownloadMissingLyrics": "Lawrlwytho geiriau coll", - "TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon" + "TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon", + "TaskCleanCollectionsAndPlaylists": "Glanhau casgliadau a rhestrau chwarae", + "TaskCleanCollectionsAndPlaylistsDescription": "Dileu eitemau o gasgliadau a rhestrau chwarae sydd ddim yn bodoli bellach.", + "TaskExtractMediaSegments": "Sganio Darnau Cyfryngau" } -- cgit v1.2.3 From cf9051c27773ffa764a785fddd045b001f5861ad Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Sat, 10 Jan 2026 06:11:27 -0500 Subject: Backport pull request #15961 from jellyfin/release-10.11.z Fix crash when plugin repository has an invalid URL Original-merge: 317a3a47c374fc4cb58f4c7a537b33fabb4c764f Merged-by: Bond-009 Backported-by: Bond_009 --- Emby.Server.Implementations/Updates/InstallationManager.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 5ff400160..5f9e29b56 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -156,6 +156,11 @@ namespace Emby.Server.Implementations.Updates _logger.LogError(ex, "The URL configured for the plugin repository manifest URL is not valid: {Manifest}", manifest); return Array.Empty(); } + catch (NotSupportedException ex) + { + _logger.LogError(ex, "The URL scheme configured for the plugin repository is not supported: {Manifest}", manifest); + return Array.Empty(); + } catch (HttpRequestException ex) { _logger.LogError(ex, "An error occurred while accessing the plugin manifest: {Manifest}", manifest); -- cgit v1.2.3 From 62e51fd00a429cf06e72e49c1e94cbd499fd366a Mon Sep 17 00:00:00 2001 From: Samuvel Paul Date: Sat, 10 Jan 2026 06:39:07 -0500 Subject: Translated using Weblate (Malayalam) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/ --- Emby.Server.Implementations/Localization/Core/ml.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json index 5c3449381..8c20ded3a 100644 --- a/Emby.Server.Implementations/Localization/Core/ml.json +++ b/Emby.Server.Implementations/Localization/Core/ml.json @@ -2,12 +2,12 @@ "AppDeviceValues": "അപ്ലിക്കേഷൻ: {0}, ഉപകരണം: {1}", "Application": "അപ്ലിക്കേഷൻ", "AuthenticationSucceededWithUserName": "{0} വിജയകരമായി പ്രാമാണീകരിച്ചു", - "CameraImageUploadedFrom": "Camera 0 from എന്നതിൽ നിന്ന് ഒരു പുതിയ ക്യാമറ ചിത്രം അപ്‌ലോഡുചെയ്‌തു", + "CameraImageUploadedFrom": "{0} എന്നതിൽ നിന്ന് ഒരു പുതിയ ക്യാമറ ചിത്രം അപ്‌ലോഡുചെയ്‌തു", "ChapterNameValue": "അധ്യായം {0}", "DeviceOfflineWithName": "{0} വിച്ഛേദിച്ചു", "DeviceOnlineWithName": "{0} ബന്ധിപ്പിച്ചു", "FailedLoginAttemptWithUserName": "{0}ൽ നിന്നുള്ള പ്രവേശന ശ്രമം പരാജയപ്പെട്ടു", - "Forced": "നിർബന്ധിച്ചു", + "Forced": "നിർബന്ധിതമായി", "HeaderFavoriteAlbums": "പ്രിയപ്പെട്ട ആൽബങ്ങൾ", "HeaderFavoriteArtists": "പ്രിയപ്പെട്ട കലാകാരന്മാർ", "HeaderFavoriteEpisodes": "പ്രിയപ്പെട്ട എപ്പിസോഡുകൾ", @@ -114,7 +114,7 @@ "Artists": "കലാകാരന്മാർ", "Shows": "ഷോകൾ", "Default": "സ്ഥിരസ്ഥിതി", - "Favorites": "പ്രിയങ്കരങ്ങൾ", + "Favorites": "പ്രിയപ്പെട്ടവ", "Books": "പുസ്തകങ്ങൾ", "Genres": "വിഭാഗങ്ങൾ", "Channels": "ചാനലുകൾ", -- cgit v1.2.3 From 9e480f6efb4bc0e1f0d1323ed7ed5a7208fded99 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 11 Nov 2025 17:41:46 +0100 Subject: Update to .NET 10.0 --- .editorconfig | 7 ++++ .github/workflows/ci-openapi.yml | 4 +- .vscode/launch.json | 6 +-- Directory.Packages.props | 47 +++++++++------------- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- .../Emby.Server.Implementations.csproj | 3 +- Jellyfin.Api/Helpers/HlsHelpers.cs | 10 +---- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Data/Jellyfin.Data.csproj | 2 +- .../FullSystemBackup/BackupService.cs | 15 +++---- .../Jellyfin.Server.Implementations.csproj | 3 +- .../Extensions/ApiServiceCollectionExtensions.cs | 11 ++--- Jellyfin.Server/Jellyfin.Server.csproj | 5 +-- MediaBrowser.Common/MediaBrowser.Common.csproj | 7 +--- MediaBrowser.Common/Net/NetworkConstants.cs | 1 - MediaBrowser.Common/Net/NetworkUtils.cs | 10 ++--- .../MediaBrowser.Controller.csproj | 4 +- .../MediaBrowser.LocalMetadata.csproj | 2 +- .../MediaBrowser.MediaEncoding.csproj | 3 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 5 +-- MediaBrowser.Model/Net/IPData.cs | 5 +-- .../MediaBrowser.Providers.csproj | 3 +- .../MediaBrowser.XbmcMetadata.csproj | 2 +- README.md | 4 +- .../Emby.Server.Implementations.Fuzz.csproj | 2 +- fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh | 2 +- fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj | 2 +- fuzz/Jellyfin.Api.Fuzz/fuzz.sh | 2 +- global.json | 2 +- .../Jellyfin.Database.Implementations.csproj | 2 +- .../Locking/OptimisticLockBehavior.cs | 2 + .../Locking/PessimisticLockBehavior.cs | 1 + .../Jellyfin.Database.Providers.Sqlite.csproj | 2 +- .../Jellyfin.Drawing.Skia.csproj | 2 +- src/Jellyfin.Drawing/Jellyfin.Drawing.csproj | 2 +- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 2 +- src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj | 3 +- .../Jellyfin.MediaEncoding.Hls.csproj | 5 +-- .../Jellyfin.MediaEncoding.Keyframes.csproj | 6 +-- src/Jellyfin.Networking/Jellyfin.Networking.csproj | 2 +- src/Jellyfin.Networking/Manager/NetworkManager.cs | 21 +++++----- tests/Directory.Build.props | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 1 - .../Jellyfin.LiveTv.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 1 - .../Jellyfin.Server.Tests.csproj | 1 - tests/Jellyfin.Server.Tests/ParseNetworkTests.cs | 7 ++-- 48 files changed, 99 insertions(+), 140 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/.editorconfig b/.editorconfig index 313b02563..fa679f120 100644 --- a/.editorconfig +++ b/.editorconfig @@ -379,6 +379,9 @@ dotnet_diagnostic.CA1720.severity = suggestion # disable warning CA1724: Type names should not match namespaces dotnet_diagnostic.CA1724.severity = suggestion +# disable warning CA1873: Avoid potentially expensive logging +dotnet_diagnostic.CA1873.severity = suggestion + # disable warning CA1805: Do not initialize unnecessarily dotnet_diagnostic.CA1805.severity = suggestion @@ -400,6 +403,10 @@ dotnet_diagnostic.CA1861.severity = suggestion # disable warning CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = suggestion +# TODO: Reevaluate when false positives are fixed: https://github.com/dotnet/roslyn-analyzers/issues/7699 +# disable warning CA2025: Do not pass 'IDisposable' instances into unawaited tasks +dotnet_diagnostic.CA2025.severity = suggestion + # disable warning CA2253: Named placeholders should not be numeric values dotnet_diagnostic.CA2253.severity = suggestion diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 8406d1d2d..cf2a2868d 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -35,7 +35,7 @@ jobs: name: openapi-head retention-days: 14 if-no-files-found: error - path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json + path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net10.0/openapi.json openapi-base: name: OpenAPI - BASE @@ -73,7 +73,7 @@ jobs: name: openapi-base retention-days: 14 if-no-files-found: error - path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net9.0/openapi.json + path: tests/Jellyfin.Server.Integration.Tests/bin/Release/net10.0/openapi.json openapi-diff: permissions: diff --git a/.vscode/launch.json b/.vscode/launch.json index d97d8de84..681f068b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net10.0/jellyfin.dll", "args": [], "cwd": "${workspaceFolder}/Jellyfin.Server", "console": "internalConsole", @@ -22,7 +22,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net10.0/jellyfin.dll", "args": ["--nowebclient"], "cwd": "${workspaceFolder}/Jellyfin.Server", "console": "internalConsole", @@ -34,7 +34,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net9.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/net10.0/jellyfin.dll", "args": ["--nowebclient", "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"], "cwd": "${workspaceFolder}/Jellyfin.Server", "console": "internalConsole", diff --git a/Directory.Packages.props b/Directory.Packages.props index 31b46da61..d78e2d021 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,32 +26,27 @@ - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -81,12 +76,8 @@ - - - - - - + + diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index b84c96116..97b52e42a 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -6,7 +6,7 @@ - net9.0 + net10.0 false true true diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 645a74aea..3faeae380 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -19,7 +19,7 @@ - net9.0 + net10.0 false true diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 15843730e..f312fb4db 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -27,7 +27,6 @@ - @@ -39,7 +38,7 @@ - net9.0 + net10.0 false true diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs index cad8d650e..15540338b 100644 --- a/Jellyfin.Api/Helpers/HlsHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsHelpers.cs @@ -45,15 +45,9 @@ public static class HlsHelpers using var reader = new StreamReader(fileStream); var count = 0; - while (!reader.EndOfStream) + string? line; + while ((line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false)) is not null) { - var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (line is null) - { - // Nothing currently in buffer. - break; - } - if (line.Contains("#EXTINF:", StringComparison.OrdinalIgnoreCase)) { count++; diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 25feaa2d7..3ccf7a746 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -6,7 +6,7 @@ - net9.0 + net10.0 true diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index fd852ece9..f7660f35d 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 false true true diff --git a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs index 70483c36c..30094a88c 100644 --- a/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs +++ b/Jellyfin.Server.Implementations/FullSystemBackup/BackupService.cs @@ -102,7 +102,7 @@ public class BackupService : IBackupService } BackupManifest? manifest; - var manifestStream = zipArchiveEntry.Open(); + var manifestStream = await zipArchiveEntry.OpenAsync().ConfigureAwait(false); await using (manifestStream.ConfigureAwait(false)) { manifest = await JsonSerializer.DeserializeAsync(manifestStream, _serializerSettings).ConfigureAwait(false); @@ -160,7 +160,7 @@ public class BackupService : IBackupService } HistoryRow[] historyEntries; - var historyArchive = historyEntry.Open(); + var historyArchive = await historyEntry.OpenAsync().ConfigureAwait(false); await using (historyArchive.ConfigureAwait(false)) { historyEntries = await JsonSerializer.DeserializeAsync(historyArchive).ConfigureAwait(false) ?? @@ -204,7 +204,7 @@ public class BackupService : IBackupService continue; } - var zipEntryStream = zipEntry.Open(); + var zipEntryStream = await zipEntry.OpenAsync().ConfigureAwait(false); await using (zipEntryStream.ConfigureAwait(false)) { _logger.LogInformation("Restore backup of {Table}", entityType.Type.Name); @@ -329,7 +329,7 @@ public class BackupService : IBackupService _logger.LogInformation("Begin backup of entity {Table}", entityType.SourceName); var zipEntry = zipArchive.CreateEntry(NormalizePathSeparator(Path.Combine("Database", $"{entityType.SourceName}.json"))); var entities = 0; - var zipEntryStream = zipEntry.Open(); + var zipEntryStream = await zipEntry.OpenAsync().ConfigureAwait(false); await using (zipEntryStream.ConfigureAwait(false)) { var jsonSerializer = new Utf8JsonWriter(zipEntryStream); @@ -366,7 +366,7 @@ public class BackupService : IBackupService foreach (var item in Directory.EnumerateFiles(_applicationPaths.ConfigurationDirectoryPath, "*.xml", SearchOption.TopDirectoryOnly) .Union(Directory.EnumerateFiles(_applicationPaths.ConfigurationDirectoryPath, "*.json", SearchOption.TopDirectoryOnly))) { - zipArchive.CreateEntryFromFile(item, NormalizePathSeparator(Path.Combine("Config", Path.GetFileName(item)))); + await zipArchive.CreateEntryFromFileAsync(item, NormalizePathSeparator(Path.Combine("Config", Path.GetFileName(item)))).ConfigureAwait(false); } void CopyDirectory(string source, string target, string filter = "*") @@ -380,6 +380,7 @@ public class BackupService : IBackupService foreach (var item in Directory.EnumerateFiles(source, filter, SearchOption.AllDirectories)) { + // TODO: @bond make async zipArchive.CreateEntryFromFile(item, NormalizePathSeparator(Path.Combine(target, Path.GetRelativePath(source, item)))); } } @@ -405,7 +406,7 @@ public class BackupService : IBackupService CopyDirectory(Path.Combine(_applicationPaths.InternalMetadataPath), Path.Combine("Data", "metadata")); } - var manifestStream = zipArchive.CreateEntry(ManifestEntryName).Open(); + var manifestStream = await zipArchive.CreateEntry(ManifestEntryName).OpenAsync().ConfigureAwait(false); await using (manifestStream.ConfigureAwait(false)) { await JsonSerializer.SerializeAsync(manifestStream, manifest).ConfigureAwait(false); @@ -505,7 +506,7 @@ public class BackupService : IBackupService return null; } - var manifestStream = manifestEntry.Open(); + var manifestStream = await manifestEntry.OpenAsync().ConfigureAwait(false); await using (manifestStream.ConfigureAwait(false)) { return await JsonSerializer.DeserializeAsync(manifestStream, _serializerSettings).ConfigureAwait(false); diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 6693ab8db..4f0c37722 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 false true @@ -27,7 +27,6 @@ - diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 8373fd50f..c7bcda442 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -174,7 +174,7 @@ namespace Jellyfin.Server.Extensions if (config.KnownProxies.Length == 0) { options.ForwardedHeaders = ForwardedHeaders.None; - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); options.KnownProxies.Clear(); } else @@ -184,7 +184,7 @@ namespace Jellyfin.Server.Extensions } // Only set forward limit if we have some known proxies or some known networks. - if (options.KnownProxies.Count != 0 || options.KnownNetworks.Count != 0) + if (options.KnownProxies.Count != 0 || options.KnownIPNetworks.Count != 0) { options.ForwardLimit = null; } @@ -290,10 +290,7 @@ namespace Jellyfin.Server.Extensions } else if (NetworkUtils.TryParseToSubnet(allowedProxies[i], out var subnet)) { - if (subnet is not null) - { - AddIPAddress(config, options, subnet.Prefix, subnet.PrefixLength); - } + AddIPAddress(config, options, subnet.BaseAddress, subnet.PrefixLength); } else if (NetworkUtils.TryParseHost(allowedProxies[i], out var addresses, config.EnableIPv4, config.EnableIPv6)) { @@ -323,7 +320,7 @@ namespace Jellyfin.Server.Extensions } else { - options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(addr, prefixLength)); + options.KnownIPNetworks.Add(new System.Net.IPNetwork(addr, prefixLength)); } } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 14ab114fb..9f5bf01a0 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -8,7 +8,7 @@ jellyfin Exe - net9.0 + net10.0 false false true @@ -44,9 +44,6 @@ - - - diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 5f15f845c..c128c2b6b 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -18,17 +18,12 @@ - - - - - - net9.0 + net10.0 false true true diff --git a/MediaBrowser.Common/Net/NetworkConstants.cs b/MediaBrowser.Common/Net/NetworkConstants.cs index ccef5d271..cec996a1a 100644 --- a/MediaBrowser.Common/Net/NetworkConstants.cs +++ b/MediaBrowser.Common/Net/NetworkConstants.cs @@ -1,5 +1,4 @@ using System.Net; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace MediaBrowser.Common.Net; diff --git a/MediaBrowser.Common/Net/NetworkUtils.cs b/MediaBrowser.Common/Net/NetworkUtils.cs index 24ed47a81..9c9a35a16 100644 --- a/MediaBrowser.Common/Net/NetworkUtils.cs +++ b/MediaBrowser.Common/Net/NetworkUtils.cs @@ -6,7 +6,6 @@ using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; using Jellyfin.Extensions; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace MediaBrowser.Common.Net; @@ -196,7 +195,7 @@ public static partial class NetworkUtils /// An . /// Boolean signaling if negated or not negated values should be parsed. /// True if parsing was successful. - public static bool TryParseToSubnet(ReadOnlySpan value, [NotNullWhen(true)] out IPNetwork? result, bool negated = false) + public static bool TryParseToSubnet(ReadOnlySpan value, out IPNetwork result, bool negated = false) { // If multiple IP addresses are in a comma-separated string, the individual addresses may contain leading and/or trailing whitespace value = value.Trim(); @@ -210,7 +209,7 @@ public static partial class NetworkUtils if (isAddressNegated != negated) { - result = null; + result = default; return false; } @@ -235,7 +234,7 @@ public static partial class NetworkUtils } } - result = null; + result = default; return false; } @@ -330,7 +329,7 @@ public static partial class NetworkUtils /// The broadcast address. public static IPAddress GetBroadcastAddress(IPNetwork network) { - var addressBytes = network.Prefix.GetAddressBytes(); + var addressBytes = network.BaseAddress.GetAddressBytes(); uint ipAddress = BitConverter.ToUInt32(addressBytes, 0); uint ipMaskV4 = BitConverter.ToUInt32(CidrToMask(network.PrefixLength, AddressFamily.InterNetwork).GetAddressBytes(), 0); uint broadCastIPAddress = ipAddress | ~ipMaskV4; @@ -347,7 +346,6 @@ public static partial class NetworkUtils public static bool SubnetContainsAddress(IPNetwork network, IPAddress address) { ArgumentNullException.ThrowIfNull(address); - ArgumentNullException.ThrowIfNull(network); if (address.IsIPv4MappedToIPv6) { diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index b5d14e94b..0025080cc 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -19,9 +19,7 @@ - - @@ -36,7 +34,7 @@ - net9.0 + net10.0 false true true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 8e3c8cf7f..c3c26085c 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -11,7 +11,7 @@ - net9.0 + net10.0 false true diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index be7eeda92..fc11047a7 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -6,7 +6,7 @@ - net9.0 + net10.0 false true @@ -26,7 +26,6 @@ - diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index ef025d02d..c655c4ccb 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -14,7 +14,7 @@ - net9.0 + net10.0 false true true @@ -37,13 +37,10 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive - - diff --git a/MediaBrowser.Model/Net/IPData.cs b/MediaBrowser.Model/Net/IPData.cs index c116d883e..e016ffea1 100644 --- a/MediaBrowser.Model/Net/IPData.cs +++ b/MediaBrowser.Model/Net/IPData.cs @@ -1,6 +1,5 @@ using System.Net; using System.Net.Sockets; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace MediaBrowser.Model.Net; @@ -66,9 +65,9 @@ public class IPData { if (Address.Equals(IPAddress.None)) { - return Subnet.Prefix.AddressFamily.Equals(IPAddress.None) + return Subnet.BaseAddress.AddressFamily.Equals(IPAddress.None) ? AddressFamily.Unspecified - : Subnet.Prefix.AddressFamily; + : Subnet.BaseAddress.AddressFamily; } else { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 34b3104b0..ed0c63b97 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -18,7 +18,6 @@ - @@ -28,7 +27,7 @@ - net9.0 + net10.0 false true diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index b195af96c..cfb3533f3 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -15,7 +15,7 @@ - net9.0 + net10.0 false true diff --git a/README.md b/README.md index 9830e8e9c..e546e7f11 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ --- -Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET platform to enable full cross-platform support. +Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps. Jellyfin is descended from Emby's 3.5.2 release and ported to the .NET platform to enable full cross-platform support. There are no strings attached, no premium licenses or features, and no hidden agendas: just a team that wants to build something better and work together to achieve it. We welcome anyone who is interested in joining us in our quest! @@ -133,7 +133,7 @@ A second option is to build the project and then run the resulting executable fi ```bash dotnet build # Build the project -cd Jellyfin.Server/bin/Debug/net9.0 # Change into the build output directory +cd Jellyfin.Server/bin/Debug/net10.0 # Change into the build output directory ``` 2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj index 1373d2fe0..1ac7402f9 100644 --- a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj +++ b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 diff --git a/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh b/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh index 8183bb37a..771aa6677 100755 --- a/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh +++ b/fuzz/Emby.Server.Implementations.Fuzz/fuzz.sh @@ -8,4 +8,4 @@ cp bin/Emby.Server.Implementations.dll . dotnet build mkdir -p Findings -AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net9.0/Emby.Server.Implementations.Fuzz "$1" +AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net10.0/Emby.Server.Implementations.Fuzz "$1" diff --git a/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj b/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj index 04c7be11d..dad2f8e4e 100644 --- a/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj +++ b/fuzz/Jellyfin.Api.Fuzz/Jellyfin.Api.Fuzz.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 diff --git a/fuzz/Jellyfin.Api.Fuzz/fuzz.sh b/fuzz/Jellyfin.Api.Fuzz/fuzz.sh index 15148e1bb..537de905d 100755 --- a/fuzz/Jellyfin.Api.Fuzz/fuzz.sh +++ b/fuzz/Jellyfin.Api.Fuzz/fuzz.sh @@ -8,4 +8,4 @@ cp bin/Jellyfin.Api.dll . dotnet build mkdir -p Findings -AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net9.0/Jellyfin.Api.Fuzz "$1" +AFL_SKIP_BIN_CHECK=1 afl-fuzz -i "Testcases/$1" -o "Findings/$1" -t 5000 ./bin/Debug/net10.0/Jellyfin.Api.Fuzz "$1" diff --git a/global.json b/global.json index 2e13a6387..867a4cfa0 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.0", + "version": "10.0.0", "rollForward": "latestMinor" } } diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Jellyfin.Database.Implementations.csproj b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Jellyfin.Database.Implementations.csproj index 28c4972d2..0b29a71cb 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Jellyfin.Database.Implementations.csproj +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Jellyfin.Database.Implementations.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 false true diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/OptimisticLockBehavior.cs b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/OptimisticLockBehavior.cs index 7bcc7eeca..76ffa5a9e 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/OptimisticLockBehavior.cs +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/OptimisticLockBehavior.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1873 + using System; using System.Data.Common; using System.Linq; diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/PessimisticLockBehavior.cs b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/PessimisticLockBehavior.cs index 2d6bc6902..404292e8e 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/PessimisticLockBehavior.cs +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/Locking/PessimisticLockBehavior.cs @@ -1,5 +1,6 @@ #pragma warning disable MT1013 // Releasing lock without guarantee of execution #pragma warning disable MT1012 // Acquiring lock without guarantee of releasing +#pragma warning disable CA1873 using System; using System.Data; diff --git a/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/Jellyfin.Database.Providers.Sqlite.csproj b/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/Jellyfin.Database.Providers.Sqlite.csproj index 03e5fc495..aeee52701 100644 --- a/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/Jellyfin.Database.Providers.Sqlite.csproj +++ b/src/Jellyfin.Database/Jellyfin.Database.Providers.Sqlite/Jellyfin.Database.Providers.Sqlite.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 false true diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index ba402dfe0..f7c20463f 100644 --- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -6,7 +6,7 @@ - net9.0 + net10.0 false true diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj index 5f4b3fe8d..a442f7457 100644 --- a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj +++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj @@ -6,7 +6,7 @@ - net9.0 + net10.0 false true diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index f52fd014d..9a7cf4aab 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 false true true diff --git a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj index f04c02504..575441de9 100644 --- a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj +++ b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 true @@ -13,7 +13,6 @@ - diff --git a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj index 80b5aa84e..902f51376 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj +++ b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 true @@ -12,9 +12,6 @@ - - - diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index cc8d942eb..5e7e2090c 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 true @@ -22,10 +22,6 @@ - - - - <_Parameter1>Jellyfin.MediaEncoding.Keyframes.Tests diff --git a/src/Jellyfin.Networking/Jellyfin.Networking.csproj b/src/Jellyfin.Networking/Jellyfin.Networking.csproj index 1a146549d..36b9581a7 100644 --- a/src/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/src/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 false true diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 126d9f15c..ed7b6dfde 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -16,7 +16,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace Jellyfin.Networking.Manager; @@ -376,7 +375,7 @@ public class NetworkManager : INetworkManager, IDisposable if (localNetworkAddresses.Length > 0 && !string.IsNullOrWhiteSpace(localNetworkAddresses[0])) { var bindAddresses = localNetworkAddresses.Select(p => NetworkUtils.TryParseToSubnet(p, out var network) - ? network.Prefix + ? network.BaseAddress : (interfaces.Where(x => x.Name.Equals(p, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Address) .FirstOrDefault() ?? IPAddress.None)) @@ -545,7 +544,7 @@ public class NetworkManager : INetworkManager, IDisposable { foreach (var lan in _lanSubnets) { - var lanPrefix = lan.Prefix; + var lanPrefix = lan.BaseAddress; publishedServerUrls.Add( new PublishedServerUriOverride( new IPData(lanPrefix, new IPNetwork(lanPrefix, lan.PrefixLength)), @@ -554,9 +553,9 @@ public class NetworkManager : INetworkManager, IDisposable false)); } } - else if (NetworkUtils.TryParseToSubnet(identifier, out var result) && result is not null) + else if (NetworkUtils.TryParseToSubnet(identifier, out var result)) { - var data = new IPData(result.Prefix, result); + var data = new IPData(result.BaseAddress, result); publishedServerUrls.Add( new PublishedServerUriOverride( data, @@ -623,7 +622,7 @@ public class NetworkManager : INetworkManager, IDisposable var parts = details.Split(','); if (NetworkUtils.TryParseToSubnet(parts[0], out var subnet)) { - var address = subnet.Prefix; + var address = subnet.BaseAddress; var index = int.Parse(parts[1], CultureInfo.InvariantCulture); if (address.AddressFamily == AddressFamily.InterNetwork || address.AddressFamily == AddressFamily.InterNetworkV6) { @@ -920,7 +919,7 @@ public class NetworkManager : INetworkManager, IDisposable { if (NetworkUtils.TryParseToSubnet(address, out var subnet)) { - return IsInLocalNetwork(subnet.Prefix); + return IsInLocalNetwork(subnet.BaseAddress); } return NetworkUtils.TryParseHost(address, out var addresses, IsIPv4Enabled, IsIPv6Enabled) @@ -1171,13 +1170,13 @@ public class NetworkManager : INetworkManager, IDisposable var logLevel = debug ? LogLevel.Debug : LogLevel.Information; if (_logger.IsEnabled(logLevel)) { - _logger.Log(logLevel, "Defined LAN subnets: {Subnets}", _lanSubnets.Select(s => s.Prefix + "/" + s.PrefixLength)); - _logger.Log(logLevel, "Defined LAN exclusions: {Subnets}", _excludedSubnets.Select(s => s.Prefix + "/" + s.PrefixLength)); - _logger.Log(logLevel, "Used LAN subnets: {Subnets}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).Select(s => s.Prefix + "/" + s.PrefixLength)); + _logger.Log(logLevel, "Defined LAN subnets: {Subnets}", _lanSubnets.Select(s => s.BaseAddress + "/" + s.PrefixLength)); + _logger.Log(logLevel, "Defined LAN exclusions: {Subnets}", _excludedSubnets.Select(s => s.BaseAddress + "/" + s.PrefixLength)); + _logger.Log(logLevel, "Used LAN subnets: {Subnets}", _lanSubnets.Where(s => !_excludedSubnets.Contains(s)).Select(s => s.BaseAddress + "/" + s.PrefixLength)); _logger.Log(logLevel, "Filtered interface addresses: {Addresses}", _interfaces.OrderByDescending(x => x.AddressFamily == AddressFamily.InterNetwork).Select(x => x.Address)); _logger.Log(logLevel, "Bind Addresses {Addresses}", GetAllBindInterfaces(false).OrderByDescending(x => x.AddressFamily == AddressFamily.InterNetwork).Select(x => x.Address)); _logger.Log(logLevel, "Remote IP filter is {Type}", config.IsRemoteIPFilterBlacklist ? "Blocklist" : "Allowlist"); - _logger.Log(logLevel, "Filtered subnets: {Subnets}", _remoteAddressFilter.Select(s => s.Prefix + "/" + s.PrefixLength)); + _logger.Log(logLevel, "Filtered subnets: {Subnets}", _remoteAddressFilter.Select(s => s.BaseAddress + "/" + s.PrefixLength)); } } } diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 6b851021f..feec35307 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -4,7 +4,7 @@ - net9.0 + net10.0 false diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 015018910..6b84c4438 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -10,7 +10,6 @@ - diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj index fdcf7d61e..bdf6bc383 100644 --- a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj +++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 8228c0df7..7b0e23788 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -5,7 +5,6 @@ - diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 5fea805ae..21596e0ed 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -5,7 +5,6 @@ - diff --git a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs index 123266d29..14f4c33b6 100644 --- a/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs +++ b/tests/Jellyfin.Server.Tests/ParseNetworkTests.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace Jellyfin.Server.Tests { @@ -87,7 +86,7 @@ namespace Jellyfin.Server.Tests // Need this here as ::1 and 127.0.0.1 are in them by default. options.KnownProxies.Clear(); - options.KnownNetworks.Clear(); + options.KnownIPNetworks.Clear(); ApiServiceCollectionExtensions.AddProxyAddresses(settings, hostList, options); @@ -97,10 +96,10 @@ namespace Jellyfin.Server.Tests Assert.True(options.KnownProxies.Contains(item)); } - Assert.Equal(knownNetworks.Length, options.KnownNetworks.Count); + Assert.Equal(knownNetworks.Length, options.KnownIPNetworks.Count); foreach (var item in knownNetworks) { - Assert.NotNull(options.KnownNetworks.FirstOrDefault(x => x.Prefix.Equals(item.Prefix) && x.PrefixLength == item.PrefixLength)); + Assert.NotEqual(default, options.KnownIPNetworks.FirstOrDefault(x => x.BaseAddress.Equals(item.BaseAddress) && x.PrefixLength == item.PrefixLength)); } } -- cgit v1.2.3 From 098e8c6fed6aa1fd873f255b09b58e4780c087d6 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 16 Nov 2025 19:31:45 +0100 Subject: Replace AlphanumericComparator with new CompareOptions.NumericOrdering --- Emby.Naming/Video/VideoListResolver.cs | 17 +++- .../Sorting/StudioComparer.cs | 4 +- MediaBrowser.Controller/Sorting/SortExtensions.cs | 4 +- src/Jellyfin.Extensions/AlphanumericComparator.cs | 112 --------------------- .../AlphanumericComparatorTests.cs | 34 ------- 5 files changed, 18 insertions(+), 153 deletions(-) delete mode 100644 src/Jellyfin.Extensions/AlphanumericComparator.cs delete mode 100644 tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index a3134f3f6..4247fea0e 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -136,19 +137,27 @@ namespace Emby.Naming.Video if (videos.Count > 1) { - var groups = videos.GroupBy(x => ResolutionRegex().IsMatch(x.Files[0].FileNameWithoutExtension)).ToList(); + var groups = videos + .Select(x => (filename: x.Files[0].FileNameWithoutExtension.ToString(), value: x)) + .Select(x => (x.filename, resolutionMatch: ResolutionRegex().Match(x.filename), x.value)) + .GroupBy(x => x.resolutionMatch.Success) + .ToList(); + videos.Clear(); + + StringComparer comparer = StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.NumericOrdering); foreach (var group in groups) { if (group.Key) { videos.InsertRange(0, group - .OrderByDescending(x => ResolutionRegex().Match(x.Files[0].FileNameWithoutExtension.ToString()).Value, new AlphanumericComparator()) - .ThenBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + .OrderByDescending(x => x.resolutionMatch.Value, comparer) + .ThenBy(x => x.filename, comparer) + .Select(x => x.value)); } else { - videos.AddRange(group.OrderBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + videos.AddRange(group.OrderBy(x => x.filename, comparer).Select(x => x.value)); } } } diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index 0edffb783..6d041cf11 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -1,11 +1,11 @@ #pragma warning disable CS1591 using System; +using System.Globalization; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; -using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Sorting { @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Sorting ArgumentNullException.ThrowIfNull(x); ArgumentNullException.ThrowIfNull(y); - return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault()); + return CultureInfo.InvariantCulture.CompareInfo.Compare(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault(), CompareOptions.NumericOrdering); } } } diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index f9c0d39dd..ec8878dcb 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -1,7 +1,9 @@ #pragma warning disable CS1591 using System; +using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Jellyfin.Extensions; @@ -9,7 +11,7 @@ namespace MediaBrowser.Controller.Sorting { public static class SortExtensions { - private static readonly AlphanumericComparator _comparer = new AlphanumericComparator(); + private static readonly StringComparer _comparer = StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.NumericOrdering); public static IEnumerable OrderByString(this IEnumerable list, Func getName) { diff --git a/src/Jellyfin.Extensions/AlphanumericComparator.cs b/src/Jellyfin.Extensions/AlphanumericComparator.cs deleted file mode 100644 index 299e2f94a..000000000 --- a/src/Jellyfin.Extensions/AlphanumericComparator.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Jellyfin.Extensions -{ - /// - /// Alphanumeric . - /// - public class AlphanumericComparator : IComparer - { - /// - /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. - /// - /// The first object to compare. - /// The second object to compare. - /// A signed integer that indicates the relative values of x and y. - public static int CompareValues(string? s1, string? s2) - { - if (s1 is null && s2 is null) - { - return 0; - } - - if (s1 is null) - { - return -1; - } - - if (s2 is null) - { - return 1; - } - - int len1 = s1.Length; - int len2 = s2.Length; - - // Early return for empty strings - if (len1 == 0 && len2 == 0) - { - return 0; - } - - if (len1 == 0) - { - return -1; - } - - if (len2 == 0) - { - return 1; - } - - int pos1 = 0; - int pos2 = 0; - - do - { - int start1 = pos1; - int start2 = pos2; - - bool isNum1 = char.IsDigit(s1[pos1++]); - bool isNum2 = char.IsDigit(s2[pos2++]); - - while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1) - { - pos1++; - } - - while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2) - { - pos2++; - } - - var span1 = s1.AsSpan(start1, pos1 - start1); - var span2 = s2.AsSpan(start2, pos2 - start2); - - if (isNum1 && isNum2) - { - // Trim leading zeros so we can compare the length - // of the strings to find the largest number - span1 = span1.TrimStart('0'); - span2 = span2.TrimStart('0'); - var span1Len = span1.Length; - var span2Len = span2.Length; - if (span1Len < span2Len) - { - return -1; - } - - if (span1Len > span2Len) - { - return 1; - } - } - - int result = span1.CompareTo(span2, StringComparison.InvariantCulture); - if (result != 0) - { - return result; - } - } while (pos1 < len1 && pos2 < len2); - - return len1 - len2; - } - - /// - public int Compare(string? x, string? y) - { - return CompareValues(x, y); - } - } -} diff --git a/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs deleted file mode 100644 index 105e2a52a..000000000 --- a/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using Xunit; - -namespace Jellyfin.Extensions.Tests -{ - public class AlphanumericComparatorTests - { - // InlineData is pre-sorted - [Theory] - [InlineData(null, "", "1", "9", "10", "a", "z")] - [InlineData("50F", "100F", "SR9", "SR100")] - [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")] - [InlineData("Hard drive 2GB", "Hard drive 20GB")] - [InlineData("b", "e", "è", "ě", "f", "g", "k")] - [InlineData("123456789", "123456789a", "abc", "abcd")] - [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")] - [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")] - [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")] - [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")] - [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")] - [InlineData("a5", "a11")] - [InlineData("a05a", "a5b")] - [InlineData("a5a", "a05b")] - [InlineData("6xxx", "007asdf")] - [InlineData("00042Q", "42s")] - public void AlphanumericComparatorTest(params string?[] strings) - { - var copy = strings.Reverse().ToArray(); - Array.Sort(copy, new AlphanumericComparator()); - Assert.Equal(strings, copy); - } - } -} -- cgit v1.2.3 From d089537bca2a6998cd132122901fcf198d67d89e Mon Sep 17 00:00:00 2001 From: Richard Torhan Date: Thu, 27 Nov 2025 18:07:07 +0100 Subject: Fix error CA1849: 'ZipFile.ExtractToDirectory(Stream, string, bool)' synchronously blocks. Await 'ZipFile.ExtractToDirectoryAsync(Stream, string, bool, CancellationToken)' instead. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1849) --- Emby.Server.Implementations/Updates/InstallationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 5f9e29b56..6e85cfd77 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -562,7 +562,7 @@ namespace Emby.Server.Implementations.Updates } stream.Position = 0; - ZipFile.ExtractToDirectory(stream, targetDir, true); + await ZipFile.ExtractToDirectoryAsync(stream, targetDir, true); // Ensure we create one or populate existing ones with missing data. await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); -- cgit v1.2.3 From 50dcec1ff5b2f3e5af83536aa390809e8bcae9ab Mon Sep 17 00:00:00 2001 From: Richard Torhan Date: Thu, 27 Nov 2025 18:11:05 +0100 Subject: Fix error CA2016: Forward the 'cancellationToken' parameter to the 'ExtractToDirectoryAsync' method or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token --- Emby.Server.Implementations/Updates/InstallationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 6e85cfd77..67b77a112 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -562,7 +562,7 @@ namespace Emby.Server.Implementations.Updates } stream.Position = 0; - await ZipFile.ExtractToDirectoryAsync(stream, targetDir, true); + await ZipFile.ExtractToDirectoryAsync(stream, targetDir, true, cancellationToken); // Ensure we create one or populate existing ones with missing data. await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); -- cgit v1.2.3 From 6a9bb060ebb7a5e9d4a9a016403f0fad0ad63218 Mon Sep 17 00:00:00 2001 From: rimasx Date: Thu, 15 Jan 2026 09:18:56 -0500 Subject: Translated using Weblate (Estonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/ --- Emby.Server.Implementations/Localization/Core/et.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index 2e692009b..91a0aa663 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -72,7 +72,7 @@ "NotificationOptionApplicationUpdateAvailable": "Rakenduse uuendus on saadaval", "NewVersionIsAvailable": "Jellyfin serveri uus versioon on allalaadimiseks saadaval.", "NameSeasonUnknown": "Tundmatu hooaeg", - "NameSeasonNumber": "Hooaeg {0}", + "NameSeasonNumber": "{0}. hooaeg", "NameInstallFailed": "{0} paigaldamine nurjus", "MusicVideos": "Muusikavideod", "Music": "Muusika", -- cgit v1.2.3 From 09edca8b7a9174c374a7d03bb1ec3aea32d02ffd Mon Sep 17 00:00:00 2001 From: MarcoCoreDuo <90222533+MarcoCoreDuo@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:30:38 -0500 Subject: Backport pull request #15899 from jellyfin/release-10.11.z Fix watched state not kept on Media replace/rename Original-merge: 8433b6d8a41f66f6eef36bb950927c6a6afa1a36 Merged-by: joshuaboniface Backported-by: Bond_009 --- CONTRIBUTORS.md | 1 + .../Library/LibraryManager.cs | 6 ++++ .../Item/BaseItemRepository.cs | 37 ++++++++++++++-------- MediaBrowser.Controller/Entities/BaseItem.cs | 3 ++ MediaBrowser.Controller/Library/ILibraryManager.cs | 8 +++++ .../Persistence/IItemRepository.cs | 8 +++++ MediaBrowser.Providers/Manager/MetadataService.cs | 11 +++++-- 7 files changed, 57 insertions(+), 17 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 171509382..1770db60b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -210,6 +210,7 @@ - [bjorntp](https://github.com/bjorntp) - [martenumberto](https://github.com/martenumberto) - [ZeusCraft10](https://github.com/ZeusCraft10) + - [MarcoCoreDuo](https://github.com/MarcoCoreDuo) # Emby Contributors diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f35d85f65..bdf04edc2 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2202,6 +2202,12 @@ namespace Emby.Server.Implementations.Library public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) => UpdateItemsAsync([item], parent, updateReason, cancellationToken); + /// + public async Task ReattachUserDataAsync(BaseItem item, CancellationToken cancellationToken) + { + await _itemRepository.ReattachUserDataAsync(item, cancellationToken).ConfigureAwait(false); + } + public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason) { if (item.IsFileProtocol) diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index 3b3d3c4f4..646a9c483 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -624,7 +624,6 @@ public sealed class BaseItemRepository var ids = tuples.Select(f => f.Item.Id).ToArray(); var existingItems = context.BaseItems.Where(e => ids.Contains(e.Id)).Select(f => f.Id).ToArray(); - var newItems = tuples.Where(e => !existingItems.Contains(e.Item.Id)).ToArray(); foreach (var item in tuples) { @@ -658,19 +657,6 @@ public sealed class BaseItemRepository context.SaveChanges(); - foreach (var item in newItems) - { - // reattach old userData entries - var userKeys = item.UserDataKey.ToArray(); - var retentionDate = (DateTime?)null; - context.UserData - .Where(e => e.ItemId == PlaceholderId) - .Where(e => userKeys.Contains(e.CustomDataKey)) - .ExecuteUpdate(e => e - .SetProperty(f => f.ItemId, item.Item.Id) - .SetProperty(f => f.RetentionDate, retentionDate)); - } - var itemValueMaps = tuples .Select(e => (e.Item, Values: GetItemValuesToSave(e.Item, e.InheritedTags))) .ToArray(); @@ -766,6 +752,29 @@ public sealed class BaseItemRepository transaction.Commit(); } + /// + public async Task ReattachUserDataAsync(BaseItemDto item, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(item); + cancellationToken.ThrowIfCancellationRequested(); + + var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + + await using (dbContext.ConfigureAwait(false)) + { + var userKeys = item.GetUserDataKeys().ToArray(); + var retentionDate = (DateTime?)null; + await dbContext.UserData + .Where(e => e.ItemId == PlaceholderId) + .Where(e => userKeys.Contains(e.CustomDataKey)) + .ExecuteUpdateAsync( + e => e + .SetProperty(f => f.ItemId, item.Id) + .SetProperty(f => f.RetentionDate, retentionDate), + cancellationToken).ConfigureAwait(false); + } + } + /// public BaseItemDto? RetrieveItem(Guid id) { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index d9d2d0e3a..7586b99e7 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2053,6 +2053,9 @@ namespace MediaBrowser.Controller.Entities public virtual async Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken) => await LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken).ConfigureAwait(false); + public async Task ReattachUserDataAsync(CancellationToken cancellationToken) => + await LibraryManager.ReattachUserDataAsync(this, cancellationToken).ConfigureAwait(false); + /// /// Validates that images within the item are still on the filesystem. /// diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index fcc5ed672..675812ac2 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -281,6 +281,14 @@ namespace MediaBrowser.Controller.Library /// Returns a Task that can be awaited. Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); + /// + /// Reattaches the user data to the item. + /// + /// The item. + /// The cancellation token. + /// A task that represents the asynchronous reattachment operation. + Task ReattachUserDataAsync(BaseItem item, CancellationToken cancellationToken); + /// /// Retrieves the item. /// diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 00c492742..bf80b7d0a 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -35,6 +35,14 @@ public interface IItemRepository Task SaveImagesAsync(BaseItem item, CancellationToken cancellationToken = default); + /// + /// Reattaches the user data to the item. + /// + /// The item. + /// The cancellation token. + /// A task that represents the asynchronous reattachment operation. + Task ReattachUserDataAsync(BaseItem item, CancellationToken cancellationToken); + /// /// Retrieves the item. /// diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index a2102ca9c..e9cb46eab 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -153,7 +153,7 @@ namespace MediaBrowser.Providers.Manager if (isFirstRefresh) { - await SaveItemAsync(metadataResult, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); + await SaveItemAsync(metadataResult, ItemUpdateType.MetadataImport, false, cancellationToken).ConfigureAwait(false); } // Next run metadata providers @@ -247,7 +247,7 @@ namespace MediaBrowser.Providers.Manager } // Save to database - await SaveItemAsync(metadataResult, updateType, cancellationToken).ConfigureAwait(false); + await SaveItemAsync(metadataResult, updateType, isFirstRefresh, cancellationToken).ConfigureAwait(false); } return updateType; @@ -275,9 +275,14 @@ namespace MediaBrowser.Providers.Manager } } - protected async Task SaveItemAsync(MetadataResult result, ItemUpdateType reason, CancellationToken cancellationToken) + protected async Task SaveItemAsync(MetadataResult result, ItemUpdateType reason, bool reattachUserData, CancellationToken cancellationToken) { await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false); + if (reattachUserData) + { + await result.Item.ReattachUserDataAsync(cancellationToken).ConfigureAwait(false); + } + if (result.Item.SupportsPeople && result.People is not null) { var baseItem = result.Item; -- cgit v1.2.3 From afcaec0a894df038f8b88a517a01bace0d3c237c Mon Sep 17 00:00:00 2001 From: Collin-Swish <79892877+Collin-Swish@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:30:39 -0500 Subject: Backport pull request #15965 from jellyfin/release-10.11.z Add mblink creation logic to library update endpoint. Original-merge: 22d593b8e986ecdb42fb1e618bfcf833b0a6f118 Merged-by: crobibero Backported-by: Bond_009 --- .../Library/LibraryManager.cs | 33 +++++++++++++--------- .../Controllers/LibraryStructureController.cs | 11 ++++++++ MediaBrowser.Controller/Library/ILibraryManager.cs | 7 +++++ 3 files changed, 38 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bdf04edc2..f7f5c387e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -3201,19 +3201,7 @@ namespace Emby.Server.Implementations.Library var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - var shortcutFilename = Path.GetFileNameWithoutExtension(path); - - var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - - while (File.Exists(lnk)) - { - shortcutFilename += "1"; - lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - } - - _fileSystem.CreateShortcut(lnk, _appHost.ReverseVirtualPath(path)); - - RemoveContentTypeOverrides(path); + CreateShortcut(virtualFolderPath, pathInfo); if (saveLibraryOptions) { @@ -3378,5 +3366,24 @@ namespace Emby.Server.Implementations.Library return item is UserRootFolder || item.IsVisibleStandalone(user); } + + public void CreateShortcut(string virtualFolderPath, MediaPathInfo pathInfo) + { + var path = pathInfo.Path; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; + + var shortcutFilename = Path.GetFileNameWithoutExtension(path); + + var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + + while (File.Exists(lnk)) + { + shortcutFilename += "1"; + lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + } + + _fileSystem.CreateShortcut(lnk, _appHost.ReverseVirtualPath(path)); + RemoveContentTypeOverrides(path); + } } } diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs index 2a885662b..117811429 100644 --- a/Jellyfin.Api/Controllers/LibraryStructureController.cs +++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs @@ -342,6 +342,17 @@ public class LibraryStructureController : BaseJellyfinApiController return NotFound(); } + LibraryOptions options = item.GetLibraryOptions(); + foreach (var mediaPath in request.LibraryOptions!.PathInfos) + { + if (options.PathInfos.Any(i => i.Path == mediaPath.Path)) + { + continue; + } + + _libraryManager.CreateShortcut(item.Path, mediaPath); + } + item.UpdateLibraryOptions(request.LibraryOptions); return NoContent(); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 675812ac2..df1c98f3f 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -660,5 +660,12 @@ namespace MediaBrowser.Controller.Library /// This exists so plugins can trigger a library scan. /// void QueueLibraryScan(); + + /// + /// Add mblink file for a media path. + /// + /// The path to the virtualfolder. + /// The new virtualfolder. + public void CreateShortcut(string virtualFolderPath, MediaPathInfo pathInfo); } } -- cgit v1.2.3 From c9b7c5bb5665d2eb02381fa12dcce786fee9ae3d Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:30:43 -0500 Subject: Backport pull request #16029 from jellyfin/release-10.11.z Skip hidden directories and .ignore paths in library monitoring Original-merge: 2cb7fb52d2221d9daa39206089b578c2c0fcb549 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/IO/LibraryMonitor.cs | 6 ++++++ Emby.Server.Implementations/Library/IgnorePatterns.cs | 1 + .../Library/IgnorePatternsTests.cs | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index d87ad729e..7cff2a25b 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -352,6 +352,12 @@ namespace Emby.Server.Implementations.IO return; } + var fileInfo = _fileSystem.GetFileSystemInfo(path); + if (DotIgnoreIgnoreRule.IsIgnored(fileInfo, null)) + { + return; + } + // Ignore certain files, If the parent of an ignored path has a change event, ignore that too foreach (var i in _tempIgnoredPaths.Keys) { diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs index fe3a1ce61..5fac2f6b0 100644 --- a/Emby.Server.Implementations/Library/IgnorePatterns.cs +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -83,6 +83,7 @@ namespace Emby.Server.Implementations.Library // Unix hidden files "**/.*", + "**/.*/**", // Mac - if you ever remove the above. // "**/._*", diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs index 07061cfc7..4cb6cb960 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -19,7 +19,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/movies/#recycle", true)] [InlineData("thumbs.db", true)] [InlineData(@"C:\media\movies\movie.avi", false)] - [InlineData("/media/.hiddendir/file.mp4", false)] + [InlineData("/media/.hiddendir/file.mp4", true)] [InlineData("/media/dir/.hiddenfile.mp4", true)] [InlineData("/media/dir/._macjunk.mp4", true)] [InlineData("/volume1/video/Series/@eaDir", true)] @@ -32,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/music/Foo B.A.R", false)] [InlineData("/media/music/Foo B.A.R.", false)] [InlineData("/movies/.zfs/snapshot/AutoM-2023-09", true)] - public void PathIgnored(string path, bool expected) + public void PathIgnored(string path, bool expected) { Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); } -- cgit v1.2.3 From fb32709259c0d1bb38d8ace921dffee7df4dcc39 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Sun, 18 Jan 2026 11:30:44 -0500 Subject: Backport pull request #16046 from jellyfin/release-10.11.z Restore weekly refresh for library folder images Original-merge: 338b480217499bc37ce4bbe214aea0f1f7d9eb6a Merged-by: cvium Backported-by: Bond_009 --- Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs index 273d356a3..a25373326 100644 --- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs +++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs @@ -98,5 +98,11 @@ namespace Emby.Server.Implementations.Images return base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex); } + + protected override bool HasChangedByDate(BaseItem item, ItemImageInfo image) + { + var age = DateTime.UtcNow - image.DateModified; + return age.TotalDays > 7; + } } } -- cgit v1.2.3 From bab4e620e3992da91c3df7602ce64649884172f2 Mon Sep 17 00:00:00 2001 From: Aung Khant Soe Date: Sun, 18 Jan 2026 22:16:33 -0500 Subject: Translated using Weblate (Burmese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/my/ --- Emby.Server.Implementations/Localization/Core/my.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/my.json b/Emby.Server.Implementations/Localization/Core/my.json index 4cb4cdc75..097d0d2fb 100644 --- a/Emby.Server.Implementations/Localization/Core/my.json +++ b/Emby.Server.Implementations/Localization/Core/my.json @@ -126,5 +126,7 @@ "TaskRefreshTrickplayImages": "ထရစ်ခ်ပလေး ပုံများကို ထုတ်မည်", "TaskKeyframeExtractor": "ကီးဖရိန်များကို ထုတ်နုတ်ခြင်း", "TaskCleanCollectionsAndPlaylists": "စုစည်းမှုများနှင့် အစဉ်လိုက်ပြသမှုများကို ရှင်းလင်းမည်", - "HearingImpaired": "အကြားအာရုံ ချို့တဲ့သူ" + "HearingImpaired": "အကြားအာရုံ ချို့တဲ့သူ", + "TaskDownloadMissingLyrics": "ကျန်နေသောသီချင်းစာသားများအား ဒေါင်းလုတ်ဆွဲပါ", + "TaskDownloadMissingLyricsDescription": "သီချင်းများအတွက် သီချင်းစာသား ဒေါင်းလုတ်ဆွဲပါ" } -- cgit v1.2.3 From 7151c4ca218aac3402231cfb2954d26b50dcea93 Mon Sep 17 00:00:00 2001 From: Dzmitry Zubialevich Date: Mon, 19 Jan 2026 01:43:13 -0500 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 62ada96c0..3d598c491 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -50,7 +50,7 @@ "User": "Карыстальнік", "UserDeletedWithName": "Карыстальнік {0} быў выдалены", "UserDownloadingItemWithValues": "{0} спампоўваецца {1}", - "TaskOptimizeDatabase": "Аптымізаваць базу дадзеных", + "TaskOptimizeDatabase": "Аптымізацыя базы даных", "Artists": "Выканаўцы", "UserOfflineFromDevice": "{0} адлучыўся ад {1}", "UserPolicyUpdatedWithName": "Палітыка карыстальніка абноўлена для {0}", @@ -59,8 +59,8 @@ "TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.", "TaskUpdatePluginsDescription": "Спампоўвае і ўсталёўвае абнаўленні для плагінаў, якія сканфігураваныя на аўтаматычнае абнаўленне.", "TaskRefreshChannelsDescription": "Абнаўляе інфармацыю аб інтэрнэт-канале.", - "TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субцітры на аснове канфігурацыі метададзеных.", - "TaskOptimizeDatabaseDescription": "Ушчыльняе базу дадзеных і скарачае вольную прастору. Выкананне гэтай задачы пасля сканавання бібліятэкі або ўнясення іншых зменаў, якія прадугледжваюць мадыфікацыю базы дадзеных, можа палепшыць выдайнасць.", + "TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субцітры на аснове канфігурацыі метаданых.", + "TaskOptimizeDatabaseDescription": "Сціскае базу даных і вызваляе вольную прастору. Выкананне гэтай задачы пасля сканіравання бібліятэкі або іншых змяненняў, якія мадыфікуюць базу даных, можа палепшыць прадукцыйнасць.", "TaskKeyframeExtractor": "Экстрактар ключавых кадраў", "TasksApplicationCategory": "Праграма", "AppDeviceValues": "Праграма: {0}, Прылада: {1}", @@ -136,6 +136,6 @@ "TaskDownloadMissingLyricsDescription": "Спампоўвае тэксты для песняў", "TaskExtractMediaSegments": "Сканіраванне медыя-сегмента", "TaskMoveTrickplayImages": "Перанесці месцазнаходжанне выявы Trickplay", - "CleanupUserDataTask": "Задача па ачыстцы дадзеных карыстальніка", + "CleanupUserDataTask": "Задача па ачыстцы даных карыстальніка", "CleanupUserDataTaskDescription": "Ачышчае ўсе даныя карыстальніка (стан прагляду, абранае і г.д.) для медыяфайлаў, што адсутнічаюць больш за 90 дзён." } -- cgit v1.2.3 From 62474af0c0aece2bd33d7e43b010680d57c90c40 Mon Sep 17 00:00:00 2001 From: queeup Date: Sat, 24 Jan 2026 18:09:20 -0500 Subject: Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- Emby.Server.Implementations/Localization/Core/tr.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 478111049..33f129505 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -30,7 +30,7 @@ "ItemAddedWithName": "{0} kütüphaneye eklendi", "ItemRemovedWithName": "{0} kütüphaneden silindi", "LabelIpAddressValue": "IP adresi: {0}", - "LabelRunningTimeValue": "Çalışma süresi: {0}", + "LabelRunningTimeValue": "Oynatma süresi: {0}", "Latest": "En son", "MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi", "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} sürümüne güncellendi", @@ -42,7 +42,7 @@ "MusicVideos": "Müzik Videoları", "NameInstallFailed": "{0} kurulumu başarısız", "NameSeasonNumber": "{0}. Sezon", - "NameSeasonUnknown": "Bilinmeyen Sezon", + "NameSeasonUnknown": "Sezon Bilinmiyor", "NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir sürümü indirmek için hazır.", "NotificationOptionApplicationUpdateAvailable": "Uygulama güncellemesi mevcut", "NotificationOptionApplicationUpdateInstalled": "Uygulama güncellemesi yüklendi", @@ -57,7 +57,7 @@ "NotificationOptionPluginUpdateInstalled": "Eklenti güncellemesi yüklendi", "NotificationOptionServerRestartRequired": "Sunucunun yeniden başlatılması gerekiyor", "NotificationOptionTaskFailed": "Zamanlanmış görev hatası", - "NotificationOptionUserLockedOut": "Kullanıcı kilitlendi", + "NotificationOptionUserLockedOut": "Kullanıcı hesabı kilitlendi", "NotificationOptionVideoPlayback": "Video oynatma başladı", "NotificationOptionVideoPlaybackStopped": "Video oynatma durduruldu", "Photos": "Fotoğraflar", @@ -82,7 +82,7 @@ "UserCreatedWithName": "{0} kullanıcısı oluşturuldu", "UserDeletedWithName": "{0} kullanıcısı silindi", "UserDownloadingItemWithValues": "{0} kullanıcısı {1} medyasını indiriyor", - "UserLockedOutWithName": "{0} adlı kullanıcı kilitlendi", + "UserLockedOutWithName": "{0} adlı kullanıcı hesabı kilitlendi", "UserOfflineFromDevice": "{0} kullanıcısının {1} ile bağlantısı kesildi", "UserOnlineFromDevice": "{0} kullanıcısı {1} ile çevrimiçi", "UserPasswordChangedWithName": "{0} kullanıcısının parolası değiştirildi", @@ -125,15 +125,15 @@ "TaskKeyframeExtractor": "Ana Kare Çıkarıcı", "External": "Harici", "HearingImpaired": "Duyma Engelli", - "TaskRefreshTrickplayImages": "Trickplay Görselleri Oluştur", - "TaskRefreshTrickplayImagesDescription": "Etkin kütüphanelerdeki videolar için trickplay önizlemeleri oluşturur.", + "TaskRefreshTrickplayImages": "Hızlı Önizleme Görsellerini Oluştur", + "TaskRefreshTrickplayImagesDescription": "Etkin kütüphanelerdeki videolar için hızlı önizleme görselleri oluşturur.", "TaskCleanCollectionsAndPlaylistsDescription": "Artık var olmayan koleksiyon ve çalma listelerindeki ögeleri kaldırır.", "TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin", "TaskAudioNormalizationDescription": "Ses normalleştirme verileri için dosyaları tarar.", "TaskAudioNormalization": "Ses Normalleştirme", "TaskExtractMediaSegments": "Medya Segmenti Tarama", - "TaskMoveTrickplayImages": "Trickplay Görsel Konumunu Taşıma", - "TaskMoveTrickplayImagesDescription": "Mevcut trickplay dosyalarını kütüphane ayarlarına göre taşır.", + "TaskMoveTrickplayImages": "Hızlı Önizleme Görsel Konumunu Taşıma", + "TaskMoveTrickplayImagesDescription": "Mevcut hızlı önizleme dosyalarını kütüphane ayarlarına göre taşır.", "TaskDownloadMissingLyrics": "Eksik şarkı sözlerini indir", "TaskDownloadMissingLyricsDescription": "Şarkı sözlerini indirir", "TaskExtractMediaSegmentsDescription": "MediaSegment özelliği etkin olan eklentilerden medya segmentlerini çıkarır veya alır.", -- cgit v1.2.3 From 411a6cced1f27c846009d5c00e313ddd12daf267 Mon Sep 17 00:00:00 2001 From: ldmheaye Date: Sat, 24 Jan 2026 20:27:03 -0500 Subject: Translated using Weblate (Chinese (Simplified Han script)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- .../Localization/Core/zh-CN.json | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 1bfa4e3c3..e45a51946 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -9,56 +9,56 @@ "Channels": "频道", "ChapterNameValue": "章节 {0}", "Collections": "合集", - "DeviceOfflineWithName": "{0} 已断开", + "DeviceOfflineWithName": "{0} 已断开连接", "DeviceOnlineWithName": "{0} 已连接", "FailedLoginAttemptWithUserName": "来自 {0} 的登录尝试失败", - "Favorites": "我的最爱", + "Favorites": "收藏夹", "Folders": "文件夹", "Genres": "类型", "HeaderAlbumArtists": "专辑艺术家", "HeaderContinueWatching": "继续观看", "HeaderFavoriteAlbums": "收藏的专辑", - "HeaderFavoriteArtists": "最爱的艺术家", - "HeaderFavoriteEpisodes": "最爱的剧集", - "HeaderFavoriteShows": "最爱的节目", - "HeaderFavoriteSongs": "最爱的歌曲", + "HeaderFavoriteArtists": "收藏的艺术家", + "HeaderFavoriteEpisodes": "收藏的剧集", + "HeaderFavoriteShows": "收藏的节目", + "HeaderFavoriteSongs": "收藏的歌曲", "HeaderLiveTV": "电视直播", - "HeaderNextUp": "接下来", + "HeaderNextUp": "接下来播放", "HeaderRecordingGroups": "录制组", "HomeVideos": "家庭视频", "Inherit": "继承", "ItemAddedWithName": "{0} 已添加到媒体库", - "ItemRemovedWithName": "{0} 已从媒体库中移除", + "ItemRemovedWithName": "{0} 已从媒体库移除", "LabelIpAddressValue": "IP 地址:{0}", "LabelRunningTimeValue": "运行时间:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin 服务器已更新", - "MessageApplicationUpdatedTo": "Jellyfin Server 版本已更新为 {0}", + "MessageApplicationUpdatedTo": "Jellyfin 服务器版本已更新到 {0}", "MessageNamedServerConfigurationUpdatedWithValue": "服务器配置 {0} 部分已更新", "MessageServerConfigurationUpdated": "服务器配置已更新", "MixedContent": "混合内容", "Movies": "电影", "Music": "音乐", - "MusicVideos": "音乐视频", + "MusicVideos": "MV", "NameInstallFailed": "{0} 安装失败", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季", - "NewVersionIsAvailable": "Jellyfin Server 有新版本可以下载。", + "NewVersionIsAvailable": "Jellyfin 服务器有新版本可供下载。", "NotificationOptionApplicationUpdateAvailable": "有可用的应用程序更新", "NotificationOptionApplicationUpdateInstalled": "应用程序更新已安装", - "NotificationOptionAudioPlayback": "音频开始播放", + "NotificationOptionAudioPlayback": "音频已开始播放", "NotificationOptionAudioPlaybackStopped": "音频播放已停止", "NotificationOptionCameraImageUploaded": "相机图片已上传", "NotificationOptionInstallationFailed": "安装失败", "NotificationOptionNewLibraryContent": "已添加新内容", - "NotificationOptionPluginError": "插件失败", + "NotificationOptionPluginError": "插件出错", "NotificationOptionPluginInstalled": "插件已安装", "NotificationOptionPluginUninstalled": "插件已卸载", - "NotificationOptionPluginUpdateInstalled": "插件更新已安装", + "NotificationOptionPluginUpdateInstalled": "插件已更新", "NotificationOptionServerRestartRequired": "服务器需要重启", "NotificationOptionTaskFailed": "计划任务失败", "NotificationOptionUserLockedOut": "用户已锁定", - "NotificationOptionVideoPlayback": "视频开始播放", + "NotificationOptionVideoPlayback": "视频已开始播放", "NotificationOptionVideoPlaybackStopped": "视频播放已停止", "Photos": "照片", "Playlists": "播放列表", @@ -72,7 +72,7 @@ "ServerNameNeedsToBeRestarted": "{0} 需要重新启动", "Shows": "节目", "Songs": "歌曲", - "StartupEmbyServerIsLoading": "Jellyfin 服务器加载中。请稍后再试。", + "StartupEmbyServerIsLoading": "Jellyfin 服务器正在启动,请稍后再试。", "SubtitleDownloadFailureForItem": "为 {0} 下载字幕失败", "SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕", "Sync": "同步", @@ -84,11 +84,11 @@ "UserDownloadingItemWithValues": "{0} 正在下载 {1}", "UserLockedOutWithName": "用户 {0} 已被锁定", "UserOfflineFromDevice": "{0} 已从 {1} 断开", - "UserOnlineFromDevice": "{0} 在线,来自 {1}", - "UserPasswordChangedWithName": "已为用户 {0} 更改密码", - "UserPolicyUpdatedWithName": "用户协议已经被更新为 {0}", - "UserStartedPlayingItemWithValues": "{0} 已在 {2} 上开始播放 {1}", - "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}", + "UserOnlineFromDevice": "{0} 已在 {1} 上线", + "UserPasswordChangedWithName": "用户 {0} 的密码已更改", + "UserPolicyUpdatedWithName": "用户协议已更新为 {0}", + "UserStartedPlayingItemWithValues": "{0} 在 {2} 上开始播放 {1}", + "UserStoppedPlayingItemWithValues": "{0} 在 {2} 上停止播放 {1}", "ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中", "ValueSpecialEpisodeName": "特典 - {0}", "VersionNumber": "版本 {0}", -- cgit v1.2.3 From a676391af2bf9f8de4c14d38f49fae3084534761 Mon Sep 17 00:00:00 2001 From: ldmheaye Date: Sat, 24 Jan 2026 20:35:10 -0500 Subject: Translated using Weblate (Chinese (Simplified Han script)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index e45a51946..317bbb626 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -5,7 +5,7 @@ "Artists": "艺术家", "AuthenticationSucceededWithUserName": "{0} 认证成功", "Books": "书籍", - "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传", + "CameraImageUploadedFrom": "新的相机照片已从 {0} 上传", "Channels": "频道", "ChapterNameValue": "章节 {0}", "Collections": "合集", -- cgit v1.2.3 From bc6c3b1013a8556d68bcb64969a23cfb5ce80722 Mon Sep 17 00:00:00 2001 From: ldmheaye Date: Sat, 24 Jan 2026 20:54:13 -0500 Subject: Translated using Weblate (Chinese (Simplified Han script)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 317bbb626..62e39b60c 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -5,13 +5,13 @@ "Artists": "艺术家", "AuthenticationSucceededWithUserName": "{0} 认证成功", "Books": "书籍", - "CameraImageUploadedFrom": "新的相机照片已从 {0} 上传", + "CameraImageUploadedFrom": "已从 {0} 上传新的相机照片", "Channels": "频道", "ChapterNameValue": "章节 {0}", "Collections": "合集", "DeviceOfflineWithName": "{0} 已断开连接", "DeviceOnlineWithName": "{0} 已连接", - "FailedLoginAttemptWithUserName": "来自 {0} 的登录尝试失败", + "FailedLoginAttemptWithUserName": "来自 {0} 的登录失败", "Favorites": "收藏夹", "Folders": "文件夹", "Genres": "类型", @@ -48,7 +48,7 @@ "NotificationOptionApplicationUpdateInstalled": "应用程序更新已安装", "NotificationOptionAudioPlayback": "音频已开始播放", "NotificationOptionAudioPlaybackStopped": "音频播放已停止", - "NotificationOptionCameraImageUploaded": "相机图片已上传", + "NotificationOptionCameraImageUploaded": "相机照片已上传", "NotificationOptionInstallationFailed": "安装失败", "NotificationOptionNewLibraryContent": "已添加新内容", "NotificationOptionPluginError": "插件出错", @@ -79,8 +79,8 @@ "System": "系统", "TvShows": "电视剧", "User": "用户", - "UserCreatedWithName": "用户 {0} 已创建", - "UserDeletedWithName": "用户 {0} 已删除", + "UserCreatedWithName": "已创建用户 {0}", + "UserDeletedWithName": "已删除用户 {0}", "UserDownloadingItemWithValues": "{0} 正在下载 {1}", "UserLockedOutWithName": "用户 {0} 已被锁定", "UserOfflineFromDevice": "{0} 已从 {1} 断开", -- cgit v1.2.3 From 441a41b2ebe9d65f7ef9fcf4546d33e62c9cdb63 Mon Sep 17 00:00:00 2001 From: ldmheaye Date: Sat, 24 Jan 2026 23:20:11 -0500 Subject: Translated using Weblate (Chinese (Simplified Han script)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 62e39b60c..b9635105a 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -57,7 +57,7 @@ "NotificationOptionPluginUpdateInstalled": "插件已更新", "NotificationOptionServerRestartRequired": "服务器需要重启", "NotificationOptionTaskFailed": "计划任务失败", - "NotificationOptionUserLockedOut": "用户已锁定", + "NotificationOptionUserLockedOut": "用户已被锁定", "NotificationOptionVideoPlayback": "视频已开始播放", "NotificationOptionVideoPlaybackStopped": "视频播放已停止", "Photos": "照片", -- cgit v1.2.3 From 92f3c8cf15b265020bca9386f434a8b968a338af Mon Sep 17 00:00:00 2001 From: queeup Date: Sun, 25 Jan 2026 04:50:00 -0500 Subject: Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- Emby.Server.Implementations/Localization/Core/tr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 33f129505..a07e6864e 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -74,7 +74,7 @@ "Songs": "Şarkılar", "StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} sağlayıcısından indirilemedi", + "SubtitleDownloadFailureFromForItem": "{1} için altyazılar {0} sağlayıcısından indirilemedi", "Sync": "Eşzamanlama", "System": "Sistem", "TvShows": "Diziler", @@ -98,8 +98,8 @@ "TasksLibraryCategory": "Kütüphane", "TasksMaintenanceCategory": "Bakım", "TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.", - "TaskDownloadMissingSubtitlesDescription": "Meta veri yapılandırmasına dayalı olarak eksik alt yazılar için internette arama yapar.", - "TaskDownloadMissingSubtitles": "Eksik alt yazıları indir", + "TaskDownloadMissingSubtitlesDescription": "Meta veri yapılandırmasına dayalı olarak eksik altyazılar için internette arama yapar.", + "TaskDownloadMissingSubtitles": "Eksik altyazıları indir", "TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.", "TaskRefreshChannels": "Kanalları Yenile", "TaskCleanTranscodeDescription": "Bir günden daha eski kod dönüştürme dosyalarını siler.", -- cgit v1.2.3 From b9e5cce3838ec0cc89c67c48d8c2c40348bebc6f Mon Sep 17 00:00:00 2001 From: Weblate Date: Tue, 27 Jan 2026 14:12:25 -0500 Subject: Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ --- Emby.Server.Implementations/Localization/Core/ar.json | 1 - Emby.Server.Implementations/Localization/Core/bg-BG.json | 1 - Emby.Server.Implementations/Localization/Core/ca.json | 1 - Emby.Server.Implementations/Localization/Core/cs.json | 1 - Emby.Server.Implementations/Localization/Core/da.json | 1 - Emby.Server.Implementations/Localization/Core/de.json | 1 - Emby.Server.Implementations/Localization/Core/el.json | 1 - Emby.Server.Implementations/Localization/Core/en-GB.json | 1 - Emby.Server.Implementations/Localization/Core/es-AR.json | 1 - Emby.Server.Implementations/Localization/Core/es-MX.json | 1 - Emby.Server.Implementations/Localization/Core/es.json | 1 - Emby.Server.Implementations/Localization/Core/fa.json | 1 - Emby.Server.Implementations/Localization/Core/fr-CA.json | 1 - Emby.Server.Implementations/Localization/Core/fr.json | 1 - Emby.Server.Implementations/Localization/Core/gsw.json | 1 - Emby.Server.Implementations/Localization/Core/he.json | 1 - Emby.Server.Implementations/Localization/Core/hr.json | 1 - Emby.Server.Implementations/Localization/Core/hu.json | 1 - Emby.Server.Implementations/Localization/Core/it.json | 1 - Emby.Server.Implementations/Localization/Core/kk.json | 1 - Emby.Server.Implementations/Localization/Core/ko.json | 1 - Emby.Server.Implementations/Localization/Core/lt-LT.json | 1 - Emby.Server.Implementations/Localization/Core/ms.json | 1 - Emby.Server.Implementations/Localization/Core/nb.json | 1 - Emby.Server.Implementations/Localization/Core/nl.json | 1 - Emby.Server.Implementations/Localization/Core/pl.json | 1 - Emby.Server.Implementations/Localization/Core/pt-BR.json | 1 - Emby.Server.Implementations/Localization/Core/pt-PT.json | 1 - Emby.Server.Implementations/Localization/Core/ru.json | 1 - Emby.Server.Implementations/Localization/Core/sk.json | 1 - Emby.Server.Implementations/Localization/Core/sl-SI.json | 1 - Emby.Server.Implementations/Localization/Core/sv.json | 1 - Emby.Server.Implementations/Localization/Core/tr.json | 1 - Emby.Server.Implementations/Localization/Core/zh-CN.json | 1 - Emby.Server.Implementations/Localization/Core/zh-HK.json | 1 - 35 files changed, 35 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index d09a7884e..7ce8baef5 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -73,7 +73,6 @@ "Shows": "العروض", "Songs": "الأغاني", "StartupEmbyServerIsLoading": "يتم تحميل خادم Jellyfin . الرجاء المحاولة بعد قليل.", - "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}", "SubtitleDownloadFailureFromForItem": "فشل تحميل الترجمات من {0} ل {1}", "Sync": "مزامنة", "System": "النظام", diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index fd3666ef1..92b8e5d56 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -73,7 +73,6 @@ "Shows": "Сериали", "Songs": "Песни", "StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.", - "SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}", "SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени", "Sync": "Синхронизиране", "System": "Система", diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 596df6348..82cc1857b 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -73,7 +73,6 @@ "Shows": "Sèries", "Songs": "Cançons", "StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho de nou en una estona.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Els subtítols per a {1} no s'han pogut baixar de {0}", "Sync": "Sincronitza", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index e14edcffa..4d2477044 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -73,7 +73,6 @@ "Shows": "Seriály", "Songs": "Skladby", "StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.", - "SubtitleDownloadFailureForItem": "Stahování titulků selhalo pro {0}", "SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo", "Sync": "Synchronizace", "System": "Systém", diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index bbee38ba5..8b0d8745d 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -73,7 +73,6 @@ "Shows": "Serier", "Songs": "Sange", "StartupEmbyServerIsLoading": "Jellyfin er i gang med at starte. Prøv igen om et øjeblik.", - "SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}", "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}", "Sync": "Synkroniser", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 0b042c8fe..e9a1630d9 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -73,7 +73,6 @@ "Shows": "Serien", "Songs": "Lieder", "StartupEmbyServerIsLoading": "Jellyfin-Server lädt. Bitte versuche es gleich noch einmal.", - "SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}", "SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden", "Sync": "Synchronisation", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 2ba2085da..87362ff8e 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -73,7 +73,6 @@ "Shows": "Σειρές", "Songs": "Τραγούδια", "StartupEmbyServerIsLoading": "Ο διακομιστής Jellyfin φορτώνει. Περιμένετε λίγο και δοκιμάστε ξανά.", - "SubtitleDownloadFailureForItem": "Οι υπότιτλοι απέτυχαν να κατέβουν για {0}", "SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}", "Sync": "Συγχρονισμός", "System": "Σύστημα", diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index 720f550b3..bd5be0b1f 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -73,7 +73,6 @@ "Shows": "Shows", "Songs": "Songs", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "Sync": "Sync", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index 1f8af4c8a..ce0044f64 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -73,7 +73,6 @@ "Shows": "Series", "Songs": "Canciones", "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 2830c657b..6748fff4c 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -73,7 +73,6 @@ "Shows": "Programas", "Songs": "Canciones", "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.", - "SubtitleDownloadFailureForItem": "Falló la descarga de subtítulos para {0}", "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 1ec5eaa2a..b9c57afe6 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -73,7 +73,6 @@ "Shows": "Series", "Songs": "Canciones", "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", - "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}", "SubtitleDownloadFailureFromForItem": "Fallo en la descarga de subtítulos desde {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index ff14c1367..90cd3a58e 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -73,7 +73,6 @@ "Shows": "سریال‌ها", "Songs": "موسیقی‌ها", "StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.", - "SubtitleDownloadFailureForItem": "دانلود زیرنویس برای {0} ناموفق بود", "SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد", "Sync": "همگام‌سازی", "System": "سیستم", diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 6d079d2f5..a8964e8b6 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -73,7 +73,6 @@ "Shows": "Séries", "Songs": "Chansons", "StartupEmbyServerIsLoading": "Serveur Jellyfin en cours de chargement. Réessayez dans quelques instants.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", "Sync": "Synchroniser", "System": "Système", diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 8bf41c02a..b2a2e502a 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -73,7 +73,6 @@ "Shows": "Séries", "Songs": "Chansons", "StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.", - "SubtitleDownloadFailureForItem": "Le téléchargement des sous-titres pour {0} a échoué.", "SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}", "Sync": "Synchroniser", "System": "Système", diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index e1ee8cf7c..9be6f05ee 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -73,7 +73,6 @@ "Shows": "Serie", "Songs": "Lieder", "StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde", "Sync": "Synchronisation", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 90c921898..ef95a639f 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -73,7 +73,6 @@ "Shows": "סדרות", "Songs": "שירים", "StartupEmbyServerIsLoading": "שרת Jellyfin בתהליך טעינה. נא לנסות שוב בקרוב.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "הורדת כתוביות מ־{0} עבור {1} נכשלה", "Sync": "סנכרון", "System": "מערכת", diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 67263d3b2..eb75cfd49 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -73,7 +73,6 @@ "Shows": "Serije", "Songs": "Pjesme", "StartupEmbyServerIsLoading": "Jellyfin server se učitava. Pokušajte ponovo uskoro.", - "SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}", "SubtitleDownloadFailureFromForItem": "Prijevod nije uspješno preuzet od {0} za {1}", "Sync": "Sinkronizacija", "System": "Sustav", diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 81a996330..813d79923 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -73,7 +73,6 @@ "Shows": "Sorozatok", "Songs": "Számok", "StartupEmbyServerIsLoading": "A Jellyfin kiszolgáló betöltődik. Próbálja újra hamarosan.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0}, ehhez: {1}", "Sync": "Szinkronizálás", "System": "Rendszer", diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 421c4ee30..c2974704b 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -73,7 +73,6 @@ "Shows": "Serie TV", "Songs": "Brani", "StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.", - "SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}", "SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}", "Sync": "Sincronizza", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index e050196bc..fc5fcf3c4 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -73,7 +73,6 @@ "Shows": "Körsetımder", "Songs": "Äuender", "StartupEmbyServerIsLoading": "Jellyfin Server jüktelude. Ärekettı köp ūzamai qaitalañyz.", - "SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз", "SubtitleDownloadFailureFromForItem": "{1} üşın subtitrlerdı {0} közınen jüktep alu sätsız", "Sync": "Ündestıru", "System": "Jüie", diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 3d1b1ed27..2b24ea2c8 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -73,7 +73,6 @@ "Shows": "시리즈", "Songs": "노래", "StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다", "Sync": "동기화", "System": "시스템", diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 3918ab81c..bdf63b4ca 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -73,7 +73,6 @@ "Shows": "Laidos", "Songs": "Kūriniai", "StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}", "Sync": "Sinchronizuoti", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index 971f79c2c..2be04be80 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -73,7 +73,6 @@ "Shows": "Tayangan", "Songs": "Lagu-lagu", "StartupEmbyServerIsLoading": "Pelayan Jellyfin sedang dimuatkan. Sila cuba sebentar lagi.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Muat turun sarikata gagal dari {0} untuk {1}", "Sync": "Segerak", "System": "Sistem", diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index e73c56cb9..cd0315720 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -73,7 +73,6 @@ "Shows": "Serier", "Songs": "Sanger", "StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.", - "SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}", "SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}", "Sync": "Synkroniser", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 09246bd11..534c64e93 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -73,7 +73,6 @@ "Shows": "Series", "Songs": "Nummers", "StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden. Probeer het later opnieuw.", - "SubtitleDownloadFailureForItem": "Downloaden van ondertiteling voor {0} is mislukt", "SubtitleDownloadFailureFromForItem": "Ondertiteling kon niet gedownload worden van {0} voor {1}", "Sync": "Synchronisatie", "System": "Systeem", diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index 8ca22ac04..f1c19ac1d 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -73,7 +73,6 @@ "Shows": "Seriale", "Songs": "Utwory", "StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.", - "SubtitleDownloadFailureForItem": "Pobieranie napisów dla {0} zakończone niepowodzeniem", "SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}", "Sync": "Synchronizacja", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index dc5bff161..8e76c6c63 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -73,7 +73,6 @@ "Shows": "Séries", "Songs": "Músicas", "StartupEmbyServerIsLoading": "O Servidor Jellyfin está carregando. Por favor, tente novamente mais tarde.", - "SubtitleDownloadFailureForItem": "Download de legendas falhou para {0}", "SubtitleDownloadFailureFromForItem": "Houve um problema ao baixar as legendas de {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index 17284854f..a27036493 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -73,7 +73,6 @@ "Shows": "Séries", "Songs": "Músicas", "StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente mais tarde.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas a partir de {0} para {1}", "Sync": "Sincronização", "System": "Sistema", diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 1470a538c..03bce0ebd 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -73,7 +73,6 @@ "Shows": "Сериалы", "Songs": "Композиции", "StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.", - "SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить", "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}", "Sync": "Синхронизация", "System": "Система", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 1de78eeae..7c8d86047 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -73,7 +73,6 @@ "Shows": "Seriály", "Songs": "Skladby", "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.", - "SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo", "SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo", "Sync": "Synchronizácia", "System": "Systém", diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index ff92db2f2..7c7c88e28 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -73,7 +73,6 @@ "Shows": "Serije", "Songs": "Pesmi", "StartupEmbyServerIsLoading": "Jellyfin strežnik se zaganja. Poskusite ponovno kasneje.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}", "Sync": "Sinhroniziraj", "System": "Sistem", diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 1ee1a5366..23acd3c53 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -73,7 +73,6 @@ "Shows": "Serier", "Songs": "Låtar", "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.", - "SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades", "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} till {1}", "Sync": "Synk", "System": "System", diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index a07e6864e..d13f662e4 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -73,7 +73,6 @@ "Shows": "Diziler", "Songs": "Şarkılar", "StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{1} için altyazılar {0} sağlayıcısından indirilemedi", "Sync": "Eşzamanlama", "System": "Sistem", diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index b9635105a..0a0795d41 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -73,7 +73,6 @@ "Shows": "节目", "Songs": "歌曲", "StartupEmbyServerIsLoading": "Jellyfin 服务器正在启动,请稍后再试。", - "SubtitleDownloadFailureForItem": "为 {0} 下载字幕失败", "SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕", "Sync": "同步", "System": "系统", diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index c8800e256..e57a0c5b0 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -73,7 +73,6 @@ "Shows": "節目", "Songs": "歌曲", "StartupEmbyServerIsLoading": "正在載入 Jellyfin,請稍後再試。", - "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕", "Sync": "同步", "System": "系統", -- cgit v1.2.3 From ec4744709df1664f263937ae41baadc48d7d6c9b Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:11:23 -0500 Subject: Backport pull request #15816 from jellyfin/release-10.11.z Fix artist display order Original-merge: a2b1936e73f6638bf07d5e1afd339a1e4404027a Merged-by: joshuaboniface Backported-by: Bond_009 --- Emby.Server.Implementations/Dto/DtoService.cs | 53 ++++++++-------------- .../Item/BaseItemRepository.cs | 17 ++++++- 2 files changed, 35 insertions(+), 35 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index c5dc3b054..b392340f7 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1051,16 +1051,16 @@ namespace Emby.Server.Implementations.Dto // Include artists that are not in the database yet, e.g., just added via metadata editor // var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList(); - dto.ArtistItems = _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]) - .Where(e => e.Value.Length > 0) - .Select(i => - { - return new NameGuidPair - { - Name = i.Key, - Id = i.Value.First().Id - }; - }).Where(i => i is not null).ToArray(); + var artistsLookup = _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]); + + dto.ArtistItems = hasArtist.Artists + .Where(name => !string.IsNullOrWhiteSpace(name)) + .Distinct() + .Select(name => artistsLookup.TryGetValue(name, out var artists) && artists.Length > 0 + ? new NameGuidPair { Name = name, Id = artists[0].Id } + : null) + .Where(item => item is not null) + .ToArray(); } if (item is IHasAlbumArtist hasAlbumArtist) @@ -1085,31 +1085,16 @@ namespace Emby.Server.Implementations.Dto // }) // .ToList(); - dto.AlbumArtists = hasAlbumArtist.AlbumArtists - // .Except(foundArtists, new DistinctNameComparer()) - .Select(i => - { - // This should not be necessary but we're seeing some cases of it - if (string.IsNullOrEmpty(i)) - { - return null; - } - - var artist = _libraryManager.GetArtist(i, new DtoOptions(false) - { - EnableImages = false - }); - if (artist is not null) - { - return new NameGuidPair - { - Name = artist.Name, - Id = artist.Id - }; - } + var albumArtistsLookup = _libraryManager.GetArtists([.. hasAlbumArtist.AlbumArtists.Where(e => !string.IsNullOrWhiteSpace(e))]); - return null; - }).Where(i => i is not null).ToArray(); + dto.AlbumArtists = hasAlbumArtist.AlbumArtists + .Where(name => !string.IsNullOrWhiteSpace(name)) + .Distinct() + .Select(name => albumArtistsLookup.TryGetValue(name, out var albumArtists) && albumArtists.Length > 0 + ? new NameGuidPair { Name = name, Id = albumArtists[0].Id } + : null) + .Where(item => item is not null) + .ToArray(); } // Add video info diff --git a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs index 90aa3a22e..43a3cdf78 100644 --- a/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs +++ b/Jellyfin.Server.Implementations/Item/BaseItemRepository.cs @@ -2682,6 +2682,21 @@ public sealed class BaseItemRepository .Where(e => artistNames.Contains(e.Name)) .ToArray(); - return artists.GroupBy(e => e.Name).ToDictionary(e => e.Key!, e => e.Select(f => DeserializeBaseItem(f)).Where(dto => dto is not null).Cast().ToArray()); + var lookup = artists + .GroupBy(e => e.Name!) + .ToDictionary( + g => g.Key, + g => g.Select(f => DeserializeBaseItem(f)).Where(dto => dto is not null).Cast().ToArray()); + + var result = new Dictionary(artistNames.Count); + foreach (var name in artistNames) + { + if (lookup.TryGetValue(name, out var artistArray)) + { + result[name] = artistArray; + } + } + + return result; } } -- cgit v1.2.3 From 0c274af72c1d223c0fea7f4f4ac99e04f1e1e904 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:11:28 -0500 Subject: Backport pull request #16077 from jellyfin/release-10.11.z Revert hidden directory ignore pattern Original-merge: 644327eb762a907328c68ab9f5d61a151cd96897 Merged-by: crobibero Backported-by: Bond_009 --- Emby.Server.Implementations/Library/IgnorePatterns.cs | 5 ++++- .../Library/IgnorePatternsTests.cs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs index 5fac2f6b0..59ccb9e2c 100644 --- a/Emby.Server.Implementations/Library/IgnorePatterns.cs +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -50,6 +50,10 @@ namespace Emby.Server.Implementations.Library "**/lost+found", "**/subs/**", "**/subs", + "**/.snapshots/**", + "**/.snapshots", + "**/.snapshot/**", + "**/.snapshot", // Trickplay files "**/*.trickplay", @@ -83,7 +87,6 @@ namespace Emby.Server.Implementations.Library // Unix hidden files "**/.*", - "**/.*/**", // Mac - if you ever remove the above. // "**/._*", diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs index 4cb6cb960..07061cfc7 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -19,7 +19,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/movies/#recycle", true)] [InlineData("thumbs.db", true)] [InlineData(@"C:\media\movies\movie.avi", false)] - [InlineData("/media/.hiddendir/file.mp4", true)] + [InlineData("/media/.hiddendir/file.mp4", false)] [InlineData("/media/dir/.hiddenfile.mp4", true)] [InlineData("/media/dir/._macjunk.mp4", true)] [InlineData("/volume1/video/Series/@eaDir", true)] @@ -32,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("/media/music/Foo B.A.R", false)] [InlineData("/media/music/Foo B.A.R.", false)] [InlineData("/movies/.zfs/snapshot/AutoM-2023-09", true)] - public void PathIgnored(string path, bool expected) + public void PathIgnored(string path, bool expected) { Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); } -- cgit v1.2.3 From 841e4dabb513c9c94bcbb0005d19e2a8be6434a5 Mon Sep 17 00:00:00 2001 From: nielsvanvelzen Date: Wed, 28 Jan 2026 12:11:30 -0500 Subject: Backport pull request #16109 from jellyfin/release-10.11.z Fix SessionInfoWebSocketListener not using SessionInfoDto Original-merge: e65aff8bc67e3cc97d2ebe141de9ff6a8681d792 Merged-by: nielsvanvelzen Backported-by: Bond_009 --- Emby.Server.Implementations/Session/SessionManager.cs | 3 ++- .../WebSocketListeners/SessionInfoWebSocketListener.cs | 15 +++++++++------ MediaBrowser.Controller/Session/ISessionManager.cs | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index bbe23f8df..8e14f5bdf 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1186,7 +1186,8 @@ namespace Emby.Server.Implementations.Session return session; } - private SessionInfoDto ToSessionInfoDto(SessionInfo sessionInfo) + /// + public SessionInfoDto ToSessionInfoDto(SessionInfo sessionInfo) { return new SessionInfoDto { diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs index 143d82bac..db24c9746 100644 --- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs @@ -7,6 +7,7 @@ using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; @@ -15,7 +16,7 @@ namespace Jellyfin.Api.WebSocketListeners; /// /// Class SessionInfoWebSocketListener. /// -public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> +public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> { private readonly ISessionManager _sessionManager; private bool _disposed; @@ -52,24 +53,26 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener /// Task{SystemInfo}. - protected override Task> GetDataToSend() + protected override Task> GetDataToSend() { - return Task.FromResult(_sessionManager.Sessions); + return Task.FromResult(_sessionManager.Sessions.Select(_sessionManager.ToSessionInfoDto)); } /// - protected override Task> GetDataToSendForConnection(IWebSocketConnection connection) + protected override Task> GetDataToSendForConnection(IWebSocketConnection connection) { + var sessions = _sessionManager.Sessions; + // For non-admin users, filter the sessions to only include their own sessions if (connection.AuthorizationInfo?.User is not null && !connection.AuthorizationInfo.IsApiKey && !connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) { var userId = connection.AuthorizationInfo.User.Id; - return Task.FromResult(_sessionManager.Sessions.Where(s => s.UserId.Equals(userId) || s.ContainsUser(userId))); + sessions = sessions.Where(s => s.UserId.Equals(userId) || s.ContainsUser(userId)); } - return Task.FromResult(_sessionManager.Sessions); + return Task.FromResult(sessions.Select(_sessionManager.ToSessionInfoDto)); } /// diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 2b3afa117..c11c65c33 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -350,5 +350,12 @@ namespace MediaBrowser.Controller.Session /// The session id or playsession id. /// Task. Task CloseLiveStreamIfNeededAsync(string liveStreamId, string sessionIdOrPlaySessionId); + + /// + /// Gets the dto for session info. + /// + /// The session info. + /// of the session. + SessionInfoDto ToSessionInfoDto(SessionInfo sessionInfo); } } -- cgit v1.2.3 From d1d4fe2e3375487cb87d87e44ec5d4653f3b8513 Mon Sep 17 00:00:00 2001 From: Augusto Date: Sun, 1 Feb 2026 09:23:54 -0500 Subject: Translated using Weblate (Spanish (Argentina)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_AR/ --- Emby.Server.Implementations/Localization/Core/es-AR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index ce0044f64..2bbf0d514 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -20,7 +20,7 @@ "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteEpisodes": "Capítulos favoritos", - "HeaderFavoriteShows": "Programas favoritos", + "HeaderFavoriteShows": "Series favoritas", "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en vivo", "HeaderNextUp": "Siguiente", -- cgit v1.2.3 From 4f695bc58afaf12383dddc2ee4dc8582ab100912 Mon Sep 17 00:00:00 2001 From: KecskeTech Date: Sun, 1 Feb 2026 06:26:49 -0500 Subject: Translated using Weblate (Hungarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/ --- Emby.Server.Implementations/Localization/Core/hu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 813d79923..7d72c1f30 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -55,7 +55,7 @@ "NotificationOptionPluginInstalled": "Bővítmény telepítve", "NotificationOptionPluginUninstalled": "Bővítmény eltávolítva", "NotificationOptionPluginUpdateInstalled": "Bővítményfrissítés telepítve", - "NotificationOptionServerRestartRequired": "A kiszolgáló újraindítása szükséges", + "NotificationOptionServerRestartRequired": "A szerver újraindítása szükséges", "NotificationOptionTaskFailed": "Hiba az ütemezett feladatban", "NotificationOptionUserLockedOut": "Felhasználó tiltva", "NotificationOptionVideoPlayback": "Videólejátszás elkezdve", -- cgit v1.2.3 From 7c200899d71cc255feb58a110f0fce244bffa0d8 Mon Sep 17 00:00:00 2001 From: Blackspirits Date: Mon, 2 Feb 2026 05:23:44 -0500 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (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 a27036493..c2ce2ba40 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -124,8 +124,8 @@ "TaskKeyframeExtractor": "Extrator de Quadros-chave", "External": "Externo", "HearingImpaired": "Surdo", - "TaskRefreshTrickplayImages": "Gerar Imagens de Trickplay", - "TaskRefreshTrickplayImagesDescription": "Cria ficheiros de trickplay para vídeos nas bibliotecas ativas.", + "TaskRefreshTrickplayImages": "Gerar imagens de trickplay", + "TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.", "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.", "TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução", "TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.", -- cgit v1.2.3 From 32d80861211031e5ee66d5068679f43335e2bcbd Mon Sep 17 00:00:00 2001 From: Blackspirits Date: Mon, 2 Feb 2026 05:23:53 -0500 Subject: Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 74bb1c63a..9ae346e25 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -124,8 +124,8 @@ "HearingImpaired": "Problemas auditivos", "TaskKeyframeExtractor": "Extrator de quadro-chave", "TaskKeyframeExtractorDescription": "Retira frames chave do video para criar listas HLS precisas. Esta tarefa pode correr durante algum tempo.", - "TaskRefreshTrickplayImages": "Gerar miniaturas de vídeo", - "TaskRefreshTrickplayImagesDescription": "Cria miniaturas de vídeo para vídeos nas bibliotecas definidas.", + "TaskRefreshTrickplayImages": "Gerar imagens de trickplay", + "TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.", "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.", "TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução", "TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.", -- cgit v1.2.3 From ccd042750df8389c1622aaa049997c771f32f181 Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Thu, 5 Feb 2026 16:39:52 -0500 Subject: Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index eb75cfd49..ebe292c22 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -8,7 +8,7 @@ "CameraImageUploadedFrom": "Nova fotografija sa kamere je učitana iz {0}", "Channels": "Kanali", "ChapterNameValue": "Poglavlje {0}", - "Collections": "Kolekcije", + "Collections": "Zbirke", "DeviceOfflineWithName": "{0} je prekinuo vezu", "DeviceOnlineWithName": "{0} je povezan", "FailedLoginAttemptWithUserName": "Neuspješan pokušaj prijave od {0}", -- cgit v1.2.3 From fead4acae121c1a2edacebbba860bdf4f6b56d25 Mon Sep 17 00:00:00 2001 From: Milo Ivir Date: Fri, 6 Feb 2026 17:04:38 -0500 Subject: Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index ebe292c22..94db43571 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -70,7 +70,7 @@ "ScheduledTaskFailedWithName": "{0} neuspjelo", "ScheduledTaskStartedWithName": "{0} pokrenuto", "ServerNameNeedsToBeRestarted": "{0} treba ponovno pokrenuti", - "Shows": "Serije", + "Shows": "Emisije", "Songs": "Pjesme", "StartupEmbyServerIsLoading": "Jellyfin server se učitava. Pokušajte ponovo uskoro.", "SubtitleDownloadFailureFromForItem": "Prijevod nije uspješno preuzet od {0} za {1}", -- cgit v1.2.3 From 558b31f3861ce6de3bbf7991fd125dd43c26bc69 Mon Sep 17 00:00:00 2001 From: Vincenzo Reale Date: Mon, 9 Feb 2026 05:07:59 -0500 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index c2974704b..d8eb8bc9e 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Dispositivo: {1}", "Application": "Applicazione", "Artists": "Artisti", - "AuthenticationSucceededWithUserName": "{0} autenticato con successo", + "AuthenticationSucceededWithUserName": "{0} autenticato correttamente", "Books": "Libri", "CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}", "Channels": "Canali", @@ -11,7 +11,7 @@ "Collections": "Collezioni", "DeviceOfflineWithName": "{0} si è disconnesso", "DeviceOnlineWithName": "{0} è connesso", - "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}", + "FailedLoginAttemptWithUserName": "Tentativo di accesso non riuscito da {0}", "Favorites": "Preferiti", "Folders": "Cartelle", "Genres": "Generi", -- cgit v1.2.3 From 01264c10a684768f9c8473cf769938522d3cc5d0 Mon Sep 17 00:00:00 2001 From: Vincenzo Reale Date: Mon, 9 Feb 2026 05:11:00 -0500 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index d8eb8bc9e..41a829757 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -17,7 +17,7 @@ "Genres": "Generi", "HeaderAlbumArtists": "Artisti dell'album", "HeaderContinueWatching": "Continua a guardare", - "HeaderFavoriteAlbums": "Album Preferiti", + "HeaderFavoriteAlbums": "Album preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteEpisodes": "Episodi Preferiti", "HeaderFavoriteShows": "Serie TV Preferite", -- cgit v1.2.3 From a0bf5199ba54161a616a697b50d3a0103e803ef6 Mon Sep 17 00:00:00 2001 From: Vincenzo Reale Date: Mon, 9 Feb 2026 05:42:16 -0500 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- .../Localization/Core/it.json | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 41a829757..ff60e6127 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -18,29 +18,29 @@ "HeaderAlbumArtists": "Artisti dell'album", "HeaderContinueWatching": "Continua a guardare", "HeaderFavoriteAlbums": "Album preferiti", - "HeaderFavoriteArtists": "Artisti Preferiti", - "HeaderFavoriteEpisodes": "Episodi Preferiti", - "HeaderFavoriteShows": "Serie TV Preferite", - "HeaderFavoriteSongs": "Brani Preferiti", + "HeaderFavoriteArtists": "Artisti preferiti", + "HeaderFavoriteEpisodes": "Episodi preferiti", + "HeaderFavoriteShows": "Serie TV preferite", + "HeaderFavoriteSongs": "Brani preferiti", "HeaderLiveTV": "Diretta TV", "HeaderNextUp": "Prossimo", - "HeaderRecordingGroups": "Gruppi di Registrazione", - "HomeVideos": "Video Personali", + "HeaderRecordingGroups": "Gruppi di registrazione", + "HomeVideos": "Video personali", "Inherit": "Eredita", "ItemAddedWithName": "{0} è stato aggiunto alla libreria", "ItemRemovedWithName": "{0} è stato rimosso dalla libreria", "LabelIpAddressValue": "Indirizzo IP: {0}", "LabelRunningTimeValue": "Durata: {0}", "Latest": "Novità", - "MessageApplicationUpdated": "Il Server Jellyfin è stato aggiornato", + "MessageApplicationUpdated": "Jellyfin Server è stato aggiornato", "MessageApplicationUpdatedTo": "Jellyfin Server è stato aggiornato a {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La sezione {0} della configurazione server è stata aggiornata", "MessageServerConfigurationUpdated": "La configurazione del server è stata aggiornata", "MixedContent": "Contenuto misto", "Movies": "Film", "Music": "Musica", - "MusicVideos": "Video Musicali", - "NameInstallFailed": "{0} installazione fallita", + "MusicVideos": "Video musicali", + "NameInstallFailed": "{0} installazione non riuscita", "NameSeasonNumber": "Stagione {0}", "NameSeasonUnknown": "Stagione sconosciuta", "NewVersionIsAvailable": "Una nuova versione di Jellyfin Server è disponibile per il download.", @@ -49,37 +49,37 @@ "NotificationOptionAudioPlayback": "La riproduzione audio è iniziata", "NotificationOptionAudioPlaybackStopped": "La riproduzione audio è stata interrotta", "NotificationOptionCameraImageUploaded": "Immagine fotocamera caricata", - "NotificationOptionInstallationFailed": "Installazione fallita", + "NotificationOptionInstallationFailed": "Installazione non riuscita", "NotificationOptionNewLibraryContent": "Nuovo contenuto aggiunto", "NotificationOptionPluginError": "Errore del plugin", "NotificationOptionPluginInstalled": "Plugin installato", "NotificationOptionPluginUninstalled": "Plugin disinstallato", "NotificationOptionPluginUpdateInstalled": "Aggiornamento plugin installato", "NotificationOptionServerRestartRequired": "Riavvio del server necessario", - "NotificationOptionTaskFailed": "Operazione pianificata fallita", + "NotificationOptionTaskFailed": "Operazione pianificata non riuscita", "NotificationOptionUserLockedOut": "Utente bloccato", "NotificationOptionVideoPlayback": "Riproduzione video iniziata", "NotificationOptionVideoPlaybackStopped": "Riproduzione video interrotta", "Photos": "Foto", "Playlists": "Playlist", "Plugin": "Plugin", - "PluginInstalledWithName": "{0} è stato Installato", + "PluginInstalledWithName": "{0} è stato installato", "PluginUninstalledWithName": "{0} è stato disinstallato", "PluginUpdatedWithName": "{0} è stato aggiornato", "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} fallito", + "ScheduledTaskFailedWithName": "{0} non riuscito", "ScheduledTaskStartedWithName": "{0} avviato", "ServerNameNeedsToBeRestarted": "{0} deve essere riavviato", "Shows": "Serie TV", "Songs": "Brani", - "StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.", + "StartupEmbyServerIsLoading": "Jellyfin Server si sta avviando. Riprova più tardi.", "SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}", "Sync": "Sincronizza", "System": "Sistema", "TvShows": "Serie TV", "User": "Utente", "UserCreatedWithName": "L'utente {0} è stato creato", - "UserDeletedWithName": "L'utente {0} è stato rimosso", + "UserDeletedWithName": "L'utente {0} è stato eliminato", "UserDownloadingItemWithValues": "{0} sta scaricando {1}", "UserLockedOutWithName": "L'utente {0} è stato bloccato", "UserOfflineFromDevice": "{0} si è disconnesso da {1}", @@ -115,7 +115,7 @@ "TasksMaintenanceCategory": "Manutenzione", "TaskCleanActivityLog": "Attività di Registro Completate", "TaskCleanActivityLogDescription": "Elimina le voci del registro delle attività più vecchie dell’età configurata.", - "Undefined": "Non Definito", + "Undefined": "Non specificato", "Forced": "Forzato", "Default": "Predefinito", "TaskOptimizeDatabaseDescription": "Compatta database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altre modifiche inerenti il database potrebbe aumentarne le prestazioni.", @@ -123,7 +123,7 @@ "TaskKeyframeExtractor": "Estrattore di Keyframe", "TaskKeyframeExtractorDescription": "Estrae i keyframe dai video per creare migliori playlist HLS. Questa procedura potrebbe richiedere molto tempo.", "External": "Esterno", - "HearingImpaired": "Non Udenti", + "HearingImpaired": "Non udenti", "TaskRefreshTrickplayImages": "Genera immagini Trickplay", "TaskRefreshTrickplayImagesDescription": "Crea anteprime trickplay per i video nelle librerie abilitate.", "TaskCleanCollectionsAndPlaylists": "Ripulire le collezioni e le playlist", -- cgit v1.2.3 From 6b8400cc3da0095fbce4f3f481464244b3ccf608 Mon Sep 17 00:00:00 2001 From: Vincenzo Reale Date: Mon, 9 Feb 2026 06:31:26 -0500 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index ff60e6127..f0c4b5027 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -61,7 +61,7 @@ "NotificationOptionVideoPlayback": "Riproduzione video iniziata", "NotificationOptionVideoPlaybackStopped": "Riproduzione video interrotta", "Photos": "Foto", - "Playlists": "Playlist", + "Playlists": "Scalette", "Plugin": "Plugin", "PluginInstalledWithName": "{0} è stato installato", "PluginUninstalledWithName": "{0} è stato disinstallato", @@ -114,20 +114,20 @@ "TasksLibraryCategory": "Libreria", "TasksMaintenanceCategory": "Manutenzione", "TaskCleanActivityLog": "Attività di Registro Completate", - "TaskCleanActivityLogDescription": "Elimina le voci del registro delle attività più vecchie dell’età configurata.", + "TaskCleanActivityLogDescription": "Elimina le voci del registro delle attività più vecchie dell'età configurata.", "Undefined": "Non specificato", "Forced": "Forzato", "Default": "Predefinito", "TaskOptimizeDatabaseDescription": "Compatta database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altre modifiche inerenti il database potrebbe aumentarne le prestazioni.", "TaskOptimizeDatabase": "Ottimizza database", "TaskKeyframeExtractor": "Estrattore di Keyframe", - "TaskKeyframeExtractorDescription": "Estrae i keyframe dai video per creare migliori playlist HLS. Questa procedura potrebbe richiedere molto tempo.", + "TaskKeyframeExtractorDescription": "Estrae i keyframe dai video per creare migliori scalette HLS. Questa procedura potrebbe richiedere molto tempo.", "External": "Esterno", "HearingImpaired": "Non udenti", "TaskRefreshTrickplayImages": "Genera immagini Trickplay", "TaskRefreshTrickplayImagesDescription": "Crea anteprime trickplay per i video nelle librerie abilitate.", - "TaskCleanCollectionsAndPlaylists": "Ripulire le collezioni e le playlist", - "TaskCleanCollectionsAndPlaylistsDescription": "Rimuove gli elementi dalle collezioni e dalle playlist che non esistono più.", + "TaskCleanCollectionsAndPlaylists": "Ripulisci le collezioni e le scalette", + "TaskCleanCollectionsAndPlaylistsDescription": "Rimuove gli elementi dalle collezioni e dalle scalette che non esistono più.", "TaskAudioNormalization": "Normalizzazione dell'audio", "TaskAudioNormalizationDescription": "Scansiona i file alla ricerca dei dati per la normalizzazione dell'audio.", "TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni", -- cgit v1.2.3 From de36952f5378d5af28aa9edc660f47cd47a2f218 Mon Sep 17 00:00:00 2001 From: francescbassas Date: Mon, 9 Feb 2026 16:28:49 -0500 Subject: Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 82cc1857b..1e7279be8 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -104,7 +104,7 @@ "TaskCleanLogsDescription": "Esborra els registres que tinguin més de {0} dies.", "TaskCleanLogs": "Neteja dels registres", "TaskRefreshLibraryDescription": "Escaneja les mediateques, a la cerca de fitxers nous i refresca les metadades.", - "TaskRefreshLibrary": "Escaneig de les mediateques", + "TaskRefreshLibrary": "Escaneja la mediateca", "TaskRefreshChapterImagesDescription": "Creació de les miniatures dels vídeos que tinguin capítols.", "TaskRefreshChapterImages": "Extracció de les imatges dels capítols", "TaskCleanCacheDescription": "Eliminació de la memòria cau no necessària per al servidor.", -- cgit v1.2.3 From 5eaaad660dfb0f6d12cf6dde8d50f25b08b24175 Mon Sep 17 00:00:00 2001 From: Pavel Miniutka Date: Tue, 10 Feb 2026 04:34:35 -0500 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 3d598c491..cb11cc089 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -3,7 +3,7 @@ "Playlists": "Плэй-лісты", "Latest": "Апошняе", "LabelIpAddressValue": "IP-адрас: {0}", - "ItemAddedWithName": "{0} даданы ў бібліятэку", + "ItemAddedWithName": "{0} дададзены ў бібліятэку", "MessageApplicationUpdated": "Сервер Jellyfin абноўлены", "NotificationOptionApplicationUpdateInstalled": "Абнаўленне праграмы ўсталявана", "PluginInstalledWithName": "{0} быў усталяваны", @@ -14,7 +14,7 @@ "Channels": "Каналы", "ChapterNameValue": "Раздзел {0}", "Collections": "Калекцыі", - "Default": "Па змаўчанні", + "Default": "Прадвызначана", "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}", "Folders": "Папкі", "Favorites": "Абранае", @@ -81,8 +81,8 @@ "NotificationOptionInstallationFailed": "Збой усталёўкі", "NewVersionIsAvailable": "Новая версія сервера Jellyfin даступная для cпампоўкі.", "NotificationOptionCameraImageUploaded": "Выява камеры запампавана", - "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыё спынена", - "NotificationOptionAudioPlayback": "Прайграванне аўдыё пачалося", + "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыя спынена", + "NotificationOptionAudioPlayback": "Прайграванне аўдыя пачалося", "NotificationOptionNewLibraryContent": "Дададзены новы кантэнт", "NotificationOptionPluginError": "Збой плагіна", "NotificationOptionPluginUninstalled": "Плагін выдалены", -- cgit v1.2.3 From 074aa7e6394ede1998c53bd9c1528c0c5dfddcd3 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Sat, 14 Feb 2026 05:57:26 -0500 Subject: Backport pull request #16231 from jellyfin/release-10.11.z Skip image checks for empty folders Original-merge: 8cd3090ceedbefd680b26dc28266ae9a6e5d652a Merged-by: Bond-009 Backported-by: Bond_009 --- .../Images/BaseDynamicImageProvider.cs | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 4874eca8e..996cd1b3c 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -267,22 +267,24 @@ namespace Emby.Server.Implementations.Images { var image = item.GetImageInfo(type, 0); - if (image is not null) + if (image is null) { - if (!image.IsLocalFile) - { - return false; - } + return GetItemsWithImages(item).Count is not 0; + } - if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path)) - { - return false; - } + if (!image.IsLocalFile) + { + return false; + } - if (!HasChangedByDate(item, image)) - { - return false; - } + if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path)) + { + return false; + } + + if (!HasChangedByDate(item, image)) + { + return false; } return true; -- cgit v1.2.3