diff options
18 files changed, 81 insertions, 57 deletions
diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 9d54533c2..7a01b02f3 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/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv index 688125917..6e12759a4 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 diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 8a35b96b3..47ff22c0b 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<Playlist> GetUserPlaylists(Guid userId) { var user = _userManager.GetUserById(userId); + var playlistsFolder = GetPlaylistsFolder(userId); + if (playlistsFolder is null) + { + return []; + } - return GetPlaylistsFolder(userId).GetChildren(user, true).OfType<Playlist>().ToList(); + return playlistsFolder.GetChildren(user, true).OfType<Playlist>().ToList(); } private static string GetTargetPath(string path) @@ -184,11 +189,11 @@ namespace Emby.Server.Implementations.Playlists return path; } - private IReadOnlyList<BaseItem> GetPlaylistItems(IEnumerable<Guid> itemIds, MediaType playlistMediaType, User user, DtoOptions options) + private IReadOnlyList<BaseItem> GetPlaylistItems(IEnumerable<Guid> 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<Guid> itemIds, Guid userId) @@ -208,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/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs index 19b245464..804097219 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); } } diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index c1343b130..d7a8c37c4 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); diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index af4a9e689..535ef27c3 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); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index fcb45e7e5..b2e5d7263 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); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a845c8124..9d7d2fd12 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1209,8 +1209,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.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 6fc9a7e1b..45aefacf6 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<BaseItem> GetPlaylistItems(MediaType playlistMediaType, IEnumerable<BaseItem> inputItems, User user, DtoOptions options) + public static IReadOnlyList<BaseItem> GetPlaylistItems(IEnumerable<BaseItem> 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<BaseItem> GetPlaylistItems(BaseItem item, User user, MediaType mediaType, DtoOptions options) + private static IEnumerable<BaseItem> 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 }; diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index 6a5e3bf04..f00d508bb 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -40,13 +40,12 @@ 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) + 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)); } @@ -55,9 +54,8 @@ namespace MediaBrowser.LocalMetadata.Images private List<LocalImageInfo> GetImageFilesFromFolder(ReadOnlySpan<char> filenameWithoutExtension, List<FileSystemMetadata> filePaths) { - var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); - var list = new List<LocalImageInfo>(1); + var thumbName = string.Concat(filenameWithoutExtension, "-thumb"); foreach (var i in filePaths) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f85510dac..d0d41c2d3 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(); } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a587fa9db..8b2685fe1 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"; } @@ -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; @@ -779,11 +783,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; } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 3e01c6efc..e1082adea 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 - - 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); + // microdvd and dvdsub/vobsub share the ".sub" file extension, but it's text-based. + + 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) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 7fd64809d..1751c5b9b 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<ImageType>(); diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index d0bd7d609..c35324746 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -250,7 +250,7 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu // If we have a release ID but not a release group ID, lookup the release group if (!string.IsNullOrWhiteSpace(releaseId) && string.IsNullOrWhiteSpace(releaseGroupId)) { - var release = await _musicBrainzQuery.LookupReleaseAsync(new Guid(releaseId), Include.Releases, cancellationToken).ConfigureAwait(false); + var release = await _musicBrainzQuery.LookupReleaseAsync(new Guid(releaseId), Include.ReleaseGroups, cancellationToken).ConfigureAwait(false); releaseGroupId = release.ReleaseGroup?.Id.ToString(); result.HasMetadata = true; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 97cdc6854..d049c5a8e 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; diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 148b33fcb..cf6a2cc55 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; } diff --git a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs index ba602b5d2..0b8b1f644 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"; |
