From 4046ef1c13129cba22ab9b2f0806406d3df346e6 Mon Sep 17 00:00:00 2001 From: gnattu Date: Fri, 7 Jun 2024 17:58:35 +0800 Subject: [PATCH 01/30] Overwrite supported codecs for livetv Only changeing streamingRequest is not enough. The internal logic will do codec shifting based on supported codecs, need to overwrite all of them. Signed-off-by: gnattu --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index af4a9e6896..535ef27c3a 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -154,6 +154,11 @@ public static class StreamingHelpers // Some channels from HDHomerun will experience A/V sync issues streamingRequest.SegmentContainer = "ts"; streamingRequest.VideoCodec = "h264"; + streamingRequest.AudioCodec = "aac"; + state.SupportedVideoCodecs = ["h264"]; + state.Request.VideoCodec = "h264"; + state.SupportedAudioCodecs = ["aac"]; + state.Request.AudioCodec = "aac"; } var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false); From b6595e4efcb8d0d8c2298c7e5dd111ec0dc28986 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 7 Jun 2024 22:12:35 +0200 Subject: [PATCH 02/30] Check hearing impared flags with equality instead of contains Fixes #12019 --- Emby.Naming/ExternalFiles/ExternalPathParser.cs | 2 +- .../ExternalFiles/ExternalPathParserTests.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 9d54533c24..7a01b02f3c 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -107,7 +107,7 @@ namespace Emby.Naming.ExternalFiles pathInfo.Language = culture.ThreeLetterISOLanguageName; extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); } - else if (_namingOptions.MediaHearingImpairedFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringComparison.OrdinalIgnoreCase))) + else if (_namingOptions.MediaHearingImpairedFlags.Any(s => currentSliceWithoutSeparator.Equals(s, StringComparison.OrdinalIgnoreCase))) { pathInfo.IsHearingImpaired = true; extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); diff --git a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs index ba602b5d2e..0b8b1f644b 100644 --- a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs @@ -104,6 +104,7 @@ public class ExternalPathParserTests [InlineData(".en.cc.title", "title", "eng", false, false, true)] [InlineData(".hi.en.title", "title", "eng", false, false, true)] [InlineData(".en.hi.title", "title", "eng", false, false, true)] + [InlineData(".Subs for Chinese Audio.eng", "Subs for Chinese Audio", "eng", false, false, false)] public void ParseFile_ExtraTokens_ParseToValues(string tokens, string? title, string? language, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false) { var path = "My.Video" + tokens + ".srt"; From 28274d4c757f1bf10b8280b34577a1ef1aaa8823 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Jun 2024 22:12:48 +0200 Subject: [PATCH 03/30] Remove empty image folders recursively --- MediaBrowser.Providers/Manager/ImageSaver.cs | 4 ++-- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index e0677aa9fe..b430561155 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -190,9 +190,9 @@ namespace MediaBrowser.Providers.Manager // Remove containing directory if empty var folder = Path.GetDirectoryName(currentPath); - if (!_fileSystem.GetFiles(folder).Any()) + if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder, true).Any()) { - Directory.Delete(folder); + Directory.Delete(folder, true); } } catch (FileNotFoundException) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index bee420d952..763e5be968 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -381,9 +381,9 @@ namespace MediaBrowser.Providers.Manager { // Always remove empty parent folder var folder = Path.GetDirectoryName(image.Path); - if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder).Any()) + if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder, true).Any()) { - Directory.Delete(folder); + Directory.Delete(folder, true); } } } From e4f3f0b3b68d7b14bad6cc45fa6191285e22c3ac Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Jun 2024 23:19:04 +0200 Subject: [PATCH 04/30] Remove all data when replacing all --- Jellyfin.Api/Controllers/ItemRefreshController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index c1343b1309..d7a8c37c4b 100644 --- a/Jellyfin.Api/Controllers/ItemRefreshController.cs +++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs @@ -80,7 +80,8 @@ public class ItemRefreshController : BaseJellyfinApiController || imageRefreshMode == MetadataRefreshMode.FullRefresh || replaceAllImages || replaceAllMetadata, - IsAutomated = false + IsAutomated = false, + RemoveOldMetadata = replaceAllMetadata }; _providerManager.QueueRefresh(item.Id, refreshOptions, RefreshPriority.High); From ec82023265929dcd95f3b3976c507d8551ddba41 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Jun 2024 23:19:30 +0200 Subject: [PATCH 05/30] Respect different metadata settings on refresh --- MediaBrowser.Providers/Manager/MetadataService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 61a4d75863..e2466fe627 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -669,6 +669,8 @@ namespace MediaBrowser.Providers.Manager }; temp.Item.Path = item.Path; temp.Item.Id = item.Id; + temp.Item.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode; + temp.Item.PreferredMetadataLanguage = item.PreferredMetadataLanguage; var foundImageTypes = new List(); From feb20c131ad787286acb96ed606f79ac0b86217c Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Jun 2024 22:12:48 +0200 Subject: [PATCH 06/30] Use helper --- .../IO/ManagedFileSystem.cs | 6 +- .../Tasks/DeleteCacheFileTask.cs | 44 +------------ .../Tasks/DeleteTranscodeFileTask.cs | 45 +------------ .../IO/FileSystemHelper.cs | 64 +++++++++++++++++++ MediaBrowser.Providers/Manager/ImageSaver.cs | 6 +- .../Manager/ItemImageProvider.cs | 6 +- 6 files changed, 77 insertions(+), 94 deletions(-) create mode 100644 MediaBrowser.Controller/IO/FileSystemHelper.cs diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index d5afac2663..250bec9ea9 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -389,7 +389,7 @@ namespace Emby.Server.Implementations.IO var info = new FileInfo(path); if (info.Exists && - ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) != isHidden) + (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden != isHidden) { if (isHidden) { @@ -417,8 +417,8 @@ namespace Emby.Server.Implementations.IO return; } - if (((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) == readOnly - && ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) == isHidden) + if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly == readOnly + && (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden == isHidden) { return; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index 03935b384c..fc3ad90f6c 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; @@ -133,53 +134,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks cancellationToken.ThrowIfCancellationRequested(); - DeleteFile(file.FullName); + FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); index++; } - DeleteEmptyFolders(directory); + FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); progress.Report(100); } - - private void DeleteEmptyFolders(string parent) - { - foreach (var directory in _fileSystem.GetDirectoryPaths(parent)) - { - DeleteEmptyFolders(directory); - if (!_fileSystem.GetFileSystemEntryPaths(directory).Any()) - { - try - { - Directory.Delete(directory, false); - } - catch (UnauthorizedAccessException ex) - { - _logger.LogError(ex, "Error deleting directory {Path}", directory); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting directory {Path}", directory); - } - } - } - } - - private void DeleteFile(string path) - { - try - { - _fileSystem.DeleteFile(path); - } - catch (UnauthorizedAccessException ex) - { - _logger.LogError(ex, "Error deleting file {Path}", path); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting file {Path}", path); - } - } } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index e4e565c642..6cb06d31c0 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.IO; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; @@ -113,53 +113,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks cancellationToken.ThrowIfCancellationRequested(); - DeleteFile(file.FullName); + FileSystemHelper.DeleteFile(_fileSystem, file.FullName, _logger); index++; } - DeleteEmptyFolders(directory); + FileSystemHelper.DeleteEmptyFolders(_fileSystem, directory, _logger); progress.Report(100); } - - private void DeleteEmptyFolders(string parent) - { - foreach (var directory in _fileSystem.GetDirectoryPaths(parent)) - { - DeleteEmptyFolders(directory); - if (!_fileSystem.GetFileSystemEntryPaths(directory).Any()) - { - try - { - Directory.Delete(directory, false); - } - catch (UnauthorizedAccessException ex) - { - _logger.LogError(ex, "Error deleting directory {Path}", directory); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting directory {Path}", directory); - } - } - } - } - - private void DeleteFile(string path) - { - try - { - _fileSystem.DeleteFile(path); - } - catch (UnauthorizedAccessException ex) - { - _logger.LogError(ex, "Error deleting file {Path}", path); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting file {Path}", path); - } - } } } diff --git a/MediaBrowser.Controller/IO/FileSystemHelper.cs b/MediaBrowser.Controller/IO/FileSystemHelper.cs new file mode 100644 index 0000000000..1a33c3aa8c --- /dev/null +++ b/MediaBrowser.Controller/IO/FileSystemHelper.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Linq; +using MediaBrowser.Model.IO; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Controller.IO; + +/// +/// Helper methods for file system management. +/// +public static class FileSystemHelper +{ + /// + /// Deletes the file. + /// + /// The fileSystem. + /// The path. + /// The logger. + public static void DeleteFile(IFileSystem fileSystem, string path, ILogger logger) + { + try + { + fileSystem.DeleteFile(path); + } + catch (UnauthorizedAccessException ex) + { + logger.LogError(ex, "Error deleting file {Path}", path); + } + catch (IOException ex) + { + logger.LogError(ex, "Error deleting file {Path}", path); + } + } + + /// + /// Recursively delete empty folders. + /// + /// The fileSystem. + /// The path. + /// The logger. + public static void DeleteEmptyFolders(IFileSystem fileSystem, string path, ILogger logger) + { + foreach (var directory in fileSystem.GetDirectoryPaths(path)) + { + DeleteEmptyFolders(fileSystem, directory, logger); + if (!fileSystem.GetFileSystemEntryPaths(directory).Any()) + { + try + { + Directory.Delete(directory, false); + } + catch (UnauthorizedAccessException ex) + { + logger.LogError(ex, "Error deleting directory {Path}", directory); + } + catch (IOException ex) + { + logger.LogError(ex, "Error deleting directory {Path}", directory); + } + } + } + } +} diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index b430561155..7911f35455 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -14,6 +14,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; @@ -190,10 +191,7 @@ namespace MediaBrowser.Providers.Manager // Remove containing directory if empty var folder = Path.GetDirectoryName(currentPath); - if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder, true).Any()) - { - Directory.Delete(folder, true); - } + FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); } catch (FileNotFoundException) { diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 763e5be968..c72d4256af 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; @@ -381,10 +382,7 @@ namespace MediaBrowser.Providers.Manager { // Always remove empty parent folder var folder = Path.GetDirectoryName(image.Path); - if (Directory.Exists(folder) && !_fileSystem.GetFiles(folder, true).Any()) - { - Directory.Delete(folder, true); - } + FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); } } } From 19a89d5a609c9f7cb14a271714c798dc361c4cb1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 8 Jun 2024 00:12:36 +0200 Subject: [PATCH 07/30] Remove folder after removing empty subfolders --- MediaBrowser.Providers/Manager/ImageSaver.cs | 4 ++++ MediaBrowser.Providers/Manager/ItemImageProvider.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 7911f35455..b9b07da67d 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -192,6 +192,10 @@ namespace MediaBrowser.Providers.Manager // Remove containing directory if empty var folder = Path.GetDirectoryName(currentPath); FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); + if (!_fileSystem.GetFiles(folder).Any()) + { + Directory.Delete(folder); + } } catch (FileNotFoundException) { diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index c72d4256af..ad80611261 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -383,6 +383,10 @@ namespace MediaBrowser.Providers.Manager // Always remove empty parent folder var folder = Path.GetDirectoryName(image.Path); FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); + if (!_fileSystem.GetFiles(folder).Any()) + { + Directory.Delete(folder); + } } } } From 99a04e23d913b06bf4de5771c9d2014cd2f56fd7 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 8 Jun 2024 16:55:27 +0800 Subject: [PATCH 08/30] Fix video embedded image detection Fixes debbfaa. Embedded images also exist in video. Signed-off-by: nyanmisaka --- .../Probing/ProbeResultNormalizer.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a587fa9db9..f634d37c7e 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -779,11 +779,10 @@ namespace MediaBrowser.MediaEncoding.Probing && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase); if (isAudio - && (string.Equals(stream.Codec, "bmp", StringComparison.OrdinalIgnoreCase) - || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) - || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) - || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase) - || string.Equals(stream.Codec, "webp", StringComparison.OrdinalIgnoreCase))) + || string.Equals(stream.Codec, "bmp", StringComparison.OrdinalIgnoreCase) + || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) + || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase) + || string.Equals(stream.Codec, "webp", StringComparison.OrdinalIgnoreCase)) { stream.Type = MediaStreamType.EmbeddedImage; } From b116a2742e0ec003c0a82dda77bc9aac05c89ce5 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 8 Jun 2024 16:38:43 +0200 Subject: [PATCH 09/30] Do not fail user deletion if we have no playlist folder --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 8a35b96b39..0c7840ee59 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -170,8 +170,13 @@ namespace Emby.Server.Implementations.Playlists private List GetUserPlaylists(Guid userId) { var user = _userManager.GetUserById(userId); + var playlistsFolder = GetPlaylistsFolder(userId); + if (playlistsFolder is null) + { + return []; + } - return GetPlaylistsFolder(userId).GetChildren(user, true).OfType().ToList(); + return playlistsFolder.GetChildren(user, true).OfType().ToList(); } private static string GetTargetPath(string path) From 302eea1cb7160bed64c76a086e425be1f90c1b11 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 8 Jun 2024 21:51:08 +0200 Subject: [PATCH 10/30] Fix local episode image thumb recognition --- .../Images/EpisodeLocalImageProvider.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index 6a5e3bf04a..ea934dc3bc 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -40,8 +40,7 @@ namespace MediaBrowser.LocalMetadata.Images var parentPathFiles = directoryService.GetFiles(parentPath); var nameWithoutExtension = Path.GetFileNameWithoutExtension(item.Path.AsSpan()).ToString(); - var thumbName = string.Concat(nameWithoutExtension, "-thumb"); - var images = GetImageFilesFromFolder(thumbName, parentPathFiles); + var images = GetImageFilesFromFolder(nameWithoutExtension, parentPathFiles); var metadataSubPath = directoryService.GetDirectories(parentPath).Where(d => d.Name.EndsWith("metadata", StringComparison.OrdinalIgnoreCase)).ToList(); foreach (var path in metadataSubPath) @@ -55,8 +54,6 @@ namespace MediaBrowser.LocalMetadata.Images private List GetImageFilesFromFolder(ReadOnlySpan filenameWithoutExtension, List filePaths) { - var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); - var list = new List(1); foreach (var i in filePaths) @@ -69,6 +66,7 @@ namespace MediaBrowser.LocalMetadata.Images if (BaseItem.SupportedImageExtensions.Contains(i.Extension.AsSpan(), StringComparison.OrdinalIgnoreCase)) { var currentNameWithoutExtension = Path.GetFileNameWithoutExtension(i.FullName.AsSpan()); + var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); if (filenameWithoutExtension.Equals(currentNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) { From fd009fc71b0e183abbf369951d974017c0b1d7b7 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 8 Jun 2024 21:57:21 +0200 Subject: [PATCH 11/30] Simplify metadata subdir check --- .../Images/EpisodeLocalImageProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index ea934dc3bc..b14ace57d2 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -42,10 +42,10 @@ namespace MediaBrowser.LocalMetadata.Images var images = GetImageFilesFromFolder(nameWithoutExtension, parentPathFiles); - var metadataSubPath = directoryService.GetDirectories(parentPath).Where(d => d.Name.EndsWith("metadata", StringComparison.OrdinalIgnoreCase)).ToList(); - foreach (var path in metadataSubPath) + var metadataSubDir = directoryService.GetDirectories(parentPath).FirstOrDefault(d => d.Name.Equals("metadata", StringComparison.Ordinal)); + if (metadataSubDir is not null) { - var files = directoryService.GetFiles(path.FullName); + var files = directoryService.GetFiles(metadataSubDir.FullName); images.AddRange(GetImageFilesFromFolder(nameWithoutExtension, files)); } From b24d05bff71149dcd6f13500414f7c364711acd9 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 8 Jun 2024 22:35:50 +0200 Subject: [PATCH 12/30] Apply review suggestion --- MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index b14ace57d2..f00d508bbe 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -55,6 +55,7 @@ namespace MediaBrowser.LocalMetadata.Images private List GetImageFilesFromFolder(ReadOnlySpan filenameWithoutExtension, List filePaths) { var list = new List(1); + var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); foreach (var i in filePaths) { @@ -66,7 +67,6 @@ namespace MediaBrowser.LocalMetadata.Images if (BaseItem.SupportedImageExtensions.Contains(i.Extension.AsSpan(), StringComparison.OrdinalIgnoreCase)) { var currentNameWithoutExtension = Path.GetFileNameWithoutExtension(i.FullName.AsSpan()); - var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); if (filenameWithoutExtension.Equals(currentNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) { From f9e7d5229e8f8c5340134d5c7a5c4ce91208f907 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Jun 2024 00:46:19 +0200 Subject: [PATCH 13/30] Limit removal scope --- .../Library/LibraryManager.cs | 2 +- MediaBrowser.Providers/Manager/ImageSaver.cs | 25 ++++++++++++---- .../Manager/ItemImageProvider.cs | 29 +++++++++---------- .../Manager/MetadataService.cs | 17 ++++------- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 953fe19e05..ac2248264d 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1884,7 +1884,7 @@ namespace Emby.Server.Implementations.Library try { var index = item.GetImageIndex(img); - image = await ConvertImageToLocal(item, img, index, removeOnFailure: true).ConfigureAwait(false); + image = await ConvertImageToLocal(item, img, index, true).ConfigureAwait(false); } catch (ArgumentException) { diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index b9b07da67d..7e881cb976 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -189,12 +189,27 @@ namespace MediaBrowser.Providers.Manager { _fileSystem.DeleteFile(currentPath); - // Remove containing directory if empty - var folder = Path.GetDirectoryName(currentPath); - FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); - if (!_fileSystem.GetFiles(folder).Any()) + // Remove local episode metadata directory if it exists and is empty + var directory = Path.GetDirectoryName(currentPath); + if (item is Episode && directory.Equals("metadata", StringComparison.Ordinal)) { - Directory.Delete(folder); + var parentDirectoryPath = Directory.GetParent(currentPath).FullName; + if (!_fileSystem.GetFiles(parentDirectoryPath).Any()) + { + try + { + _logger.LogInformation("Deleting empty local metadata folder {Folder}", parentDirectoryPath); + Directory.Delete(parentDirectoryPath); + } + catch (UnauthorizedAccessException ex) + { + _logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting directory {Path}", parentDirectoryPath); + } + } } } catch (FileNotFoundException) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index ad80611261..f89dc51795 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -10,7 +10,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; @@ -97,7 +97,7 @@ namespace MediaBrowser.Providers.Manager public bool ValidateImages(BaseItem item, IEnumerable providers, ImageRefreshOptions refreshOptions) { var hasChanges = false; - IDirectoryService directoryService = refreshOptions?.DirectoryService; + var directoryService = refreshOptions?.DirectoryService; if (item is not Photo) { @@ -360,10 +360,8 @@ namespace MediaBrowser.Providers.Manager private void PruneImages(BaseItem item, IReadOnlyList images) { - for (var i = 0; i < images.Count; i++) + foreach (var image in images) { - var image = images[i]; - if (image.IsLocalFile) { try @@ -378,16 +376,17 @@ namespace MediaBrowser.Providers.Manager { _logger.LogWarning(ex, "Unable to delete {Image}", image.Path); } - finally - { - // Always remove empty parent folder - var folder = Path.GetDirectoryName(image.Path); - FileSystemHelper.DeleteEmptyFolders(_fileSystem, folder, _logger); - if (!_fileSystem.GetFiles(folder).Any()) - { - Directory.Delete(folder); - } - } + } + } + + // Cleanup old metadata directory for episodes if empty + if (item is Episode) + { + var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata"); + var localImages = images.Where(i => i.Path.StartsWith(oldLocalMetadataDirectory, StringComparison.Ordinal)).ToList(); + if (!_fileSystem.GetFiles(oldLocalMetadataDirectory).Any()) + { + Directory.Delete(oldLocalMetadataDirectory); } } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 61a4d75863..934f88c650 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -92,10 +92,6 @@ namespace MediaBrowser.Providers.Manager } } - var localImagesFailed = false; - - var allImageProviders = ProviderManager.GetImageProviders(item, refreshOptions).ToList(); - if (refreshOptions.RemoveOldMetadata && refreshOptions.ReplaceAllImages) { if (ImageProvider.RemoveImages(item)) @@ -105,6 +101,8 @@ namespace MediaBrowser.Providers.Manager } // Start by validating images + var localImagesFailed = false; + var allImageProviders = ProviderManager.GetImageProviders(item, refreshOptions).ToList(); try { // Always validate images and check for new locally stored ones. @@ -811,19 +809,16 @@ namespace MediaBrowser.Providers.Manager { var refreshResult = new RefreshResult(); - var tmpDataMerged = false; + if (id is not null) + { + MergeNewData(temp.Item, id); + } foreach (var provider in providers) { var providerName = provider.GetType().Name; Logger.LogDebug("Running {Provider} for {Item}", providerName, logName); - if (id is not null && !tmpDataMerged) - { - MergeNewData(temp.Item, id); - tmpDataMerged = true; - } - try { var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false); From b63f7a2bc05c39236b061f2e06862e19f187e894 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Jun 2024 00:46:46 +0200 Subject: [PATCH 14/30] Only remove image from item if file system delete was successful --- MediaBrowser.Controller/Entities/BaseItem.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 184bb4d688..8bd4fb4f38 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1949,14 +1949,15 @@ namespace MediaBrowser.Controller.Entities return; } - // Remove it from the item - RemoveImage(info); - + // Remove from file system if (info.IsLocalFile) { FileSystem.DeleteFile(info.Path); } + // Remove from item + RemoveImage(info); + await UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false); } From 8b442a77495fd4788704e521ecbcf172aaa6ef8b Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Jun 2024 08:24:58 +0200 Subject: [PATCH 15/30] Check for existence before trying to delete directory --- MediaBrowser.Providers/Manager/ImageSaver.cs | 2 +- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 7e881cb976..4b3b0a14d0 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.Manager if (item is Episode && directory.Equals("metadata", StringComparison.Ordinal)) { var parentDirectoryPath = Directory.GetParent(currentPath).FullName; - if (!_fileSystem.GetFiles(parentDirectoryPath).Any()) + if (_fileSystem.DirectoryExists(parentDirectoryPath) && !_fileSystem.GetFiles(parentDirectoryPath).Any()) { try { diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index f89dc51795..245489ea21 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -384,7 +384,7 @@ namespace MediaBrowser.Providers.Manager { var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata"); var localImages = images.Where(i => i.Path.StartsWith(oldLocalMetadataDirectory, StringComparison.Ordinal)).ToList(); - if (!_fileSystem.GetFiles(oldLocalMetadataDirectory).Any()) + if (_fileSystem.DirectoryExists(oldLocalMetadataDirectory) && !_fileSystem.GetFiles(oldLocalMetadataDirectory).Any()) { Directory.Delete(oldLocalMetadataDirectory); } From 8e5a2f565ce2e3263bde9d91b2baca1993e9bf0d Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 9 Jun 2024 21:23:13 +0800 Subject: [PATCH 16/30] Fix mpeg-ts detection When the container name is `mpeg`, it means it is MPEG-PS, while the TS container should have the explicit name `mpeg-ts`. Signed-off-by: gnattu --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a587fa9db9..5db8752739 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -280,8 +280,8 @@ namespace MediaBrowser.MediaEncoding.Probing splitFormat[i] = "mpeg"; } - // Handle MPEG-2 container - else if (string.Equals(splitFormat[i], "mpeg", StringComparison.OrdinalIgnoreCase)) + // Handle MPEG-TS container + else if (string.Equals(splitFormat[i], "mpegts", StringComparison.OrdinalIgnoreCase)) { splitFormat[i] = "ts"; } From e4078f984aaeead74b435f80b3ef989f8fdc680a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Jun 2024 18:47:21 +0200 Subject: [PATCH 17/30] Fix season handling --- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/Entities/TV/Season.cs | 15 ++++++++++++ MediaBrowser.Controller/Entities/TV/Series.cs | 24 ++++++++++++------- .../TV/SeriesMetadataService.cs | 20 ++++++++-------- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index fcb45e7e58..235495c0c6 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -194,7 +194,7 @@ namespace MediaBrowser.Controller.Entities /// /// The item. /// Unable to add + item.Name. - public void AddChild(BaseItem item) + public virtual void AddChild(BaseItem item) { item.SetParent(this); diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 083f12746e..8444382578 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -265,5 +265,20 @@ namespace MediaBrowser.Controller.Entities.TV return hasChanges; } + + /// + public override void AddChild(BaseItem item) + { + if (item is Episode episode) + { + episode.SeriesPresentationUniqueKey = SeriesPresentationUniqueKey; + episode.SeriesId = SeriesId; + episode.SeriesName = SeriesName; + episode.SeasonId = Id; + episode.SeasonName = Name; + } + + base.AddChild(item); + } } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index d704208cde..dd3df510ff 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -350,17 +350,10 @@ namespace MediaBrowser.Controller.Entities.TV public List GetSeasonEpisodes(Season parentSeason, User user, DtoOptions options, bool shouldIncludeMissingEpisodes) { - var queryFromSeries = ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons; - - // add optimization when this setting is not enabled - var seriesKey = queryFromSeries ? - GetUniqueSeriesKey(this) : - GetUniqueSeriesKey(parentSeason); - var query = new InternalItemsQuery(user) { - AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey, - SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null, + AncestorWithPresentationUniqueKey = null, + SeriesPresentationUniqueKey = GetUniqueSeriesKey(this), IncludeItemTypes = new[] { BaseItemKind.Episode }, OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, DtoOptions = options @@ -506,5 +499,18 @@ namespace MediaBrowser.Controller.Entities.TV return list; } + + /// + public override void AddChild(BaseItem item) + { + if (item is IHasSeries typedItem) + { + typedItem.SeriesId = Id; + typedItem.SeriesName = Name; + typedItem.SeriesPresentationUniqueKey = PresentationUniqueKey; + } + + base.AddChild(item); + } } } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 2389bce577..13aa6226e7 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Providers.TV private void RemoveObsoleteSeasons(Series series) { - // TODO Legacy. It's not really "physical" seasons as any virtual seasons are always converted to non-virtual in UpdateAndCreateSeasonsAsync. + // TODO Legacy. It's not really "physical" seasons as any virtual seasons are always converted to non-virtual in CreateSeasonsAsync. var physicalSeasonNumbers = new HashSet(); var virtualSeasons = new List(); foreach (var existingSeason in series.Children.OfType()) @@ -203,11 +203,16 @@ namespace MediaBrowser.Providers.TV foreach (var seasonNumber in uniqueSeasonNumbers) { // Null season numbers will have a 'dummy' season created because seasons are always required. - if (!seasons.Any(i => i.IndexNumber == seasonNumber)) + var existingSeason = seasons.FirstOrDefault(i => i.IndexNumber == seasonNumber); + if (existingSeason is null) { var seasonName = GetValidSeasonNameForSeries(series, null, seasonNumber); - var season = await CreateSeasonAsync(series, seasonName, seasonNumber, cancellationToken).ConfigureAwait(false); - series.AddChild(season); + await CreateSeasonAsync(series, seasonName, seasonNumber, cancellationToken).ConfigureAwait(false); + } + else if (existingSeason.IsVirtualItem) + { + existingSeason.IsVirtualItem = false; + await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); } } } @@ -220,7 +225,7 @@ namespace MediaBrowser.Providers.TV /// The season number. /// The cancellation token. /// The newly created season. - private async Task CreateSeasonAsync( + private async Task CreateSeasonAsync( Series series, string? seasonName, int? seasonNumber, @@ -236,15 +241,10 @@ namespace MediaBrowser.Providers.TV series.Id + (seasonNumber ?? -1).ToString(CultureInfo.InvariantCulture) + seasonName, typeof(Season)), IsVirtualItem = false, - SeriesId = series.Id, - SeriesName = series.Name }; series.AddChild(season); - await season.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken).ConfigureAwait(false); - - return season; } private string GetValidSeasonNameForSeries(Series series, string? seasonName, int? seasonNumber) From 8882bb495c2fedca1f42625ba0b489f33b57cb92 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sun, 9 Jun 2024 20:48:25 +0200 Subject: [PATCH 18/30] Rewrite PlaylistItemsProvider as ILocalMetadataProvider --- .../Playlists/PlaylistItemsProvider.cs | 324 ++++++++++-------- 1 file changed, 176 insertions(+), 148 deletions(-) diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index 4eb75b82ff..51a3ba0c7f 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.IO; @@ -18,182 +16,212 @@ using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; using PlaylistsNET.Content; -namespace MediaBrowser.Providers.Playlists +namespace MediaBrowser.Providers.Playlists; + +/// +/// Local playlist provider. +/// +public class PlaylistItemsProvider : ILocalMetadataProvider, + IHasOrder, + IForcedProvider, + IHasItemChangeMonitor { - public class PlaylistItemsProvider : ICustomMetadataProvider, - IHasOrder, - IForcedProvider, - IPreRefreshProvider, - IHasItemChangeMonitor + private readonly IFileSystem _fileSystem; + private readonly ILibraryManager _libraryManager; + private readonly ILogger _logger; + private readonly CollectionType[] _ignoredCollections = [CollectionType.livetv, CollectionType.boxsets, CollectionType.playlists]; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public PlaylistItemsProvider(ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem) { - private readonly IFileSystem _fileSystem; - private readonly ILibraryManager _libraryManager; - private readonly ILogger _logger; - private readonly CollectionType[] _ignoredCollections = [CollectionType.livetv, CollectionType.boxsets, CollectionType.playlists]; + _logger = logger; + _libraryManager = libraryManager; + _fileSystem = fileSystem; + } - public PlaylistItemsProvider(ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem) + /// + public string Name => "Playlist Item Provider"; + + /// + public int Order => 100; + + /// + public Task> GetMetadata( + ItemInfo info, + IDirectoryService directoryService, + CancellationToken cancellationToken) + { + var result = new MetadataResult() { - _logger = logger; - _libraryManager = libraryManager; - _fileSystem = fileSystem; + Item = new Playlist + { + Path = info.Path + } + }; + Fetch(result); + + return Task.FromResult(result); + } + + private void Fetch(MetadataResult result) + { + var item = result.Item; + var path = item.Path; + if (!Playlist.IsPlaylistFile(path)) + { + return; } - public string Name => "Playlist Reader"; - - // Run last - public int Order => 100; - - public Task FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken) + var extension = Path.GetExtension(path); + if (!Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { - var path = item.Path; - if (!Playlist.IsPlaylistFile(path)) - { - return Task.FromResult(ItemUpdateType.None); - } - - var extension = Path.GetExtension(path); - if (!Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - return Task.FromResult(ItemUpdateType.None); - } - - var items = GetItems(path, extension).ToArray(); + return; + } + var items = GetItems(path, extension).ToArray(); + if (items.Length > 0) + { + result.HasMetadata = true; item.LinkedChildren = items; - - return Task.FromResult(ItemUpdateType.MetadataImport); } - private IEnumerable GetItems(string path, string extension) + return; + } + + private IEnumerable GetItems(string path, string extension) + { + var libraryRoots = _libraryManager.GetUserRootFolder().Children + .OfType() + .Where(f => f.CollectionType.HasValue && !_ignoredCollections.Contains(f.CollectionType.Value)) + .SelectMany(f => f.PhysicalLocations) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + using (var stream = File.OpenRead(path)) { - var libraryRoots = _libraryManager.GetUserRootFolder().Children - .OfType() - .Where(f => f.CollectionType.HasValue && !_ignoredCollections.Contains(f.CollectionType.Value)) - .SelectMany(f => f.PhysicalLocations) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - using (var stream = File.OpenRead(path)) + if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase)) - { - return GetWplItems(stream, path, libraryRoots); - } - - if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase)) - { - return GetZplItems(stream, path, libraryRoots); - } - - if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase)) - { - return GetM3uItems(stream, path, libraryRoots); - } - - if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase)) - { - return GetM3uItems(stream, path, libraryRoots); - } - - if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase)) - { - return GetPlsItems(stream, path, libraryRoots); - } + return GetWplItems(stream, path, libraryRoots); } - return Enumerable.Empty(); - } - - private IEnumerable GetPlsItems(Stream stream, string playlistPath, List libraryRoots) - { - var content = new PlsContent(); - var playlist = content.GetFromStream(stream); - - return playlist.PlaylistEntries - .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) - .Where(i => i is not null); - } - - private IEnumerable GetM3uItems(Stream stream, string playlistPath, List libraryRoots) - { - var content = new M3uContent(); - var playlist = content.GetFromStream(stream); - - return playlist.PlaylistEntries - .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) - .Where(i => i is not null); - } - - private IEnumerable GetZplItems(Stream stream, string playlistPath, List libraryRoots) - { - var content = new ZplContent(); - var playlist = content.GetFromStream(stream); - - return playlist.PlaylistEntries - .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) - .Where(i => i is not null); - } - - private IEnumerable GetWplItems(Stream stream, string playlistPath, List libraryRoots) - { - var content = new WplContent(); - var playlist = content.GetFromStream(stream); - - return playlist.PlaylistEntries - .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) - .Where(i => i is not null); - } - - private LinkedChild GetLinkedChild(string itemPath, string playlistPath, List libraryRoots) - { - if (TryGetPlaylistItemPath(itemPath, playlistPath, libraryRoots, out var parsedPath)) + if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase)) { - return new LinkedChild - { - Path = parsedPath, - Type = LinkedChildType.Manual - }; + return GetZplItems(stream, path, libraryRoots); } - return null; + if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase)) + { + return GetM3uItems(stream, path, libraryRoots); + } + + if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase)) + { + return GetM3uItems(stream, path, libraryRoots); + } + + if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase)) + { + return GetPlsItems(stream, path, libraryRoots); + } } - private bool TryGetPlaylistItemPath(string itemPath, string playlistPath, List libraryPaths, out string path) + return Enumerable.Empty(); + } + + private IEnumerable GetPlsItems(Stream stream, string playlistPath, List libraryRoots) + { + var content = new PlsContent(); + var playlist = content.GetFromStream(stream); + + return playlist.PlaylistEntries + .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) + .Where(i => i is not null); + } + + private IEnumerable GetM3uItems(Stream stream, string playlistPath, List libraryRoots) + { + var content = new M3uContent(); + var playlist = content.GetFromStream(stream); + + return playlist.PlaylistEntries + .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) + .Where(i => i is not null); + } + + private IEnumerable GetZplItems(Stream stream, string playlistPath, List libraryRoots) + { + var content = new ZplContent(); + var playlist = content.GetFromStream(stream); + + return playlist.PlaylistEntries + .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) + .Where(i => i is not null); + } + + private IEnumerable GetWplItems(Stream stream, string playlistPath, List libraryRoots) + { + var content = new WplContent(); + var playlist = content.GetFromStream(stream); + + return playlist.PlaylistEntries + .Select(i => GetLinkedChild(i.Path, playlistPath, libraryRoots)) + .Where(i => i is not null); + } + + private LinkedChild GetLinkedChild(string itemPath, string playlistPath, List libraryRoots) + { + if (TryGetPlaylistItemPath(itemPath, playlistPath, libraryRoots, out var parsedPath)) { - path = null; - string pathToCheck = _fileSystem.MakeAbsolutePath(Path.GetDirectoryName(playlistPath), itemPath); - if (!File.Exists(pathToCheck)) + return new LinkedChild { - return false; - } + Path = parsedPath, + Type = LinkedChildType.Manual + }; + } - foreach (var libraryPath in libraryPaths) - { - if (pathToCheck.StartsWith(libraryPath, StringComparison.OrdinalIgnoreCase)) - { - path = pathToCheck; - return true; - } - } + return null; + } + private bool TryGetPlaylistItemPath(string itemPath, string playlistPath, List libraryPaths, out string path) + { + path = null; + string pathToCheck = _fileSystem.MakeAbsolutePath(Path.GetDirectoryName(playlistPath), itemPath); + if (!File.Exists(pathToCheck)) + { return false; } - public bool HasChanged(BaseItem item, IDirectoryService directoryService) + foreach (var libraryPath in libraryPaths) { - var path = item.Path; - - if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol) + if (pathToCheck.StartsWith(libraryPath, StringComparison.OrdinalIgnoreCase)) { - var file = directoryService.GetFile(path); - if (file is not null && file.LastWriteTimeUtc != item.DateModified) - { - _logger.LogDebug("Refreshing {Path} due to date modified timestamp change.", path); - return true; - } + path = pathToCheck; + return true; } - - return false; } + + return false; + } + + /// + public bool HasChanged(BaseItem item, IDirectoryService directoryService) + { + var path = item.Path; + if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol) + { + var file = directoryService.GetFile(path); + if (file is not null && file.LastWriteTimeUtc != item.DateModified) + { + _logger.LogDebug("Refreshing {Path} due to date modified timestamp change.", path); + return true; + } + } + + return false; } } From 7ff3f6af6c95d3d403ac79bb0958198885d64447 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 10 Jun 2024 21:43:42 +0200 Subject: [PATCH 19/30] Fix MB release group query --- .../Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index d0bd7d6098..c35324746a 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -250,7 +250,7 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider Date: Tue, 11 Jun 2024 18:48:38 +0200 Subject: [PATCH 20/30] Apply review suggestion --- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/Entities/TV/Season.cs | 15 --------------- MediaBrowser.Controller/Entities/TV/Series.cs | 13 ------------- .../TV/SeriesMetadataService.cs | 3 +++ 4 files changed, 4 insertions(+), 29 deletions(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 235495c0c6..fcb45e7e58 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -194,7 +194,7 @@ namespace MediaBrowser.Controller.Entities /// /// The item. /// Unable to add + item.Name. - public virtual void AddChild(BaseItem item) + public void AddChild(BaseItem item) { item.SetParent(this); diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 8444382578..083f12746e 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -265,20 +265,5 @@ namespace MediaBrowser.Controller.Entities.TV return hasChanges; } - - /// - public override void AddChild(BaseItem item) - { - if (item is Episode episode) - { - episode.SeriesPresentationUniqueKey = SeriesPresentationUniqueKey; - episode.SeriesId = SeriesId; - episode.SeriesName = SeriesName; - episode.SeasonId = Id; - episode.SeasonName = Name; - } - - base.AddChild(item); - } } } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index dd3df510ff..6297b67e46 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -499,18 +499,5 @@ namespace MediaBrowser.Controller.Entities.TV return list; } - - /// - public override void AddChild(BaseItem item) - { - if (item is IHasSeries typedItem) - { - typedItem.SeriesId = Id; - typedItem.SeriesName = Name; - typedItem.SeriesPresentationUniqueKey = PresentationUniqueKey; - } - - base.AddChild(item); - } } } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 13aa6226e7..5ede023f1c 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -241,6 +241,9 @@ namespace MediaBrowser.Providers.TV series.Id + (seasonNumber ?? -1).ToString(CultureInfo.InvariantCulture) + seasonName, typeof(Season)), IsVirtualItem = false, + SeriesId = series.Id, + SeriesName = series.Name, + SeriesPresentationUniqueKey = series.GetPresentationUniqueKey() }; series.AddChild(season); From fcec1fcc4da1de7d65047e3f08054a2ea0e228e7 Mon Sep 17 00:00:00 2001 From: Rivenlalala Date: Mon, 10 Jun 2024 13:41:33 +0000 Subject: [PATCH 21/30] Make m2ts extension case-insensitive --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f85510dac7..d0d41c2d38 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1155,10 +1155,10 @@ namespace MediaBrowser.MediaEncoding.Encoder // Get all files from the BDMV/STREAMING directory // Only return playable local .m2ts files + var files = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM")).ToList(); return validPlaybackFiles - .Select(f => _fileSystem.GetFileInfo(Path.Join(path, "BDMV", "STREAM", f))) - .Where(f => f.Exists) - .Select(f => f.FullName) + .Select(validFile => files.FirstOrDefault(f => Path.GetFileName(f.FullName.AsSpan()).Equals(validFile, StringComparison.OrdinalIgnoreCase))?.FullName) + .Where(f => f is not null) .ToList(); } From 56104d304297123c67765c3312bf8d63f77a4041 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 14 Jun 2024 10:22:10 +0200 Subject: [PATCH 22/30] Map IPv6 mapped IPv4 addresses back to IPv4 before running checks --- src/Jellyfin.Networking/Manager/NetworkManager.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 148b33fcbd..cf6a2cc553 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -919,10 +919,14 @@ public class NetworkManager : INetworkManager, IDisposable { ArgumentNullException.ThrowIfNull(address); - // See conversation at https://github.com/jellyfin/jellyfin/pull/3515. + // Map IPv6 mapped IPv4 back to IPv4 (happens if Kestrel runs in dual-socket mode) + if (address.IsIPv4MappedToIPv6) + { + address = address.MapToIPv4(); + } + if ((TrustAllIPv6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6) - || address.Equals(IPAddress.Loopback) - || address.Equals(IPAddress.IPv6Loopback)) + || IPAddress.IsLoopback(address)) { return true; } From 9f35f56eaff08442bb022464352a230cc7cb20fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Sun, 9 Jun 2024 12:03:11 +0200 Subject: [PATCH 23/30] Fix the Australian PG rating As per https://www.classification.gov.au/classification-ratings/what-are-ratings Fixes #11650 Well, sort of. I don't think it is possible to differentiate between them, as we'd be comparing the integer values, not the position in the list? --- Emby.Server.Implementations/Localization/Ratings/au.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv index 6881259172..6e12759a46 100644 --- a/Emby.Server.Implementations/Localization/Ratings/au.csv +++ b/Emby.Server.Implementations/Localization/Ratings/au.csv @@ -1,11 +1,11 @@ Exempt,0 G,0 7+,7 +PG,15 M,15 MA,15 MA15+,15 MA 15+,15 -PG,16 16+,16 R,18 R18+,18 From 55d245a77b001821600ee1dcf7671dfe164332d9 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 17 Jun 2024 23:13:53 +0200 Subject: [PATCH 24/30] Fix saving item metadata --- .../Tasks/CleanupCollectionAndPlaylistPathsTask.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs index 19b2454641..8040972192 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs @@ -127,15 +127,8 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask { _logger.LogDebug("Updating {FolderName}", folder.Name); folder.LinkedChildren = folder.LinkedChildren.Except(itemsToRemove).ToArray(); + _providerManager.SaveMetadataAsync(folder, ItemUpdateType.MetadataEdit); folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken); - - _providerManager.QueueRefresh( - folder.Id, - new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - ForceSave = true - }, - RefreshPriority.High); } } From bf53f1ae385bd7631b0213182a155b109cd2bf10 Mon Sep 17 00:00:00 2001 From: Tim Eisele Date: Tue, 18 Jun 2024 16:16:21 +0200 Subject: [PATCH 25/30] Do not override if is set (#12120) --- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 97cdc68545..d049c5a8ef 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -519,7 +519,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (reader.TryReadDateTimeExact(nfoConfiguration.ReleaseDateFormat, out var releaseDate)) { item.PremiereDate = releaseDate; - item.ProductionYear = releaseDate.Year; + + // Production year can already be set by the year tag + item.ProductionYear ??= releaseDate.Year; } break; From a00f9e1a106c15410d62a13a772c9fdf11f6451a Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 20 Jun 2024 22:03:01 +0200 Subject: [PATCH 26/30] Cleanup seasons after creating real ones --- MediaBrowser.Providers/TV/SeriesMetadataService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 5ede023f1c..b03d6ffb56 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -61,8 +61,8 @@ namespace MediaBrowser.Providers.TV await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false); RemoveObsoleteEpisodes(item); - RemoveObsoleteSeasons(item); await CreateSeasonsAsync(item, cancellationToken).ConfigureAwait(false); + RemoveObsoleteSeasons(item); } /// From 6010bc01c321ea85935314191f5f8c19fd4b3bfb Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 21 Jun 2024 23:07:25 +0800 Subject: [PATCH 27/30] Fix MicroDVD being recognized as DVDSUB subtitles (#12149) --- .../MediaEncoding/EncodingHelper.cs | 4 ++-- .../Probing/ProbeResultNormalizer.cs | 14 +++++++++----- MediaBrowser.Model/Entities/MediaStream.cs | 14 +++++++------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 03c7faf471..066fb85ec6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1208,8 +1208,8 @@ namespace MediaBrowser.Controller.MediaEncoding var subtitlePath = state.SubtitleStream.Path; var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan()); - if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase) - || subtitleExtension.Equals(".sup", StringComparison.OrdinalIgnoreCase)) + // dvdsub/vobsub graphical subtitles use .sub+.idx pairs + if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase)) { var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); if (File.Exists(idxFile)) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 7d3f9327c2..8b2685fe15 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -624,15 +624,19 @@ namespace MediaBrowser.MediaEncoding.Probing { if (string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase)) { - codec = "dvbsub"; + codec = "DVBSUB"; } - else if ((codec ?? string.Empty).Contains("PGS", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(codec, "dvb_teletext", StringComparison.OrdinalIgnoreCase)) { - codec = "PGSSUB"; + codec = "DVBTXT"; } - else if ((codec ?? string.Empty).Contains("DVD", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(codec, "dvd_subtitle", StringComparison.OrdinalIgnoreCase)) { - codec = "DVDSUB"; + codec = "DVDSUB"; // .sub+.idx + } + else if (string.Equals(codec, "hdmv_pgs_subtitle", StringComparison.OrdinalIgnoreCase)) + { + codec = "PGSSUB"; // .sup } return codec; diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 0d2d7c6965..a260f50ba4 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -656,14 +656,14 @@ namespace MediaBrowser.Model.Entities { string codec = format ?? string.Empty; - // sub = external .sub file + // microdvd and dvdsub/vobsub share the ".sub" file extension, but it's text-based. - return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) - && !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase) - && !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) - && !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) - && !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase) - && !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase); + return codec.Contains("microdvd", StringComparison.OrdinalIgnoreCase) + || (!codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) + && !codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase) + && !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) + && !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase) + && !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase)); } public bool SupportsSubtitleConversionTo(string toCodec) From 34a65980e31b7f2f45308a6247442c508d0d2098 Mon Sep 17 00:00:00 2001 From: Tim Eisele Date: Fri, 21 Jun 2024 17:07:38 +0200 Subject: [PATCH 28/30] Remove incomplete mediatype restriction from playlists (#12024) --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 6 +++--- MediaBrowser.Controller/Playlists/Playlist.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 0c7840ee59..47ff22c0b3 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -189,11 +189,11 @@ namespace Emby.Server.Implementations.Playlists return path; } - private IReadOnlyList GetPlaylistItems(IEnumerable itemIds, MediaType playlistMediaType, User user, DtoOptions options) + private IReadOnlyList GetPlaylistItems(IEnumerable itemIds, User user, DtoOptions options) { var items = itemIds.Select(_libraryManager.GetItemById).Where(i => i is not null); - return Playlist.GetPlaylistItems(playlistMediaType, items, user, options); + return Playlist.GetPlaylistItems(items, user, options); } public Task AddItemToPlaylistAsync(Guid playlistId, IReadOnlyCollection itemIds, Guid userId) @@ -213,7 +213,7 @@ namespace Emby.Server.Implementations.Playlists ?? throw new ArgumentException("No Playlist exists with Id " + playlistId); // Retrieve all the items to be added to the playlist - var newItems = GetPlaylistItems(newItemIds, playlist.MediaType, user, options) + var newItems = GetPlaylistItems(newItemIds, user, options) .Where(i => i.SupportsAddingToPlaylist); // Filter out duplicate items, if necessary diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 6fc9a7e1bf..45aefacf6d 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -166,7 +166,7 @@ namespace MediaBrowser.Controller.Playlists return base.GetChildren(user, true, query); } - public static IReadOnlyList GetPlaylistItems(MediaType playlistMediaType, IEnumerable inputItems, User user, DtoOptions options) + public static IReadOnlyList GetPlaylistItems(IEnumerable inputItems, User user, DtoOptions options) { if (user is not null) { @@ -177,14 +177,14 @@ namespace MediaBrowser.Controller.Playlists foreach (var item in inputItems) { - var playlistItems = GetPlaylistItems(item, user, playlistMediaType, options); + var playlistItems = GetPlaylistItems(item, user, options); list.AddRange(playlistItems); } return list; } - private static IEnumerable GetPlaylistItems(BaseItem item, User user, MediaType mediaType, DtoOptions options) + private static IEnumerable GetPlaylistItems(BaseItem item, User user, DtoOptions options) { if (item is MusicGenre musicGenre) { @@ -216,7 +216,7 @@ namespace MediaBrowser.Controller.Playlists { Recursive = true, IsFolder = false, - MediaTypes = [mediaType], + MediaTypes = [MediaType.Audio, MediaType.Video], EnableTotalRecordCount = false, DtoOptions = options }; From 6cf98d49300459b7795b994733ba05837e22cf1e Mon Sep 17 00:00:00 2001 From: Tim Eisele Date: Fri, 21 Jun 2024 17:08:05 +0200 Subject: [PATCH 29/30] Only cleanup children on specific exceptions (#12134) --- MediaBrowser.Controller/Entities/Folder.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index fcb45e7e58..b2e5d7263f 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; @@ -370,9 +371,18 @@ namespace MediaBrowser.Controller.Entities { nonCachedChildren = GetNonCachedChildren(directoryService); } + catch (IOException ex) + { + Logger.LogError(ex, "Error retrieving children from file system"); + } + catch (SecurityException ex) + { + Logger.LogError(ex, "Error retrieving children from file system"); + } catch (Exception ex) { - Logger.LogError(ex, "Error retrieving children folder"); + Logger.LogError(ex, "Error retrieving children"); + return; } progress.Report(ProgressHelpers.RetrievedChildren); From aadd57bc486f85fdded44058c82adfbc64dda0ee Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 24 Jun 2024 09:16:51 +0200 Subject: [PATCH 30/30] Fix check --- MediaBrowser.Providers/Manager/ItemImageProvider.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 245489ea21..4690845d2a 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -379,18 +379,17 @@ namespace MediaBrowser.Providers.Manager } } + item.RemoveImages(images); + // Cleanup old metadata directory for episodes if empty if (item is Episode) { var oldLocalMetadataDirectory = Path.Combine(item.ContainingFolderPath, "metadata"); - var localImages = images.Where(i => i.Path.StartsWith(oldLocalMetadataDirectory, StringComparison.Ordinal)).ToList(); if (_fileSystem.DirectoryExists(oldLocalMetadataDirectory) && !_fileSystem.GetFiles(oldLocalMetadataDirectory).Any()) { Directory.Delete(oldLocalMetadataDirectory); } } - - item.RemoveImages(images); } ///