From 0c8b9091a55f4be1b6dbb4a0725a05aca52b422f Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 3 May 2022 15:48:46 +0200 Subject: [PATCH 001/105] Fix streambuilder reasons for direct playback checks --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 50 ++++++++++++------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index ee9cfeeca8c..ce75e678eab 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -385,7 +385,7 @@ namespace MediaBrowser.Model.Dlna // If device requirements are satisfied then allow both direct stream and direct play if (item.SupportsDirectPlay) { - if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay)) + if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay)) { if (options.EnableDirectPlay) { @@ -401,7 +401,7 @@ namespace MediaBrowser.Model.Dlna // While options takes the network and other factors into account. Only applies to direct stream if (item.SupportsDirectStream) { - if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream)) + if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream)) { if (options.EnableDirectStream) { @@ -604,11 +604,11 @@ namespace MediaBrowser.Model.Dlna var videoStream = item.VideoStream; - var directPlayEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay); - var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream); - bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult == 0); - bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directPlayEligibilityResult == 0); - var transcodeReasons = directPlayEligibilityResult | directStreamEligibilityResult; + var directPlayBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay); + var directStreamBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream); + bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayBitrateEligibility == 0); + bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamBitrateEligibility == 0); + var transcodeReasons = directPlayBitrateEligibility | directStreamBitrateEligibility; _logger.LogDebug( "Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", @@ -625,7 +625,7 @@ namespace MediaBrowser.Model.Dlna var directPlay = directPlayInfo.PlayMethod; transcodeReasons |= directPlayInfo.TranscodeReasons; - if (directPlay != null) + if (directPlay.HasValue) { directPlayProfile = directPlayInfo.Profile; playlistItem.PlayMethod = directPlay.Value; @@ -676,7 +676,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.TranscodeReasons = transcodeReasons; - if (playlistItem.PlayMethod != PlayMethod.DirectStream || !options.EnableDirectStream) + if (playlistItem.PlayMethod != PlayMethod.DirectStream && playlistItem.PlayMethod != PlayMethod.DirectPlay) { // Can't direct play, find the transcoding profile // If we do this for direct-stream we will overwrite the info @@ -687,6 +687,8 @@ namespace MediaBrowser.Model.Dlna BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, transcodingProfile.Container, transcodingProfile.VideoCodec, transcodingProfile.AudioCodec); + playlistItem.PlayMethod = PlayMethod.Transcode; + if (subtitleStream != null) { var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol); @@ -696,14 +698,9 @@ namespace MediaBrowser.Model.Dlna playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format }; } - if (playlistItem.PlayMethod != PlayMethod.DirectPlay) + if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0) { - playlistItem.PlayMethod = PlayMethod.Transcode; - - if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0) - { - ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true); - } + ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true); } } } @@ -771,12 +768,12 @@ namespace MediaBrowser.Model.Dlna private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable candidateAudioStreams, string container, string videoCodec, string audioCodec) { - // prefer matching video codecs + // Prefer matching video codecs var videoCodecs = ContainerProfile.SplitValue(videoCodec); var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null; playlistItem.VideoCodecs = directVideoCodec != null ? new[] { directVideoCodec } : videoCodecs; - // copy video codec options as a starting point, this applies to transcode and direct-stream + // Copy video codec options as a starting point, this applies to transcode and direct-stream playlistItem.MaxFramerate = videoStream?.AverageFrameRate; var qualifier = videoStream?.Codec; if (videoStream?.Level != null) @@ -799,7 +796,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString()); } - // prefer matching audio codecs, could do better here + // Prefer matching audio codecs, could do better here var audioCodecs = ContainerProfile.SplitValue(audioCodec); var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec)); playlistItem.AudioCodecs = audioCodecs; @@ -809,7 +806,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.AudioStreamIndex = audioStream.Index; playlistItem.AudioCodecs = new[] { audioStream.Codec }; - // copy matching audio codec options + // Copy matching audio codec options playlistItem.AudioSampleRate = audioStream.SampleRate; playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString()); @@ -1070,7 +1067,7 @@ namespace MediaBrowser.Model.Dlna DeviceProfile profile = options.Profile; string container = mediaSource.Container; - // video + // Video int? width = videoStream?.Width; int? height = videoStream?.Height; int? bitDepth = videoStream?.BitDepth; @@ -1082,7 +1079,7 @@ namespace MediaBrowser.Model.Dlna bool? isInterlaced = videoStream?.IsInterlaced; string videoCodecTag = videoStream?.CodecTag; bool? isAvc = videoStream?.IsAVC; - // audio + // Audio var defaultLanguage = audioStream?.Language ?? string.Empty; var defaultMarked = audioStream?.IsDefault ?? false; @@ -1211,6 +1208,7 @@ namespace MediaBrowser.Model.Dlna return (Result: (Profile: directPlayProfile, PlayMethod: playMethod, AudioStreamIndex: selectedAudioStream?.Index, TranscodeReason: failureReasons), Order: order, Rank: ranked); }) .OrderByDescending(analysis => analysis.Result.PlayMethod) + .ThenByDescending(analysis => analysis.Rank) .ThenBy(analysis => analysis.Order) .ToArray() .ToLookup(analysis => analysis.Result.PlayMethod != null); @@ -1223,7 +1221,7 @@ namespace MediaBrowser.Model.Dlna return profileMatch; } - var failureReasons = analyzedProfiles[false].OrderBy(a => a.Result.TranscodeReason).ThenBy(analysis => analysis.Order).FirstOrDefault().Result.TranscodeReason; + var failureReasons = analyzedProfiles[false].Select(analysis => analysis.Result).FirstOrDefault().TranscodeReason; if (failureReasons == 0) { failureReasons = TranscodeReason.DirectPlayError; @@ -1269,13 +1267,13 @@ namespace MediaBrowser.Model.Dlna mediaSource.Path ?? "Unknown path"); } - private TranscodeReason IsEligibleForDirectPlay( + private TranscodeReason IsBitrateEligibleForDirectPlayback( MediaSourceInfo item, long maxBitrate, VideoOptions options, PlayMethod playMethod) { - bool result = IsItemBitrateEligibleForDirectPlay(item, maxBitrate, playMethod); + bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod); if (!result) { return TranscodeReason.ContainerBitrateExceedsLimit; @@ -1443,7 +1441,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private bool IsItemBitrateEligibleForDirectPlay(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod) + private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod) { // Don't restrict by bitrate if coming from an external domain if (item.IsRemote) From 2be9a34b26fc38c22b0578dd67466faf0ee2e3ac Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 3 May 2022 18:31:19 +0200 Subject: [PATCH 002/105] Fix tests and profiles --- .../Dlna/StreamBuilderTests.cs | 49 +++++++++++-------- .../DeviceProfile-Tizen3-stereo.json | 8 +-- .../DeviceProfile-Tizen4-4K-5.1.json | 8 +-- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index 0035dc66535..9baf6877d99 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] - [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 + [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Firefox @@ -38,7 +38,7 @@ namespace Jellyfin.Model.Tests [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] - [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 + [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Safari @@ -89,7 +89,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome-NoHLS", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")] [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")] - [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 + [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // TranscodeMedia @@ -177,7 +177,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] - [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 + [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Firefox @@ -187,7 +187,7 @@ namespace Jellyfin.Model.Tests [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] - [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 + [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Safari @@ -338,23 +338,23 @@ namespace Jellyfin.Model.Tests Assert.NotNull(mediaSource); var videoStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Video); var audioStreams = mediaSource.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio); - // TODO: check AudioStreamIndex vs options.AudioStreamIndex + // TODO: Check AudioStreamIndex vs options.AudioStreamIndex var inputAudioStream = mediaSource.GetDefaultAudioStream(audioStreamIndexInput ?? mediaSource.DefaultAudioStreamIndex); var uri = ParseUri(val); if (playMethod == PlayMethod.DirectPlay) { - // check expected container + // Check expected container var containers = ContainerProfile.SplitValue(mediaSource.Container); - // TODO: test transcode too + // TODO: Test transcode too // Assert.Contains(uri.Extension, containers); - // check expected video codec (1) + // Check expected video codec (1) Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec); - // check expected audio codecs (1) + // Check expected audio codecs (1) Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec); Assert.Single(val.TargetAudioCodec); // Assert.Single(val.AudioCodecs); @@ -370,7 +370,7 @@ namespace Jellyfin.Model.Tests Assert.NotEmpty(val.VideoCodecs); Assert.NotEmpty(val.AudioCodecs); - // check expected container (todo: this could be a test param) + // Check expected container (todo: this could be a test param) if (transcodeProtocol == "http") { // Assert.Equal("webm", val.Container); @@ -403,32 +403,39 @@ namespace Jellyfin.Model.Tests stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs)); } - // todo: fill out tests here + // TODO: Fill out tests here } // DirectStream and Remux else { - // check expected video codec (1) + // Check expected video codec (1) Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec); if (transcodeMode == "DirectStream") { + // Check expected audio codecs (1) if (!targetAudioStream.IsExternal) { - // check expected audio codecs (1) - Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs); + if (val.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported)) + { + Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); + } + else + { + Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs); + } } } else if (transcodeMode == "Remux") { - // check expected audio codecs (1) + // Check expected audio codecs (1) Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); Assert.Single(val.AudioCodecs); } - // video details + // Video details var videoStream = targetVideoStream; Assert.False(val.EstimateContentLength); Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo); @@ -437,10 +444,10 @@ namespace Jellyfin.Model.Tests Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth); Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue); - // audio codec not supported + // Audio codec not supported if ((why & TranscodeReason.AudioCodecNotSupported) != 0) { - // audio stream specified + // Audio stream specified if (options.AudioStreamIndex >= 0) { // TODO:fixme @@ -450,10 +457,10 @@ namespace Jellyfin.Model.Tests } } - // audio stream not specified + // Audio stream not specified else { - // TODO:fixme + // TODO: Fixme Assert.All(audioStreams, stream => { if (!stream.IsExternal) diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json index 719f553ce89..53637b79310 100644 --- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json +++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json @@ -45,8 +45,8 @@ }, { "Container": "wmv", - "AudioCodec": "", - "VideoCodec": "", + "AudioCodec": "wma", + "VideoCodec": "wmv,vc1", "Type": "Video", "$type": "DirectPlayProfile" }, @@ -59,8 +59,8 @@ }, { "Container": "asf", - "AudioCodec": "", - "VideoCodec": "", + "AudioCodec": "wma", + "VideoCodec": "wmv,vc1", "Type": "Video", "$type": "DirectPlayProfile" }, diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json index 79b1f4fdb1b..d3ef22c2561 100644 --- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json +++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json @@ -45,8 +45,8 @@ }, { "Container": "wmv", - "AudioCodec": "", - "VideoCodec": "", + "AudioCodec": "wma", + "VideoCodec": "wmv,vc1", "Type": "Video", "$type": "DirectPlayProfile" }, @@ -59,8 +59,8 @@ }, { "Container": "asf", - "AudioCodec": "", - "VideoCodec": "", + "AudioCodec": "wma", + "VideoCodec": "wmv,vc1", "Type": "Video", "$type": "DirectPlayProfile" }, From 4b1256e67b7d984ac7737f65b2cceab9a8cb0d96 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Fri, 6 May 2022 02:27:16 +0800 Subject: [PATCH 003/105] Fix the issue that HEVC transcoding can't be disabled --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index ee9cfeeca8c..9702412cb25 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -771,10 +771,17 @@ namespace MediaBrowser.Model.Dlna private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable candidateAudioStreams, string container, string videoCodec, string audioCodec) { - // prefer matching video codecs var videoCodecs = ContainerProfile.SplitValue(videoCodec); var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null; - playlistItem.VideoCodecs = directVideoCodec != null ? new[] { directVideoCodec } : videoCodecs; + if (directVideoCodec != null) + { + // merge directVideoCodec to videoCodecs + videoCodecs = videoCodecs != null && videoCodecs.Length > 0 + ? videoCodecs.Union(new[] { directVideoCodec }).ToArray() + : new[] { directVideoCodec }; + } + + playlistItem.VideoCodecs = videoCodecs; // copy video codec options as a starting point, this applies to transcode and direct-stream playlistItem.MaxFramerate = videoStream?.AverageFrameRate; From 5386f060957f3af11b2520c35d454fa78daab993 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 9 May 2022 17:09:03 +0800 Subject: [PATCH 004/105] Apply suggestions from code review Co-authored-by: Cody Robibero --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 9702412cb25..d49b92cd377 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -776,9 +776,8 @@ namespace MediaBrowser.Model.Dlna if (directVideoCodec != null) { // merge directVideoCodec to videoCodecs - videoCodecs = videoCodecs != null && videoCodecs.Length > 0 - ? videoCodecs.Union(new[] { directVideoCodec }).ToArray() - : new[] { directVideoCodec }; + Array.Resize(ref videoCodecs, (videoCodecs?.Length ?? 0) + 1); + videoCodecs[^1] = directVideoCodec; } playlistItem.VideoCodecs = videoCodecs; From e67d8ce077a2400318e5532e04b63cbf8925a39c Mon Sep 17 00:00:00 2001 From: Jacob Casper Date: Sun, 8 May 2022 18:04:19 -0500 Subject: [PATCH 005/105] Don't let permission denied kill library scans I have a single `./Movies` directory that contains symlinks to many other directories, some of which are in shared paths. Other daemons sometimes overwrite permissions on these paths and prevent Jellyfin from being able to scan. Because this exception is unhandled within a LINQ statement it can randomly kill the metadata refresh for my entire Movies library. Running with this patch has allowed me to at least add + scan for movies in directories that Jellyfin can currently access and gives me some notification of the symlinks failing due to permissions errors. (cherry picked from commit 193cc8690a60bc2a432df14d256f00371e055c90) --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 399ece7fd0d..120b1812a7f 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -262,6 +262,10 @@ namespace Emby.Server.Implementations.IO _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); + } } } From 9523a1682b2d9b2b2e30833f85777c7e96ca76a3 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Wed, 11 May 2022 16:38:30 +0800 Subject: [PATCH 006/105] Apply suggestions from code review Co-authored-by: Claus Vium --- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index d49b92cd377..56d1ebf5472 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -776,7 +776,7 @@ namespace MediaBrowser.Model.Dlna if (directVideoCodec != null) { // merge directVideoCodec to videoCodecs - Array.Resize(ref videoCodecs, (videoCodecs?.Length ?? 0) + 1); + Array.Resize(ref videoCodecs, videoCodecs.Length + 1); videoCodecs[^1] = directVideoCodec; } From 368d10d042e89d47dd493c77c33f8b5bff020918 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 11 May 2022 19:18:31 +0800 Subject: [PATCH 007/105] Fix the mismatched resolution in sw PGS burn-in --- .../MediaEncoding/EncodingHelper.cs | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 54b41a6d25b..bffd3a1ca67 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2521,7 +2521,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - "scale=trunc(min(max(iw\\,ih*dar)\\,min({0}\\,{1}*dar))/{2})*{2}:trunc(min(max(iw/dar\\,ih)\\,min({0}/dar\\,{1}))/2)*2", + "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2", maxWidthParam, maxHeightParam, scaleVal); @@ -2565,7 +2565,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - "scale=trunc(min(max(iw\\,ih*dar)\\,{0})/{1})*{1}:trunc(ow/dar/2)*2", + "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2", maxWidthParam, scaleVal); } @@ -2577,7 +2577,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, - "scale=trunc(oh*a/{1})*{1}:min(max(iw/dar\\,ih)\\,{0})", + "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})", maxHeightParam, scaleVal); } @@ -2626,7 +2626,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else { - filter = "scale={0}:trunc({0}/dar/2)*2"; + filter = "scale={0}:trunc({0}/a/2)*2"; } } @@ -2780,8 +2780,8 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (hasGraphicalSubs) { - // [0:s]scale=s=1280x720 - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + // [0:s]scale=expr + var subSwScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); } @@ -2967,7 +2967,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); } @@ -3165,7 +3167,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); } @@ -3411,7 +3415,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=endall:shortest=1:repeatlast=0"); } @@ -3620,7 +3626,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); } @@ -3867,7 +3875,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); @@ -4042,7 +4052,9 @@ namespace MediaBrowser.Controller.MediaEncoding { if (hasGraphicalSubs) { - var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); + var subSwScaleFilter = isSwDecoder + ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH) + : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH); subFilters.Add(subSwScaleFilter); overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0"); From be832e82ccbebf821cda03a9059d5969fd338a84 Mon Sep 17 00:00:00 2001 From: Luke Brown Date: Wed, 11 May 2022 22:22:52 -0500 Subject: [PATCH 008/105] change capture groups to take up to 4 digits and adding inline data to test --- Emby.Naming/Common/NamingOptions.cs | 2 +- tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 71962f229c6..e016d7e51f7 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -314,7 +314,7 @@ namespace Emby.Naming.Common // This isn't a Kodi naming rule, but the expression below causes false positives, // so we make sure this one gets tested first. // "Foo Bar 889" - new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?[\w\s]+?)\s(?[0-9]{1,3})(-(?[0-9]{2,3}))*[^\\\/x]*$") + new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?[\w\s]+?)\s(?[0-9]{1,4})(-(?[0-9]{2,4}))*[^\\\/x]*$") { IsNamed = true }, diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 1e7fedb36fa..68059f98069 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -1,4 +1,4 @@ -using Emby.Naming.Common; +using Emby.Naming.Common; using Emby.Naming.TV; using Xunit; @@ -9,6 +9,7 @@ namespace Jellyfin.Naming.Tests.TV private readonly NamingOptions _namingOptions = new NamingOptions(); [Theory] + [InlineData("Season 21/One Piece 1001", 1001)] [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", 3)] [InlineData("The Daily Show/The Daily Show 25x22 - [WEBDL-720p][AAC 2.0][x264] Noah Baumbach-TBS.mkv", 22)] [InlineData("Castle Rock 2x01 Que el rio siga su curso [WEB-DL HULU 1080p h264 Dual DD5.1 Subs].mkv", 1)] From 7aa0db24d81af95ab696ccb1011f98df225a39c2 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 15 May 2022 14:53:40 +0200 Subject: [PATCH 009/105] fix: disable "Automatically add to collection" by default --- MediaBrowser.Model/Configuration/LibraryOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index ad3bce86eac..c4d313bdba8 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration RequirePerfectSubtitleMatch = true; AllowEmbeddedSubtitles = EmbeddedSubtitleOptions.AllowAll; - AutomaticallyAddToCollection = true; + AutomaticallyAddToCollection = false; EnablePhotos = true; SaveSubtitlesWithMedia = true; EnableRealtimeMonitor = true; From de3c68d47420f55869192565fea3969859bfac4d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 15 May 2022 20:16:25 -0400 Subject: [PATCH 010/105] Bump version to 10.8.0-beta3 --- debian/changelog | 6 ++++++ debian/metapackage/jellyfin | 2 +- fedora/jellyfin.spec | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 78da3a2b281..9c608e2baa5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.8.0~beta3) unstable; urgency=medium + + * New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3 + + -- Jellyfin Packaging Team Sun, 15 May 2022 20:15:43 -0400 + jellyfin-server (10.8.0~beta2) unstable; urgency=medium * New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin index 9096d8b59d2..eaab8fc9a1b 100644 --- a/debian/metapackage/jellyfin +++ b/debian/metapackage/jellyfin @@ -5,7 +5,7 @@ Homepage: https://jellyfin.org Standards-Version: 3.9.2 Package: jellyfin -Version: 10.8.0~beta2 +Version: 10.8.0~beta3 Maintainer: Jellyfin Packaging Team Depends: jellyfin-server, jellyfin-web Description: Provides the Jellyfin Free Software Media System diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 439943b44ef..1a5c1b29730 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.0~beta2 +Version: 10.8.0~beta3 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv3 @@ -153,6 +153,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Sun May 15 2022 Jellyfin Packaging Team +- New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3 * Sun Apr 17 2022 Jellyfin Packaging Team - New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 * Fri Mar 25 2022 Jellyfin Packaging Team From a82e378da9514d2ac04ea38b3191160f38f4f020 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 16 May 2022 14:14:26 -0600 Subject: [PATCH 011/105] Update to dotnet 6.0.5 --- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6ba95008005..3418e7697cd 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 5e99b9c32a8..3add8f4fff8 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 94c9c3000d4..8057c25dae9 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index fda603c1fa5..e32a2b9336d 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 8853c929520..59661d13bcc 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -40,7 +40,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 837f416d863..2d574fa74cc 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 4a4e204269d..4308d86b46c 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 383aace0693..7143a39f3e5 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 1864bd1a593..625b0791ef1 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 995b71010ca..c457aa0bbe7 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/9d8c7137-2091-4fc6-a419-60ba59c8b9de/db0c5cda94f31d2260d369123de32d59/dotnet-sdk-6.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet 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 cd40c30af3f..291722db91c 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 6b79e48d76e..6cd4361807f 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From a532a866e387d82b996811d4cc3808a021f759ec Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 19 May 2022 17:50:29 -0600 Subject: [PATCH 012/105] Populate authentication info with server details if using API key --- .../Security/AuthorizationContext.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index d59d36e88e6..9f813f532ce 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; +using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Http; @@ -16,11 +17,16 @@ namespace Jellyfin.Server.Implementations.Security { private readonly JellyfinDbProvider _jellyfinDbProvider; private readonly IUserManager _userManager; + private readonly IServerApplicationHost _serverApplicationHost; - public AuthorizationContext(JellyfinDbProvider jellyfinDb, IUserManager userManager) + public AuthorizationContext( + JellyfinDbProvider jellyfinDb, + IUserManager userManager, + IServerApplicationHost serverApplicationHost) { _jellyfinDbProvider = jellyfinDb; _userManager = userManager; + _serverApplicationHost = serverApplicationHost; } public Task GetAuthorizationInfo(HttpContext requestContext) @@ -187,17 +193,17 @@ namespace Jellyfin.Server.Implementations.Security authInfo.Token = key.AccessToken; if (string.IsNullOrWhiteSpace(authInfo.DeviceId)) { - authInfo.DeviceId = string.Empty; + authInfo.DeviceId = _serverApplicationHost.SystemId; } if (string.IsNullOrWhiteSpace(authInfo.Device)) { - authInfo.Device = string.Empty; + authInfo.Device = _serverApplicationHost.Name; } if (string.IsNullOrWhiteSpace(authInfo.Version)) { - authInfo.Version = string.Empty; + authInfo.Version = _serverApplicationHost.ApplicationVersionString; } authInfo.IsApiKey = true; From 760b021032f82779f3ec352a00487175bb1e361e Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 20 May 2022 16:29:43 -0600 Subject: [PATCH 013/105] Manually describe Version for openapi --- .../Extensions/ApiServiceCollectionExtensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 3df8481fd4a..66fa3bc31bd 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -440,6 +440,12 @@ namespace Jellyfin.Server.Extensions .Cast() .ToArray() }); + + // Swashbuckle doesn't use JsonOptions to describe responses, so we need to manually describe it. + options.MapType(() => new OpenApiSchema + { + Type = "string" + }); } } } From 84878f537cfa12149b8f4d308446e165fae78d5b Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sat, 21 May 2022 13:11:26 +0200 Subject: [PATCH 014/105] Support searching with tv program filters --- Jellyfin.Api/Controllers/ItemsController.cs | 72 +++++++++++++------ .../Controllers/TrailersController.cs | 15 ++++ 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 2794a06f308..58caae9f853 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -89,6 +89,11 @@ namespace Jellyfin.Api.Controllers /// Optional filter by items that have an imdb id or not. /// Optional filter by items that have a tmdb id or not. /// Optional filter by items that have a tvdb id or not. + /// Optional filter for live tv movies. + /// Optional filter for live tv series. + /// Optional filter for live tv news. + /// Optional filter for live tv kids. + /// Optional filter for live tv sports. /// Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited. /// Optional. The record index to start at. All items with a lower index will be dropped from the results. /// Optional. The maximum number of records to return. @@ -173,6 +178,11 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasImdbId, [FromQuery] bool? hasTmdbId, [FromQuery] bool? hasTvdbId, + [FromQuery] bool? isMovie, + [FromQuery] bool? isSeries, + [FromQuery] bool? isNews, + [FromQuery] bool? isKids, + [FromQuery] bool? isSports, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -316,6 +326,11 @@ namespace Jellyfin.Api.Controllers Is3D = is3D, HasTvdbId = hasTvdbId, HasTmdbId = hasTmdbId, + IsMovie = isMovie, + IsSeries = isSeries, + IsNews = isNews, + IsKids = isKids, + IsSports = isSports, HasOverview = hasOverview, HasOfficialRating = hasOfficialRating, HasParentalRating = hasParentalRating, @@ -515,8 +530,8 @@ namespace Jellyfin.Api.Controllers /// Optional filter by items that have or do not have a parental rating. /// Optional filter by items that are HD or not. /// Optional filter by items that are 4K or not. - /// Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted. - /// Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimeted. + /// Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimited. + /// Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimited. /// Optional filter by items that are missing episodes or not. /// Optional filter by items that are unaired episodes or not. /// Optional filter by minimum community rating. @@ -529,42 +544,47 @@ namespace Jellyfin.Api.Controllers /// Optional filter by items that have an imdb id or not. /// Optional filter by items that have a tmdb id or not. /// Optional filter by items that have a tvdb id or not. - /// Optional. If specified, results will be filtered by exxcluding item ids. This allows multiple, comma delimeted. + /// Optional filter for live tv movies. + /// Optional filter for live tv series. + /// Optional filter for live tv news. + /// Optional filter for live tv kids. + /// Optional filter for live tv sports. + /// Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited. /// Optional. The record index to start at. All items with a lower index will be dropped from the results. /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. /// Sort Order - Ascending,Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. - /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. - /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted. - /// Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimeted. - /// Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes. + /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. + /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. + /// Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited. + /// Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes. /// Optional filter by items that are marked as favorite, or not. /// Optional filter by MediaType. Allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited. - /// Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime. + /// Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime. /// Optional filter by items that are played, or not. - /// Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted. + /// Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimited. /// Optional, include user data. /// Optional, the max number of images to return, per image type. /// Optional. The image types to include in the output. /// Optional. If specified, results will be filtered to include only those containing the specified person. /// Optional. If specified, results will be filtered to include only those containing the specified person id. /// Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited. - /// Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimeted. + /// Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered to include only those containing the specified artist id. /// Optional. If specified, results will be filtered to include only those containing the specified album artist id. /// Optional. If specified, results will be filtered to include only those containing the specified contributing artist id. - /// Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimeted. + /// Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimited. /// Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited. - /// Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted. + /// Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimited. /// Optional filter by minimum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items that are locked. /// Optional filter by items that are placeholders. @@ -575,12 +595,12 @@ namespace Jellyfin.Api.Controllers /// Optional. Filter by the maximum width of the item. /// Optional. Filter by the maximum height of the item. /// Optional filter by items that are 3D, or not. - /// Optional filter by Series Status. Allows multiple, comma delimeted. + /// Optional filter by Series Status. Allows multiple, comma delimited. /// Optional filter by items whose name is sorted equally or greater than a given input string. /// Optional filter by items whose name is sorted equally than a given input string. /// Optional filter by items whose name is equally or lesser than a given input string. - /// Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimeted. - /// Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimeted. + /// Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimited. + /// Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimited. /// Optional. Enable the total record count. /// Optional, include image information in output. /// A with the items. @@ -613,6 +633,11 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasImdbId, [FromQuery] bool? hasTmdbId, [FromQuery] bool? hasTvdbId, + [FromQuery] bool? isMovie, + [FromQuery] bool? isSeries, + [FromQuery] bool? isNews, + [FromQuery] bool? isKids, + [FromQuery] bool? isSports, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -695,6 +720,11 @@ namespace Jellyfin.Api.Controllers hasImdbId, hasTmdbId, hasTvdbId, + isMovie, + isSeries, + isNews, + isKids, + isSports, excludeItemIds, startIndex, limit, diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 5cb7468b24c..790d6e64d87 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -57,6 +57,11 @@ namespace Jellyfin.Api.Controllers /// Optional filter by items that have an imdb id or not. /// Optional filter by items that have a tmdb id or not. /// Optional filter by items that have a tvdb id or not. + /// Optional filter for live tv movies. + /// Optional filter for live tv series. + /// Optional filter for live tv news. + /// Optional filter for live tv kids. + /// Optional filter for live tv sports. /// Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited. /// Optional. The record index to start at. All items with a lower index will be dropped from the results. /// Optional. The maximum number of records to return. @@ -140,6 +145,11 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasImdbId, [FromQuery] bool? hasTmdbId, [FromQuery] bool? hasTvdbId, + [FromQuery] bool? isMovie, + [FromQuery] bool? isSeries, + [FromQuery] bool? isNews, + [FromQuery] bool? isKids, + [FromQuery] bool? isSports, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -224,6 +234,11 @@ namespace Jellyfin.Api.Controllers hasImdbId, hasTmdbId, hasTvdbId, + isMovie, + isSeries, + isNews, + isKids, + isSports, excludeItemIds, startIndex, limit, From d29a4234750a03dec61fef5b1b34dfa63fdc003a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 22 May 2022 10:59:41 -0600 Subject: [PATCH 015/105] Enable SupportsTranscoding if device has transcoding profiles --- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 31b97983653..e5e36886763 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -255,10 +255,27 @@ namespace Jellyfin.Api.Helpers streamInfo.PlaySessionId = playSessionId; streamInfo.StartPositionTicks = startTimeTicks; - mediaSource.SupportsDirectPlay = streamInfo.PlayMethod == PlayMethod.DirectPlay; + if (mediaSource.SupportsDirectPlay) + { + mediaSource.SupportsDirectPlay = streamInfo.PlayMethod == PlayMethod.DirectPlay; + } + // Players do not handle this being set according to PlayMethod - mediaSource.SupportsDirectStream = options.EnableDirectStream ? streamInfo.PlayMethod == PlayMethod.DirectPlay || streamInfo.PlayMethod == PlayMethod.DirectStream : streamInfo.PlayMethod == PlayMethod.DirectPlay; - mediaSource.SupportsTranscoding = streamInfo.PlayMethod == PlayMethod.DirectStream || mediaSource.TranscodingContainer != null; + if (mediaSource.SupportsDirectStream) + { + mediaSource.SupportsDirectStream = + options.EnableDirectStream + ? streamInfo.PlayMethod == PlayMethod.DirectPlay || streamInfo.PlayMethod == PlayMethod.DirectStream + : streamInfo.PlayMethod == PlayMethod.DirectPlay; + } + + if (mediaSource.SupportsTranscoding) + { + mediaSource.SupportsTranscoding = + streamInfo.PlayMethod == PlayMethod.DirectStream + || mediaSource.TranscodingContainer != null + || profile.TranscodingProfiles.Any(i => i.Type == streamInfo.MediaType && i.Context == options.Context); + } if (item is Audio) { @@ -290,7 +307,7 @@ namespace Jellyfin.Api.Helpers } else { - if (mediaSource.SupportsTranscoding || mediaSource.SupportsDirectStream) + if (!mediaSource.SupportsDirectPlay && (mediaSource.SupportsTranscoding || mediaSource.SupportsDirectStream)) { streamInfo.PlayMethod = PlayMethod.Transcode; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); From ff4f624850bc7d5024feaee93ffe52c07c27d172 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Sun, 22 May 2022 22:14:57 +0300 Subject: [PATCH 016/105] Clear TranscodingInfo if play method changed --- Emby.Server.Implementations/Session/SessionManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e2fa93a3808..d08fedd111f 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -770,6 +770,11 @@ namespace Emby.Server.Implementations.Session await UpdateNowPlayingItem(session, info, libraryItem, !isAutomated).ConfigureAwait(false); + if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode) + { + ClearTranscodingInfo(session.DeviceId); + } + var users = GetUsers(session); // only update saved user data on actual check-ins, not automated ones From c5dae180341ebb38720151deab208b98c2142a38 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Mon, 23 May 2022 07:49:54 -0600 Subject: [PATCH 017/105] Apply suggestions from review --- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 27 +++++++++---------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index e5e36886763..5c05c57a619 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -255,27 +255,18 @@ namespace Jellyfin.Api.Helpers streamInfo.PlaySessionId = playSessionId; streamInfo.StartPositionTicks = startTimeTicks; - if (mediaSource.SupportsDirectPlay) - { - mediaSource.SupportsDirectPlay = streamInfo.PlayMethod == PlayMethod.DirectPlay; - } + mediaSource.SupportsDirectPlay = streamInfo.PlayMethod == PlayMethod.DirectPlay; // Players do not handle this being set according to PlayMethod - if (mediaSource.SupportsDirectStream) - { - mediaSource.SupportsDirectStream = - options.EnableDirectStream - ? streamInfo.PlayMethod == PlayMethod.DirectPlay || streamInfo.PlayMethod == PlayMethod.DirectStream - : streamInfo.PlayMethod == PlayMethod.DirectPlay; - } + mediaSource.SupportsDirectStream = + options.EnableDirectStream + ? streamInfo.PlayMethod == PlayMethod.DirectPlay || streamInfo.PlayMethod == PlayMethod.DirectStream + : streamInfo.PlayMethod == PlayMethod.DirectPlay; - if (mediaSource.SupportsTranscoding) - { - mediaSource.SupportsTranscoding = - streamInfo.PlayMethod == PlayMethod.DirectStream - || mediaSource.TranscodingContainer != null - || profile.TranscodingProfiles.Any(i => i.Type == streamInfo.MediaType && i.Context == options.Context); - } + mediaSource.SupportsTranscoding = + streamInfo.PlayMethod == PlayMethod.DirectStream + || mediaSource.TranscodingContainer != null + || profile.TranscodingProfiles.Any(i => i.Type == streamInfo.MediaType && i.Context == options.Context); if (item is Audio) { From 3c5b4b9a27489ebe118b87b892f7c72ed73482eb Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 24 May 2022 08:12:36 -0600 Subject: [PATCH 018/105] Conditionally include platform specific Skia assets --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 2188c0d9914..ae78ce95a45 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -18,8 +18,8 @@ - - + + From 1c5571b24e7d1315d395686b37398cef2a34afbe Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 26 May 2022 08:32:37 -0600 Subject: [PATCH 019/105] Remove conditional skia inclusion (#7799) --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index ae78ce95a45..814253e0f0d 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -19,7 +19,7 @@ - + From 293bcfb3425108922b476113cb67c7e1a21d11f2 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 27 May 2022 03:05:08 +0800 Subject: [PATCH 020/105] Exclude streams with mismatched types in external files --- .../MediaInfo/MediaInfoResolver.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index 1421d0183d3..6d9aac2c0fe 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -106,19 +106,28 @@ namespace MediaBrowser.Providers.MediaInfo if (mediaInfo.MediaStreams.Count == 1) { MediaStream mediaStream = mediaInfo.MediaStreams[0]; - mediaStream.Index = startIndex++; - mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; - mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; - mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); + if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio) + || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle)) + { + mediaStream.Index = startIndex++; + mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; + mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; + + mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); + } } else { foreach (MediaStream mediaStream in mediaInfo.MediaStreams) { - mediaStream.Index = startIndex++; + if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio) + || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle)) + { + mediaStream.Index = startIndex++; - mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); + mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); + } } } } @@ -222,13 +231,6 @@ namespace MediaBrowser.Providers.MediaInfo mediaStream.Title = string.IsNullOrEmpty(mediaStream.Title) ? (string.IsNullOrEmpty(pathInfo.Title) ? null : pathInfo.Title) : mediaStream.Title; mediaStream.Language = string.IsNullOrEmpty(mediaStream.Language) ? (string.IsNullOrEmpty(pathInfo.Language) ? null : pathInfo.Language) : mediaStream.Language; - mediaStream.Type = _type switch - { - DlnaProfileType.Audio => MediaStreamType.Audio, - DlnaProfileType.Subtitle => MediaStreamType.Subtitle, - _ => mediaStream.Type - }; - return mediaStream; } } From b369194710f01dd9d483463492098fd4b02d6cac Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Fri, 27 May 2022 06:11:11 +0800 Subject: [PATCH 021/105] Fix tests --- .../Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs | 3 +++ .../MediaInfo/MediaInfoResolverTests.cs | 5 ++++- .../MediaInfo/SubtitleResolverTests.cs | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs index aec523882d0..92968920944 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs @@ -43,6 +43,9 @@ public class AudioResolverTests MediaStreams = new List { new() + { + Type = MediaStreamType.Audio + } } })); diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs index 98b4a6ccf67..7c3027f9496 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs @@ -359,7 +359,10 @@ public class MediaInfoResolverTests var mediaStreams = new List(); for (int i = 0; i < streamCount; i++) { - mediaStreams.Add(new()); + mediaStreams.Add(new() + { + Type = MediaStreamType.Subtitle + }); } return mediaStreams; diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs index 0e6457ce37a..6de6d296e52 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs @@ -43,6 +43,9 @@ public class SubtitleResolverTests MediaStreams = new List { new() + { + Type = MediaStreamType.Subtitle + } } })); From 8a6b26cd424adb9537e14fd5ef833de9dd7dd4de Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 27 May 2022 15:57:51 -0600 Subject: [PATCH 022/105] initial patch --- Emby.Server.Implementations/Session/SessionManager.cs | 1 + MediaBrowser.Model/Session/PlayerStateInfo.cs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index d08fedd111f..8c8d088b6ab 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -413,6 +413,7 @@ namespace Emby.Server.Implementations.Session session.PlayState.IsPaused = info.IsPaused; session.PlayState.PositionTicks = info.PositionTicks; session.PlayState.MediaSourceId = info.MediaSourceId; + session.PlayState.LiveStreamId = info.LiveStreamId; session.PlayState.CanSeek = info.CanSeek; session.PlayState.IsMuted = info.IsMuted; session.PlayState.VolumeLevel = info.VolumeLevel; diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs index 0f10605ea1d..80e6d4e0b05 100644 --- a/MediaBrowser.Model/Session/PlayerStateInfo.cs +++ b/MediaBrowser.Model/Session/PlayerStateInfo.cs @@ -64,5 +64,11 @@ namespace MediaBrowser.Model.Session /// /// The repeat mode. public RepeatMode RepeatMode { get; set; } + + /// + /// Gets or sets the now playing live stream identifier. + /// + /// The live stream identifier. + public string LiveStreamId { get; set; } } } From bf0a7c374c9277bcf71e000c68e22a9c24c41744 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 27 May 2022 15:58:31 -0600 Subject: [PATCH 023/105] Close live stream on session end --- Emby.Server.Implementations/Session/SessionManager.cs | 6 +++++- Emby.Server.Implementations/Session/WebSocketController.cs | 4 ++-- MediaBrowser.Controller/Session/ISessionManager.cs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 8c8d088b6ab..eaf2c25dafc 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -329,13 +329,17 @@ namespace Emby.Server.Implementations.Session } /// - public void CloseIfNeeded(SessionInfo session) + public async Task CloseIfNeededAsync(SessionInfo session) { if (!session.SessionControllers.Any(i => i.IsSessionActive)) { var key = GetSessionKey(session.Client, session.DeviceId); _activeConnections.TryRemove(key, out _); + if (!string.IsNullOrEmpty(session.PlayState?.LiveStreamId)) + { + await _mediaSourceManager.CloseLiveStream(session.PlayState?.LiveStreamId).ConfigureAwait(false); + } OnSessionEnded(session); } diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index 9fa92a53a15..d21b6a929c1 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -53,13 +53,13 @@ namespace Emby.Server.Implementations.Session connection.Closed += OnConnectionClosed; } - private void OnConnectionClosed(object? sender, EventArgs e) + private async void OnConnectionClosed(object? sender, EventArgs e) { var connection = sender as IWebSocketConnection ?? throw new ArgumentException($"{nameof(sender)} is not of type {nameof(IWebSocketConnection)}", nameof(sender)); _logger.LogDebug("Removing websocket from session {Session}", _session.Id); _sockets.Remove(connection); connection.Closed -= OnConnectionClosed; - _sessionManager.CloseIfNeeded(_session); + await _sessionManager.CloseIfNeededAsync(_session).ConfigureAwait(false); } /// diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index c8655609583..b16399598cc 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -352,6 +352,6 @@ namespace MediaBrowser.Controller.Session /// Task. Task RevokeUserTokens(Guid userId, string currentAccessToken); - void CloseIfNeeded(SessionInfo session); + Task CloseIfNeededAsync(SessionInfo session); } } From d8f1a87c85162a7070a29d9cb95df1e6d115e7bd Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 27 May 2022 16:25:31 -0600 Subject: [PATCH 024/105] Update Emby.Server.Implementations/Session/SessionManager.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index eaf2c25dafc..d25376297fd 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -338,7 +338,7 @@ namespace Emby.Server.Implementations.Session _activeConnections.TryRemove(key, out _); if (!string.IsNullOrEmpty(session.PlayState?.LiveStreamId)) { - await _mediaSourceManager.CloseLiveStream(session.PlayState?.LiveStreamId).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(session.PlayState.LiveStreamId).ConfigureAwait(false); } OnSessionEnded(session); From 3721b5e985e7d0642c6963902c994e9830f9ce01 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 3 Jun 2022 18:53:05 -0600 Subject: [PATCH 025/105] Backport all dependency updates --- .../Emby.Server.Implementations.csproj | 6 +++--- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 4 ++-- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 9 ++++++--- .../Jellyfin.Common.Tests.csproj | 9 ++++++--- .../Jellyfin.Controller.Tests.csproj | 9 ++++++--- tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 9 ++++++--- .../Jellyfin.Extensions.Tests.csproj | 6 +++--- .../Jellyfin.MediaEncoding.Hls.Tests.csproj | 4 ++-- .../Jellyfin.MediaEncoding.Keyframes.Tests.csproj | 4 ++-- .../Jellyfin.MediaEncoding.Tests.csproj | 9 ++++++--- .../Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 11 +++++++---- .../Jellyfin.Naming.Tests.csproj | 9 ++++++--- .../Jellyfin.Networking.Tests.csproj | 11 +++++++---- .../Jellyfin.Providers.Tests.csproj | 6 +++--- .../Jellyfin.Server.Implementations.Tests.csproj | 9 ++++++--- .../Jellyfin.Server.Integration.Tests.csproj | 9 ++++++--- .../Jellyfin.Server.Tests.csproj | 9 ++++++--- .../Jellyfin.XbmcMetadata.Tests.csproj | 9 ++++++--- 21 files changed, 93 insertions(+), 57 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 3418e7697cd..43d1c387858 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -30,9 +30,9 @@ - - - + + + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 3add8f4fff8..f6f370fbf33 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -20,7 +20,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index e32a2b9336d..f1bfaec7fa6 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -34,7 +34,7 @@ - + @@ -48,7 +48,7 @@ - + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 59661d13bcc..8637fcc0711 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -35,7 +35,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 8d242d13d12..b2824db110b 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 0da00bb0afb..f9e9a0ff382 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -17,11 +17,14 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index ee51562b40d..c56a3ad9efe 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -12,11 +12,14 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index d2087b0230d..5942891784c 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -12,10 +12,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index c8c526f4d12..9377d595f33 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -7,10 +7,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj index bd8667a8479..e4361e1bf45 100644 --- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -7,9 +7,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -17,7 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj index cc36dc4fa04..16aeac5fb04 100644 --- a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj @@ -7,9 +7,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj index de1fcab59b2..8235b07756b 100644 --- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj @@ -8,9 +8,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index e186a488a90..88d45e255b0 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -22,10 +22,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index 5fdb22546e2..b538ad542ee 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -7,12 +7,15 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 69ba5d86a6b..626ba863be4 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -12,10 +12,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index ba39cc0ff5e..a8db7753d23 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -12,12 +12,15 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - + + diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj index 6d52a3cd6b0..0fe0ee140f6 100644 --- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj +++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj @@ -13,10 +13,10 @@ - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 2d0a7c0313d..76067cdb2a9 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -21,10 +21,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + 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 291722db91c..e8b336c09b8 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -11,12 +11,15 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 6cd4361807f..f5226c5cae2 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -12,11 +12,14 @@ - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index d8bc4d59608..dc7949c81ed 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -13,10 +13,13 @@ - - + + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 754bda8f739316130870ed48b2f1ca1a0ac28fbb Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 29 May 2022 01:25:37 +0200 Subject: [PATCH 026/105] IAsyncDisposable is one big pitfall https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#unacceptable-pattern Regex used: ``` await using \(.+\) \W+await using ``` --- Jellyfin.Server/Program.cs | 10 ++++++---- .../Subtitles/SubtitleEncoder.cs | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 1323d8a4809..2bda8d29057 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -545,12 +545,14 @@ namespace Jellyfin.Server const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; Stream resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath) ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'"); - Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); await using (resource.ConfigureAwait(false)) - await using (dst.ConfigureAwait(false)) { - // Copy the resource contents to the expected file path for the config file - await resource.CopyToAsync(dst).ConfigureAwait(false); + Stream dst = new FileStream(configPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); + await using (dst.ConfigureAwait(false)) + { + // Copy the resource contents to the expected file path for the config file + await resource.CopyToAsync(dst).ConfigureAwait(false); + } } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index f6b7efb1e9c..49bc2d775bb 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -681,11 +681,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (!string.Equals(text, newText, StringComparison.Ordinal)) { var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - var writer = new StreamWriter(fileStream, encoding); await using (fileStream.ConfigureAwait(false)) - await using (writer.ConfigureAwait(false)) { - await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false); + var writer = new StreamWriter(fileStream, encoding); + await using (writer.ConfigureAwait(false)) + { + await writer.WriteAsync(newText.AsMemory(), cancellationToken).ConfigureAwait(false); + } } } } From 2cc896251f03d0ba61933584bfc8cdee44107c96 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 6 Jun 2022 19:47:50 +0800 Subject: [PATCH 027/105] Fix the PNG image decoding issue in Skia Regression was introduced in 2.88.0: https://github.com/mono/SkiaSharp/issues/2095 --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 814253e0f0d..5d645bddfe1 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -18,8 +18,8 @@ - - + + From 4f0666ac5cfc57c333970cc97223d2df3b5e3e94 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 6 Jun 2022 15:41:01 +0200 Subject: [PATCH 028/105] chore: enable on demand keyframe extraction for mkv --- MediaBrowser.Model/Configuration/EncodingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 06931ac3b5a..fce0b6d6f65 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.Model.Configuration EnableHardwareEncoding = true; AllowHevcEncoding = false; EnableSubtitleExtraction = true; - AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = Array.Empty(); + AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" }; HardwareDecodingCodecs = new string[] { "h264", "vc1" }; } From 910995f9224ff633968b892ea03d8be578ce46f1 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 1 Jun 2022 03:17:09 +0800 Subject: [PATCH 029/105] Fix Dolby Vision profile 5 and 8 to SDR HW tone-mapping --- .../MediaEncoding/EncodingHelper.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index bffd3a1ca67..ed5ebc8a3d0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -144,15 +144,28 @@ namespace MediaBrowser.Controller.MediaEncoding private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options) { - if (state.VideoStream == null) + if (state.VideoStream == null + || !options.EnableTonemapping + || GetVideoColorBitDepth(state) != 10) { return false; } - return options.EnableTonemapping - && (string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.VideoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) - && GetVideoColorBitDepth(state) == 10; + if (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)) + { + // Only native SW decoder and HW accelerator can parse dovi rpu. + var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty; + var isSwDecoder = string.IsNullOrEmpty(vidDecoder); + var isNvdecDecoder = vidDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase); + var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase); + var isD3d11vaDecoder = vidDecoder.Contains("d3d11va", StringComparison.OrdinalIgnoreCase); + return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder; + } + + return string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.VideoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase); } private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options) From fb95fb1a733ffbd164de415a95d764ae3f6cab3b Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 1 Jun 2022 16:43:02 +0800 Subject: [PATCH 030/105] Update DoVi 10bit codec tags and remove extra -strict options --- .../Controllers/DynamicHlsController.cs | 21 ++++++++++++++----- .../MediaEncoding/EncodingHelper.cs | 3 +-- MediaBrowser.Model/Entities/MediaStream.cs | 3 +-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 6347b908cae..9173487dd89 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1711,20 +1711,30 @@ namespace Jellyfin.Api.Controllers return audioTranscodeParams; } + // flac and opus are experimental in mp4 muxer + var strictArgs = string.Empty; + + if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) + { + strictArgs = " -strict -2"; + } + if (EncodingHelper.IsCopyCodec(audioCodec)) { var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions); var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container); + var copyArgs = "-codec:a:0 copy" + bitStreamArgs + strictArgs; if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) { - return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0" + bitStreamArgs; + return copyArgs + " -copypriorss:a:0 0"; } - return "-codec:a:0 copy -strict -2" + bitStreamArgs; + return copyArgs; } - var args = "-codec:a:0 " + audioCodec; + var args = "-codec:a:0 " + audioCodec + strictArgs; var channels = state.OutputAudioChannels; @@ -1779,11 +1789,12 @@ namespace Jellyfin.Api.Controllers || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { if (EncodingHelper.IsCopyCodec(codec) - && (string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase) || string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase))) { // Prefer dvh1 to dvhe - args += " -tag:v:0 dvh1"; + args += " -tag:v:0 dvh1 -strict -2"; } else { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ed5ebc8a3d0..ce5a9fe3f23 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -529,8 +529,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(codec, "flac", StringComparison.OrdinalIgnoreCase)) { - // flac is experimental in mp4 muxer - return "flac -strict -2"; + return "flac"; } return codec.ToLowerInvariant(); diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4742d21e9e1..96b48ca52aa 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -121,8 +121,7 @@ namespace MediaBrowser.Model.Entities var codecTag = CodecTag; - if (string.Equals(codecTag, "dva1", StringComparison.OrdinalIgnoreCase) - || string.Equals(codecTag, "dvav", StringComparison.OrdinalIgnoreCase) + if (string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase)) From be28f940b71da1c551a44d5e70f875218617c609 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 6 Jun 2022 21:45:00 +0800 Subject: [PATCH 031/105] Fix the issue that analyzeduration env is not applied --- .../MediaEncoding/EncodingHelper.cs | 30 +++++++++++-------- .../Encoder/MediaEncoder.cs | 10 ++++++- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ce5a9fe3f23..32348c63706 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -13,11 +13,13 @@ using System.Threading; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; +using Microsoft.Extensions.Configuration; namespace MediaBrowser.Controller.MediaEncoding { @@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly IApplicationPaths _appPaths; private readonly IMediaEncoder _mediaEncoder; private readonly ISubtitleEncoder _subtitleEncoder; + private readonly IConfiguration _config; private static readonly string[] _videoProfilesH264 = new[] { @@ -54,11 +57,13 @@ namespace MediaBrowser.Controller.MediaEncoding public EncodingHelper( IApplicationPaths appPaths, IMediaEncoder mediaEncoder, - ISubtitleEncoder subtitleEncoder) + ISubtitleEncoder subtitleEncoder, + IConfiguration config) { _appPaths = appPaths; _mediaEncoder = mediaEncoder; _subtitleEncoder = subtitleEncoder; + _config = config; } public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions) @@ -4877,22 +4882,21 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions, string segmentContainer) { var inputModifier = string.Empty; - var probeSizeArgument = string.Empty; + var analyzeDurationArgument = string.Empty; - string analyzeDurationArgument; - if (state.MediaSource.AnalyzeDurationMs.HasValue) + // Apply -analyzeduration as per the environment variable, + // otherwise ffmpeg will break on certain files due to default value is 0. + // The default value of -probesize is more than enough, so leave it as is. + var ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty; + + if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration)) + { + analyzeDurationArgument = "-analyzeduration " + ffmpegAnalyzeDuration; + } + else if (state.MediaSource.AnalyzeDurationMs.HasValue) { analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture); } - else - { - analyzeDurationArgument = string.Empty; - } - - if (!string.IsNullOrEmpty(probeSizeArgument)) - { - inputModifier += " " + probeSizeArgument; - } if (!string.IsNullOrEmpty(analyzeDurationArgument)) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 1bac4b18752..7233f21b51d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -16,6 +16,7 @@ using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.Model.Dlna; @@ -49,6 +50,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly IServerConfigurationManager _configurationManager; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; + private readonly IConfiguration _config; private readonly string _startupOptionFFmpegPath; private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2); @@ -85,6 +87,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; + _config = config; _startupOptionFFmpegPath = config.GetValue(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty; _jsonSerializerOptions = JsonDefaults.Options; } @@ -371,8 +374,13 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFile = request.MediaSource.Path; string analyzeDuration = string.Empty; + string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty; - if (request.MediaSource.AnalyzeDurationMs > 0) + if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration)) + { + analyzeDuration = "-analyzeduration " + ffmpegAnalyzeDuration; + } + else if (request.MediaSource.AnalyzeDurationMs > 0) { analyzeDuration = "-analyzeduration " + (request.MediaSource.AnalyzeDurationMs * 1000).ToString(); From 84c9e7a22beeaa9c82f3f33810bedf3e4572c24e Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 1 Jun 2022 03:52:16 +0800 Subject: [PATCH 032/105] Fix thumbnail extraction in DoVi --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7233f21b51d..77b97c9b488 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -637,10 +637,15 @@ namespace MediaBrowser.MediaEncoding.Encoder filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24")); } - // Use SW tonemap on HDR video stream only when the zscale filter is available. - var enableHdrExtraction = string.Equals(videoStream?.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) && SupportsFilter("zscale"); - if (enableHdrExtraction) + // Use SW tonemap on HDR10/HLG video stream only when the zscale filter is available. + var enableHdrExtraction = false; + + if ((string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) + && SupportsFilter("zscale")) { + enableHdrExtraction = true; + filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"); } From 0e8da3e805957eb954830936290f7d591f6d09a2 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 1 Jun 2022 04:09:04 +0800 Subject: [PATCH 033/105] Remove the redundant -sc_threshold from hw encoders --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 32348c63706..aaab7954dc7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1239,10 +1239,9 @@ namespace MediaBrowser.Controller.MediaEncoding // Example: we encoded half of desired length, then codec detected // scene cut and inserted a keyframe; next forced keyframe would // be created outside of segment, which breaks seeking. - // -sc_threshold 0 is used to prevent the hardware encoder from post processing to break the set keyframe. gopArg = string.Format( CultureInfo.InvariantCulture, - " -g:v:0 {0} -keyint_min:v:0 {0} -sc_threshold:v:0 0", + " -g:v:0 {0} -keyint_min:v:0 {0}", Math.Ceiling(segmentLength * framerate.Value)); } @@ -1262,6 +1261,12 @@ namespace MediaBrowser.Controller.MediaEncoding || string.Equals(codec, "hevc_vaapi", StringComparison.OrdinalIgnoreCase)) { args += keyFrameArg; + + // prevent the libx264 from post processing to break the set keyframe. + if (string.Equals(codec, "libx264", StringComparison.OrdinalIgnoreCase)) + { + args += " -sc_threshold:v:0 0"; + } } else { From 19000960122a96d68b78556545a9e16af4f91cb9 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Fri, 3 Jun 2022 17:44:22 +0800 Subject: [PATCH 034/105] Fix the too high default qmin option in amf encoders --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index aaab7954dc7..eb344b52877 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1041,7 +1041,8 @@ namespace MediaBrowser.Controller.MediaEncoding if (string.Equals(videoCodec, "h264_amf", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "hevc_amf", StringComparison.OrdinalIgnoreCase)) { - return FormattableString.Invariant($" -qmin 18 -qmax 32 -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); + // Override the too high default qmin 18 in transcoding preset + return FormattableString.Invariant($" -rc cbr -qmin 0 -qmax 32 -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); } if (string.Equals(videoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) From 6b16d90b9b6f1cb52e8c1b4da5ac6504526350ae Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 9 Jun 2022 14:26:05 -0600 Subject: [PATCH 035/105] Don't add `MigrationOptions` to the api spec --- Jellyfin.Server/Filters/AdditionalModelFilter.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Jellyfin.Server/Filters/AdditionalModelFilter.cs b/Jellyfin.Server/Filters/AdditionalModelFilter.cs index 44e5b0d1e7b..487948f8152 100644 --- a/Jellyfin.Server/Filters/AdditionalModelFilter.cs +++ b/Jellyfin.Server/Filters/AdditionalModelFilter.cs @@ -1,3 +1,6 @@ +using System; +using Jellyfin.Extensions; +using Jellyfin.Server.Migrations; using MediaBrowser.Common.Plugins; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.LiveTv; @@ -15,6 +18,8 @@ namespace Jellyfin.Server.Filters /// public class AdditionalModelFilter : IDocumentFilter { + // Array of options that should not be visible in the api spec. + private static readonly Type[] _ignoredConfigurations = { typeof(MigrationOptions) }; private readonly IServerConfigurationManager _serverConfigurationManager; /// @@ -44,6 +49,11 @@ namespace Jellyfin.Server.Filters foreach (var configuration in _serverConfigurationManager.GetConfigurationStores()) { + if (_ignoredConfigurations.IndexOf(configuration.ConfigurationType) != -1) + { + continue; + } + context.SchemaGenerator.GenerateSchema(configuration.ConfigurationType, context.SchemaRepository); } } From b4954985be709c0d534a7cd2849f5340bc8c7780 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 10 Jun 2022 08:56:58 +0200 Subject: [PATCH 036/105] chore: disable DLNA by default --- .../Attributes/DlnaEnabledAttribute.cs | 25 +++++ .../Controllers/DlnaServerController.cs | 91 ++++--------------- 2 files changed, 41 insertions(+), 75 deletions(-) create mode 100644 Jellyfin.Api/Attributes/DlnaEnabledAttribute.cs diff --git a/Jellyfin.Api/Attributes/DlnaEnabledAttribute.cs b/Jellyfin.Api/Attributes/DlnaEnabledAttribute.cs new file mode 100644 index 00000000000..d3a6ac9c81e --- /dev/null +++ b/Jellyfin.Api/Attributes/DlnaEnabledAttribute.cs @@ -0,0 +1,25 @@ +using Emby.Dlna; +using MediaBrowser.Controller.Configuration; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace Jellyfin.Api.Attributes; + +/// +public sealed class DlnaEnabledAttribute : ActionFilterAttribute +{ + /// + public override void OnActionExecuting(ActionExecutingContext context) + { + var serverConfigurationManager = context.HttpContext.RequestServices.GetRequiredService(); + + var enabled = serverConfigurationManager.GetDlnaConfiguration().EnableServer; + + if (!enabled) + { + context.Result = new StatusCodeResult(StatusCodes.Status503ServiceUnavailable); + } + } +} diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index b1c576c3307..401c0197ac6 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -20,6 +20,7 @@ namespace Jellyfin.Api.Controllers /// Dlna Server Controller. /// [Route("Dlna")] + [DlnaEnabled] [Authorize(Policy = Policies.AnonymousLanAccessPolicy)] public class DlnaServerController : BaseJellyfinApiController { @@ -55,15 +56,10 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - var url = GetAbsoluteUri(); - var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); - var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress); - return Ok(xml); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + var url = GetAbsoluteUri(); + var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); + var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress); + return Ok(xml); } /// @@ -83,12 +79,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetContentDirectory([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return Ok(_contentDirectory.GetServiceXml()); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return Ok(_contentDirectory.GetServiceXml()); } /// @@ -108,12 +99,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return Ok(_mediaReceiverRegistrar.GetServiceXml()); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return Ok(_mediaReceiverRegistrar.GetServiceXml()); } /// @@ -133,12 +119,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetConnectionManager([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return Ok(_connectionManager.GetServiceXml()); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return Ok(_connectionManager.GetServiceXml()); } /// @@ -155,12 +136,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public async Task> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); } /// @@ -177,12 +153,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public async Task> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); } /// @@ -199,12 +170,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId) { - if (DlnaEntryPoint.Enabled) - { - return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); } /// @@ -224,12 +190,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public ActionResult ProcessMediaReceiverRegistrarEventRequest(string serverId) { - if (DlnaEntryPoint.Enabled) - { - return ProcessEventRequest(_mediaReceiverRegistrar); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return ProcessEventRequest(_mediaReceiverRegistrar); } /// @@ -249,12 +210,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public ActionResult ProcessContentDirectoryEventRequest(string serverId) { - if (DlnaEntryPoint.Enabled) - { - return ProcessEventRequest(_contentDirectory); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return ProcessEventRequest(_contentDirectory); } /// @@ -274,12 +230,7 @@ namespace Jellyfin.Api.Controllers [ProducesFile(MediaTypeNames.Text.Xml)] public ActionResult ProcessConnectionManagerEventRequest(string serverId) { - if (DlnaEntryPoint.Enabled) - { - return ProcessEventRequest(_connectionManager); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return ProcessEventRequest(_connectionManager); } /// @@ -299,12 +250,7 @@ namespace Jellyfin.Api.Controllers [ProducesImageFile] public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName) { - if (DlnaEntryPoint.Enabled) - { - return GetIconInternal(fileName); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return GetIconInternal(fileName); } /// @@ -322,12 +268,7 @@ namespace Jellyfin.Api.Controllers [ProducesImageFile] public ActionResult GetIcon([FromRoute, Required] string fileName) { - if (DlnaEntryPoint.Enabled) - { - return GetIconInternal(fileName); - } - - return StatusCode(StatusCodes.Status503ServiceUnavailable); + return GetIconInternal(fileName); } private ActionResult GetIconInternal(string fileName) From 007856e61af1fc23820a4a884b6df9d26678de68 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 10 Jun 2022 09:04:07 +0200 Subject: [PATCH 037/105] actually disable DLNA... --- Emby.Dlna/Configuration/DlnaOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs index 91fac4bef5c..e95a878c674 100644 --- a/Emby.Dlna/Configuration/DlnaOptions.cs +++ b/Emby.Dlna/Configuration/DlnaOptions.cs @@ -13,7 +13,7 @@ namespace Emby.Dlna.Configuration public DlnaOptions() { EnablePlayTo = true; - EnableServer = true; + EnableServer = false; BlastAliveMessages = true; SendOnlyMatchedHost = true; ClientDiscoveryIntervalSeconds = 60; From 93941f97281ee04ad27159591bd29316a38e8493 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 10 Jun 2022 22:16:13 -0400 Subject: [PATCH 038/105] Bump version to 10.8.0 --- debian/changelog | 18 +++--------------- fedora/jellyfin.spec | 10 +++------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/debian/changelog b/debian/changelog index 9c608e2baa5..ea135796114 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,20 +1,8 @@ -jellyfin-server (10.8.0~beta3) unstable; urgency=medium +jellyfin-server (10.8.0-1) unstable; urgency=medium - * New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3 + * New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0 - -- Jellyfin Packaging Team Sun, 15 May 2022 20:15:43 -0400 - -jellyfin-server (10.8.0~beta2) unstable; urgency=medium - - * New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 - - -- Jellyfin Packaging Team Sun, 17 Apr 2022 15:51:43 -0400 - -jellyfin-server (10.8.0~beta1) unstable; urgency=medium - - * New upstream version 10.8.0-beta1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta1 - - -- Jellyfin Packaging Team Fri, 25 Mar 2022 22:22:51 -0400 + -- Jellyfin Packaging Team Fri, 10 Jun 2022 22:15:12 -0400 jellyfin-server (10.7.0-1) unstable; urgency=medium diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 1a5c1b29730..56e7476d208 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.0~beta3 +Version: 10.8.0 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv3 @@ -153,12 +153,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog -* Sun May 15 2022 Jellyfin Packaging Team -- New upstream version 10.8.0-beta3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta3 -* Sun Apr 17 2022 Jellyfin Packaging Team -- New upstream version 10.8.0-beta2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta2 -* Fri Mar 25 2022 Jellyfin Packaging Team -- New upstream version 10.8.0-beta1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0-beta1 +* Fri Jun 10 2022 Jellyfin Packaging Team +- New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0 * Mon Nov 29 2021 Brian J. Murrell - Add jellyfin-server-lowports.service drop-in in a server-lowports subpackage to allow binding to low ports From 5204863705e4d0ccf3fa91b209702ff6c4264e0b Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 11 Jun 2022 15:08:07 +0200 Subject: [PATCH 039/105] fix: respect the image refresh options when parsing remote images from NFO --- .../Providers/ImageRefreshOptions.cs | 4 ++-- .../Manager/MetadataService.cs | 18 ++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs index a9d16a49e4e..fd73ed5f808 100644 --- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs @@ -34,8 +34,8 @@ namespace MediaBrowser.Controller.Providers public bool IsReplacingImage(ImageType type) { - return ImageRefreshMode == MetadataRefreshMode.FullRefresh && - (ReplaceAllImages || ReplaceImages.Contains(type)); + return ImageRefreshMode == MetadataRefreshMode.FullRefresh + && (ReplaceAllImages || ReplaceImages.Contains(type)); } } } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 6d767914f7d..5a2936bd8b8 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -655,8 +655,6 @@ namespace MediaBrowser.Providers.Manager }; temp.Item.Path = item.Path; - var userDataList = new List(); - // If replacing all metadata, run internet providers first if (options.ReplaceAllMetadata) { @@ -670,7 +668,7 @@ namespace MediaBrowser.Providers.Manager var hasLocalMetadata = false; - foreach (var provider in providers.OfType>().ToList()) + foreach (var provider in providers.OfType>()) { var providerName = provider.GetType().Name; Logger.LogDebug("Running {Provider} for {Item}", providerName, logName); @@ -687,6 +685,11 @@ namespace MediaBrowser.Providers.Manager { try { + if (!options.IsReplacingImage(remoteImage.Type)) + { + continue; + } + await ProviderManager.SaveImage(item, remoteImage.Url, remoteImage.Type, null, cancellationToken).ConfigureAwait(false); refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } @@ -701,11 +704,6 @@ namespace MediaBrowser.Providers.Manager refreshResult.UpdateType |= ItemUpdateType.ImageUpdate; } - if (localItem.UserDataList != null) - { - userDataList.AddRange(localItem.UserDataList); - } - MergeData(localItem, temp, Array.Empty(), !options.ReplaceAllMetadata, true); refreshResult.UpdateType |= ItemUpdateType.MetadataImport; @@ -764,15 +762,11 @@ namespace MediaBrowser.Providers.Manager } } - // var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; - foreach (var provider in customProviders.Where(i => i is not IPreRefreshProvider)) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } - // ImportUserData(item, userDataList, cancellationToken); - return refreshResult; } From 874fcaba691f925ecb5f9270b691754685d65691 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 12 Jun 2022 01:05:29 -0400 Subject: [PATCH 040/105] Move service hardening options to override config Some combination of these options were causing problems with the functionality of restart.sh as described in the comment and in detail in issue #7503. While these seem OK on their face, the implications of this breaking restart.sh means that they could potentially break other things too. Thus, we should move these into the optional override file which is in the administrator's full control, instead of in the default unit, and leave them off unless a user or package maintainer (e.g. NixOS as described in the original issue #6952) wants to enable them. Fixes #7503 --- debian/conf/jellyfin.service.conf | 48 +++++++++++++++++++++++++++++++ debian/jellyfin.service | 33 --------------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/debian/conf/jellyfin.service.conf b/debian/conf/jellyfin.service.conf index 1b69dd74ef3..1f92d7d94eb 100644 --- a/debian/conf/jellyfin.service.conf +++ b/debian/conf/jellyfin.service.conf @@ -3,5 +3,53 @@ # Use this file to override the user or environment file location. [Service] +# Alter the user that Jellyfin runs as #User = jellyfin + +# Alter where environment variables are sourced from #EnvironmentFile = /etc/default/jellyfin + +# Service hardening options +# These were added in PR #6953 to solve issue #6952, but some combination of +# them causes "restart.sh" functionality to break with the following error: +# sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the +# 'nosuid' option set or an NFS file system without root privileges? +# See issue #7503 for details on the troubleshooting that went into this. +# Since these were added for NixOS specifically and are above and beyond +# what 99% of systemd units do, they have been moved here as optional +# additional flags to set for maximum system security and can be enabled at +# the administrator's or package maintainer's discretion. +# Uncomment these only if you know what you're doing, and doing so may cause +# bugs with in-server Restart and potentially other functionality as well. +#NoNewPrivileges=true +#SystemCallArchitectures=native +#RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK +#RestrictNamespaces=false +#RestrictRealtime=true +#RestrictSUIDSGID=true +#ProtectControlGroups=false +#ProtectHostname=true +#ProtectKernelLogs=false +#ProtectKernelModules=false +#ProtectKernelTunables=false +#LockPersonality=true +#PrivateTmp=false +#PrivateDevices=false +#PrivateUsers=true +#RemoveIPC=true +#SystemCallFilter=~@clock +#SystemCallFilter=~@aio +#SystemCallFilter=~@chown +#SystemCallFilter=~@cpu-emulation +#SystemCallFilter=~@debug +#SystemCallFilter=~@keyring +#SystemCallFilter=~@memlock +#SystemCallFilter=~@module +#SystemCallFilter=~@mount +#SystemCallFilter=~@obsolete +#SystemCallFilter=~@privileged +#SystemCallFilter=~@raw-io +#SystemCallFilter=~@reboot +#SystemCallFilter=~@setuid +#SystemCallFilter=~@swap +#SystemCallErrorNumber=EPERM diff --git a/debian/jellyfin.service b/debian/jellyfin.service index 064e1053735..2f97c465497 100644 --- a/debian/jellyfin.service +++ b/debian/jellyfin.service @@ -13,38 +13,5 @@ Restart = on-failure TimeoutSec = 15 SuccessExitStatus=0 143 -NoNewPrivileges=true -SystemCallArchitectures=native -RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK -RestrictNamespaces=false -RestrictRealtime=true -RestrictSUIDSGID=true -ProtectControlGroups=false -ProtectHostname=true -ProtectKernelLogs=false -ProtectKernelModules=false -ProtectKernelTunables=false -LockPersonality=true -PrivateTmp=false -PrivateDevices=false -PrivateUsers=true -RemoveIPC=true -SystemCallFilter=~@clock -SystemCallFilter=~@aio -SystemCallFilter=~@chown -SystemCallFilter=~@cpu-emulation -SystemCallFilter=~@debug -SystemCallFilter=~@keyring -SystemCallFilter=~@memlock -SystemCallFilter=~@module -SystemCallFilter=~@mount -SystemCallFilter=~@obsolete -SystemCallFilter=~@privileged -SystemCallFilter=~@raw-io -SystemCallFilter=~@reboot -SystemCallFilter=~@setuid -SystemCallFilter=~@swap -SystemCallErrorNumber=EPERM - [Install] WantedBy = multi-user.target From f318417c4f2791cf7d4c1209d9a6c432bbc002c9 Mon Sep 17 00:00:00 2001 From: cvium Date: Sun, 12 Jun 2022 13:55:17 +0200 Subject: [PATCH 041/105] fix: tv shows do not support multi edition --- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 140c4272e12..fe4ccd6acb3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -225,7 +225,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) { - return ResolveVideos(parent, files, true, collectionType, true); + return ResolveVideos(parent, files, false, collectionType, true); } return null; From 11c5a0b1824f589ce142410812b7ac509968c96a Mon Sep 17 00:00:00 2001 From: Ian Walton Date: Sat, 11 Jun 2022 20:34:29 -0400 Subject: [PATCH 042/105] Prevent 400 error when using navigation buttons. Co-authored-by: Claus Vium --- MediaBrowser.Model/Session/GeneralCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs index 757b19b3174..dfbb616aa88 100644 --- a/MediaBrowser.Model/Session/GeneralCommand.cs +++ b/MediaBrowser.Model/Session/GeneralCommand.cs @@ -14,9 +14,9 @@ public class GeneralCommand } [JsonConstructor] - public GeneralCommand(Dictionary arguments) + public GeneralCommand(Dictionary? arguments) { - Arguments = arguments; + Arguments = arguments ?? new Dictionary(); } public GeneralCommandType Name { get; set; } From 079fac4a54e615b88a6c5a0d26fdceed0507cef9 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 12 Jun 2022 08:32:38 -0600 Subject: [PATCH 043/105] Switch to FirstOrDefault extension --- Jellyfin.Api/Controllers/SearchController.cs | 3 ++- src/Jellyfin.Extensions/ReadOnlyListExtension.cs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 6ffedccbd4c..07e113ad3e4 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -6,6 +6,7 @@ using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -187,7 +188,7 @@ namespace Jellyfin.Api.Controllers result.AlbumArtist = album.AlbumArtist; break; case Audio song: - result.AlbumArtist = song.AlbumArtists?[0]; + result.AlbumArtist = song.AlbumArtists?.FirstOrDefault(); result.Artists = song.Artists; MusicAlbum musicAlbum = song.AlbumEntity; diff --git a/src/Jellyfin.Extensions/ReadOnlyListExtension.cs b/src/Jellyfin.Extensions/ReadOnlyListExtension.cs index 7785cfb495e..ba99bb534da 100644 --- a/src/Jellyfin.Extensions/ReadOnlyListExtension.cs +++ b/src/Jellyfin.Extensions/ReadOnlyListExtension.cs @@ -57,5 +57,21 @@ namespace Jellyfin.Extensions return -1; } + + /// + /// Get the first or default item from a list. + /// + /// The source list. + /// The type of item. + /// The first item or default if list is empty. + public static T? FirstOrDefault(this IReadOnlyList? source) + { + if (source is null || source.Count == 0) + { + return default; + } + + return source[0]; + } } } From c07c7b753cdf7f830b839b4bec804accc1aefb55 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 13 Jun 2022 10:32:34 +0200 Subject: [PATCH 044/105] fix: only use keyframes when remuxing video --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 3 ++- .../Playlist/CreateMainPlaylistRequest.cs | 9 ++++++++- .../Playlist/DynamicHlsPlaylistGenerator.cs | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 9173487dd89..8127193c209 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1414,7 +1414,8 @@ namespace Jellyfin.Api.Controllers state.RunTimeTicks ?? 0, state.Request.SegmentContainer ?? string.Empty, "hls1/main/", - Request.QueryString.ToString()); + Request.QueryString.ToString(), + EncodingHelper.IsCopyCodec(state.OutputVideoCodec)); var playlist = _dynamicHlsPlaylistGenerator.CreateMainPlaylist(request); return new FileContentResult(Encoding.UTF8.GetBytes(playlist), MimeTypes.GetMimeType("playlist.m3u8")); diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs index ac28ca26ab3..8572a5eaedf 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/CreateMainPlaylistRequest.cs @@ -14,7 +14,8 @@ public class CreateMainPlaylistRequest /// The desired segment container eg. "ts". /// The URI prefix for the relative URL in the playlist. /// The desired query string to append (must start with ?). - public CreateMainPlaylistRequest(string filePath, int desiredSegmentLengthMs, long totalRuntimeTicks, string segmentContainer, string endpointPrefix, string queryString) + /// Whether the video is being remuxed. + public CreateMainPlaylistRequest(string filePath, int desiredSegmentLengthMs, long totalRuntimeTicks, string segmentContainer, string endpointPrefix, string queryString, bool isRemuxingVideo) { FilePath = filePath; DesiredSegmentLengthMs = desiredSegmentLengthMs; @@ -22,6 +23,7 @@ public class CreateMainPlaylistRequest SegmentContainer = segmentContainer; EndpointPrefix = endpointPrefix; QueryString = queryString; + IsRemuxingVideo = isRemuxingVideo; } /// @@ -53,4 +55,9 @@ public class CreateMainPlaylistRequest /// Gets the query string. /// public string QueryString { get; } + + /// + /// Gets a value indicating whether the video is being remuxed. + /// + public bool IsRemuxingVideo { get; } } diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs index 3382ba25115..07a6d6050b0 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs @@ -34,7 +34,8 @@ public class DynamicHlsPlaylistGenerator : IDynamicHlsPlaylistGenerator public string CreateMainPlaylist(CreateMainPlaylistRequest request) { IReadOnlyList segments; - if (TryExtractKeyframes(request.FilePath, out var keyframeData)) + // For video transcodes it is sufficient with equal length segments as ffmpeg will create new keyframes + if (request.IsRemuxingVideo && TryExtractKeyframes(request.FilePath, out var keyframeData)) { segments = ComputeSegments(keyframeData, request.DesiredSegmentLengthMs); } From ac760b9c8657060b7085d900215ae26021d94123 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 13 Jun 2022 11:53:19 +0200 Subject: [PATCH 045/105] fix: read configuration during Invoke instead of during construction --- .../Middleware/ResponseTimeMiddleware.cs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs index da9b691365e..1c25696cd10 100644 --- a/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs +++ b/Jellyfin.Server/Middleware/ResponseTimeMiddleware.cs @@ -19,41 +19,44 @@ namespace Jellyfin.Server.Middleware private readonly RequestDelegate _next; private readonly ILogger _logger; - private readonly bool _enableWarning; - private readonly long _warningThreshold; - /// /// Initializes a new instance of the class. /// /// Next request delegate. /// Instance of the interface. - /// Instance of the interface. public ResponseTimeMiddleware( RequestDelegate next, - ILogger logger, - IServerConfigurationManager serverConfigurationManager) + ILogger logger) { _next = next; _logger = logger; - - _enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; - _warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; } /// /// Invoke request. /// /// Request context. + /// Instance of the interface. /// Task. - public async Task Invoke(HttpContext context) + public async Task Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager) { var watch = new Stopwatch(); watch.Start(); - + var enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; + var warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; context.Response.OnStarting(() => { watch.Stop(); - LogWarning(context, watch); + if (enableWarning && watch.ElapsedMilliseconds > warningThreshold) + { + _logger.LogWarning( + "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}", + context.Request.GetDisplayUrl(), + context.GetNormalizedRemoteIp(), + watch.Elapsed, + context.Response.StatusCode); + } + var responseTimeForCompleteRequest = watch.ElapsedMilliseconds; context.Response.Headers[ResponseHeaderResponseTime] = responseTimeForCompleteRequest.ToString(CultureInfo.InvariantCulture); return Task.CompletedTask; @@ -62,18 +65,5 @@ namespace Jellyfin.Server.Middleware // Call the next delegate/middleware in the pipeline await this._next(context).ConfigureAwait(false); } - - private void LogWarning(HttpContext context, Stopwatch watch) - { - if (_enableWarning && watch.ElapsedMilliseconds > _warningThreshold) - { - _logger.LogWarning( - "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}", - context.Request.GetDisplayUrl(), - context.GetNormalizedRemoteIp(), - watch.Elapsed, - context.Response.StatusCode); - } - } } } From e5aa708cb93915054b118aa65d44797a7c8b7a5d Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 14 Jun 2022 00:48:16 +0800 Subject: [PATCH 046/105] Improve AMF tonemap speed when using sw decoding Reduce memory copy-back, also set the target device of hwupload explicitly. --- .../MediaEncoding/EncodingHelper.cs | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index eb344b52877..3b7fd482a11 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2902,7 +2902,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doCuTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=cuda"); } } @@ -2982,7 +2982,7 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add(subTextSubtitlesFilter); } - subFilters.Add("hwupload"); + subFilters.Add("hwupload=derive_device=cuda"); overlayFilters.Add("overlay_cuda=eof_action=endall:shortest=1:repeatlast=0"); } } @@ -3094,7 +3094,9 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doOclTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=d3d11va:extra_hw_frames=16"); + mainFilters.Add("format=d3d11"); + mainFilters.Add("hwmap=derive_device=opencl"); } } @@ -3121,7 +3123,7 @@ namespace MediaBrowser.Controller.MediaEncoding var memoryOutput = false; var isUploadForOclTonemap = isSwDecoder && doOclTonemap; - if ((isD3d11vaDecoder && isSwEncoder) || isUploadForOclTonemap) + if ((isD3d11vaDecoder && isSwEncoder)) { memoryOutput = true; @@ -3133,7 +3135,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // OUTPUT yuv420p surface - if (isSwDecoder && isAmfEncoder) + if (isSwDecoder && isAmfEncoder && !isUploadForOclTonemap) { memoryOutput = true; } @@ -3148,7 +3150,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } - if (isDxInDxOut && !hasSubs) + if ((isDxInDxOut || isUploadForOclTonemap) && !hasSubs) { // OUTPUT d3d11(nv12) surface(vram) // reverse-mapping via d3d11-opencl interop. @@ -3159,7 +3161,7 @@ namespace MediaBrowser.Controller.MediaEncoding /* Make sub and overlay filters for subtitle stream */ var subFilters = new List(); var overlayFilters = new List(); - if (isDxInDxOut) + if (isDxInDxOut || isUploadForOclTonemap) { if (hasSubs) { @@ -3180,7 +3182,7 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add(subTextSubtitlesFilter); } - subFilters.Add("hwupload"); + subFilters.Add("hwupload=derive_device=opencl"); overlayFilters.Add("overlay_opencl=eof_action=endall:shortest=1:repeatlast=0"); overlayFilters.Add("hwmap=derive_device=d3d11va:reverse=1"); overlayFilters.Add("format=d3d11"); @@ -3314,7 +3316,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doOclTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=opencl"); } } else if (isD3d11vaDecoder || isQsvDecoder) @@ -3421,7 +3423,7 @@ namespace MediaBrowser.Controller.MediaEncoding // qsv requires a fixed pool size. // default to 64 otherwise it will fail on certain iGPU. - subFilters.Add("hwupload=extra_hw_frames=64"); + subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64"); var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var overlaySize = (overlayW.HasValue && overlayH.HasValue) @@ -3511,7 +3513,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doOclTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=opencl"); } } else if (isVaapiDecoder || isQsvDecoder) @@ -3632,7 +3634,7 @@ namespace MediaBrowser.Controller.MediaEncoding // qsv requires a fixed pool size. // default to 64 otherwise it will fail on certain iGPU. - subFilters.Add("hwupload=extra_hw_frames=64"); + subFilters.Add("hwupload=derive_device=qsv:extra_hw_frames=64"); var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var overlaySize = (overlayW.HasValue && overlayH.HasValue) @@ -3695,7 +3697,7 @@ namespace MediaBrowser.Controller.MediaEncoding var newfilters = new List(); var noOverlay = swFilterChain.OverlayFilters.Count == 0; newfilters.AddRange(noOverlay ? swFilterChain.MainFilters : swFilterChain.OverlayFilters); - newfilters.Add("hwupload"); + newfilters.Add("hwupload=derive_device=vaapi"); var mainFilters = noOverlay ? newfilters : swFilterChain.MainFilters; var overlayFilters = noOverlay ? swFilterChain.OverlayFilters : newfilters; @@ -3776,7 +3778,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doOclTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=opencl"); } } else if (isVaapiDecoder) @@ -3881,7 +3883,7 @@ namespace MediaBrowser.Controller.MediaEncoding subFilters.Add(subTextSubtitlesFilter); } - subFilters.Add("hwupload"); + subFilters.Add("hwupload=derive_device=vaapi"); var (overlayW, overlayH) = GetFixedOutputSize(inW, inH, reqW, reqH, reqMaxW, reqMaxH); var overlaySize = (overlayW.HasValue && overlayH.HasValue) @@ -3972,7 +3974,7 @@ namespace MediaBrowser.Controller.MediaEncoding // sw => hw if (doOclTonemap) { - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=opencl"); } } else if (isVaapiDecoder) @@ -4002,7 +4004,7 @@ namespace MediaBrowser.Controller.MediaEncoding { mainFilters.Add("hwdownload"); mainFilters.Add("format=p010le"); - mainFilters.Add("hwupload"); + mainFilters.Add("hwupload=derive_device=opencl"); } } From 83d8dbf93e162d3a45edd07dd1132d7dcd440509 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 14 Jun 2022 02:21:00 +0800 Subject: [PATCH 047/105] Remove MPEG4 hwaccel from AMF --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index eb344b52877..ad988872fe3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4684,11 +4684,6 @@ namespace MediaBrowser.Controller.MediaEncoding { return GetHwaccelType(state, options, "vc1", bitDepth, hwSurface); } - - if (string.Equals("mpeg4", videoStream.Codec, StringComparison.OrdinalIgnoreCase)) - { - return GetHwaccelType(state, options, "mpeg4", bitDepth, hwSurface); - } } if (is8_10bitSwFormatsAmf) From c8282e8441ba572e35a9db92f415dcebe4bbbe67 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Tue, 14 Jun 2022 17:41:16 +0800 Subject: [PATCH 048/105] Apply suggestions from code review Co-authored-by: Bond-009 --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3b7fd482a11..07a20baa0b8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3123,7 +3123,7 @@ namespace MediaBrowser.Controller.MediaEncoding var memoryOutput = false; var isUploadForOclTonemap = isSwDecoder && doOclTonemap; - if ((isD3d11vaDecoder && isSwEncoder)) + if (isD3d11vaDecoder && isSwEncoder) { memoryOutput = true; From d73e9f3af53bc5674dd8a1d7773ebf3c387f064f Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 14 Jun 2022 08:18:35 -0600 Subject: [PATCH 049/105] Fix splashscreen (#7895) --- Jellyfin.Api/Controllers/ImageController.cs | 37 ++++++++++++++++++- .../Branding/BrandingOptions.cs | 5 +++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 05d80ba35d6..6c7842c7b91 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1724,6 +1724,11 @@ namespace Jellyfin.Api.Controllers [FromQuery, Range(0, 100)] int quality = 90) { var brandingOptions = _serverConfigurationManager.GetConfiguration("branding"); + if (!brandingOptions.SplashscreenEnabled) + { + return NotFound(); + } + string splashscreenPath; if (!string.IsNullOrWhiteSpace(brandingOptions.SplashscreenLocation) @@ -1776,6 +1781,7 @@ namespace Jellyfin.Api.Controllers /// /// Uploads a custom splashscreen. + /// The body is expected to the image contents base64 encoded. /// /// A indicating success. /// Successfully uploaded new splashscreen. @@ -1799,7 +1805,13 @@ namespace Jellyfin.Api.Controllers return BadRequest("Error reading mimetype from uploaded image"); } - var filePath = Path.Combine(_appPaths.DataPath, "splashscreen-upload" + MimeTypes.ToExtension(mimeType.Value)); + var extension = MimeTypes.ToExtension(mimeType.Value); + if (string.IsNullOrEmpty(extension)) + { + return BadRequest("Error converting mimetype to an image extension"); + } + + var filePath = Path.Combine(_appPaths.DataPath, "splashscreen-upload" + extension); var brandingOptions = _serverConfigurationManager.GetConfiguration("branding"); brandingOptions.SplashscreenLocation = filePath; _serverConfigurationManager.SaveConfiguration("branding", brandingOptions); @@ -1812,6 +1824,29 @@ namespace Jellyfin.Api.Controllers return NoContent(); } + /// + /// Delete a custom splashscreen. + /// + /// A indicating success. + /// Successfully deleted the custom splashscreen. + /// User does not have permission to delete splashscreen.. + [HttpDelete("Branding/Splashscreen")] + [Authorize(Policy = Policies.RequiresElevation)] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult DeleteCustomSplashscreen() + { + var brandingOptions = _serverConfigurationManager.GetConfiguration("branding"); + if (!string.IsNullOrEmpty(brandingOptions.SplashscreenLocation) + && System.IO.File.Exists(brandingOptions.SplashscreenLocation)) + { + System.IO.File.Delete(brandingOptions.SplashscreenLocation); + brandingOptions.SplashscreenLocation = null; + _serverConfigurationManager.SaveConfiguration("branding", brandingOptions); + } + + return NoContent(); + } + private static async Task GetMemoryStream(Stream inputStream) { using var reader = new StreamReader(inputStream); diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs index cc42c1718a9..a0adb56ef79 100644 --- a/MediaBrowser.Model/Branding/BrandingOptions.cs +++ b/MediaBrowser.Model/Branding/BrandingOptions.cs @@ -20,6 +20,11 @@ public class BrandingOptions /// The custom CSS. public string? CustomCss { get; set; } + /// + /// Gets or sets a value indicating whether to enable the splashscreen. + /// + public bool SplashscreenEnabled { get; set; } = true; + /// /// Gets or sets the splashscreen location on disk. /// From 16fba6035c1f1f3f978b33f4becb9579d8a031f8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 14 Jun 2022 18:15:56 +0200 Subject: [PATCH 050/105] Enable XmlReaderSettings.Async, fixes #7929 --- .../Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 5ae5ff3becd..4bf66c09882 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -465,7 +465,8 @@ namespace MediaBrowser.Providers.Music ValidationType = ValidationType.None, CheckCharacters = false, IgnoreProcessingInstructions = true, - IgnoreComments = true + IgnoreComments = true, + Async = true }; using var reader = XmlReader.Create(oReader, settings); From 052a59ac3e88bc260fd75c625b8d885ffd685eab Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sat, 30 Apr 2022 15:36:09 +0200 Subject: [PATCH 051/105] Add tests for preferred audio language selection --- .../Library/MediaStreamSelectorTests.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs index d59f2f4e514..2b1c6e937b3 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs @@ -16,15 +16,31 @@ public class MediaStreamSelectorTests } [Theory] - [InlineData(true)] - [InlineData(false)] - public void GetDefaultAudioStreamIndex_WithoutDefault_NotNull(bool preferDefaultTrack) + [InlineData(new string[0], false, 1)] + [InlineData(new string[0], true, 1)] + [InlineData(new[] { "eng" }, false, 2)] + [InlineData(new[] { "eng" }, true, 1)] + [InlineData(new[] { "eng", "fre" }, false, 2)] + [InlineData(new[] { "fre", "eng" }, false, 1)] + [InlineData(new[] { "eng", "fre" }, true, 1)] + public void GetDefaultAudioStreamIndex_PreferredLanguage_SelectsCorrect(string[] preferredLanguages, bool preferDefaultTrack, int expectedIndex) { - var streams = new[] + var streams = new MediaStream[] { - new MediaStream() + new() + { + Index = 1, + Language = "fre", + IsDefault = true + }, + new() + { + Index = 2, + Language = "eng", + IsDefault = false + } }; - Assert.NotNull(MediaStreamSelector.GetDefaultAudioStreamIndex(streams, Array.Empty(), preferDefaultTrack)); + Assert.Equal(expectedIndex, MediaStreamSelector.GetDefaultAudioStreamIndex(streams, preferredLanguages, preferDefaultTrack)); } } From 5d66c84f2d34aca4db2e27419088ad95fb7c5807 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Tue, 17 May 2022 21:03:51 +0200 Subject: [PATCH 052/105] Fix default audio selection ignoring type --- .../Library/MediaStreamSelector.cs | 4 ++-- .../Library/MediaStreamSelectorTests.cs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index c5abb9a0a37..20a2edb05a1 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -13,11 +13,11 @@ namespace Emby.Server.Implementations.Library { public static int? GetDefaultAudioStreamIndex(IReadOnlyList streams, IReadOnlyList preferredLanguages, bool preferDefaultTrack) { - var sortedStreams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages); + var sortedStreams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages).ToList(); if (preferDefaultTrack) { - var defaultStream = streams.FirstOrDefault(i => i.IsDefault); + var defaultStream = sortedStreams.FirstOrDefault(i => i.IsDefault); if (defaultStream != null) { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs index 2b1c6e937b3..538010f6c01 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MediaStreamSelectorTests.cs @@ -27,15 +27,23 @@ public class MediaStreamSelectorTests { var streams = new MediaStream[] { + new() + { + Index = 0, + Type = MediaStreamType.Video, + IsDefault = true + }, new() { Index = 1, + Type = MediaStreamType.Audio, Language = "fre", IsDefault = true }, new() { Index = 2, + Type = MediaStreamType.Audio, Language = "eng", IsDefault = false } From 3f497459cb3f43b97847ebfd51de8c058aa8e2f8 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 15 Jun 2022 12:19:10 +0200 Subject: [PATCH 053/105] Fix recommendations --- .../Data/SqliteItemRepository.cs | 39 ++++++++------ MediaBrowser.Model/Querying/ItemSortBy.cs | 52 ++++++++++++++++++- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 6c243050d45..964a630b265 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2403,7 +2403,7 @@ namespace Emby.Server.Implementations.Data } // genres, tags, studios, person, year? - builder.Append("+ (Select count(1) * 10 from ItemValues where ItemId=Guid and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId))"); + builder.Append("+ (Select count(1) * 10 from ItemValues where ItemId=Guid and CleanValue in (select CleanValue from ItemValues where ItemId=@SimilarItemId))"); if (item is MusicArtist) { @@ -3058,12 +3058,12 @@ namespace Emby.Server.Implementations.Data if (string.Equals(name, ItemSortBy.Artist, StringComparison.OrdinalIgnoreCase)) { - return "(select CleanValue from itemvalues where ItemId=Guid and Type=0 LIMIT 1)"; + return "(select CleanValue from ItemValues where ItemId=Guid and Type=0 LIMIT 1)"; } if (string.Equals(name, ItemSortBy.AlbumArtist, StringComparison.OrdinalIgnoreCase)) { - return "(select CleanValue from itemvalues where ItemId=Guid and Type=1 LIMIT 1)"; + return "(select CleanValue from ItemValues where ItemId=Guid and Type=1 LIMIT 1)"; } if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase)) @@ -3073,7 +3073,7 @@ namespace Emby.Server.Implementations.Data if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase)) { - return "(select CleanValue from itemvalues where ItemId=Guid and Type=3 LIMIT 1)"; + return "(select CleanValue from ItemValues where ItemId=Guid and Type=3 LIMIT 1)"; } if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase)) @@ -3146,6 +3146,11 @@ namespace Emby.Server.Implementations.Data return ItemSortBy.IndexNumber; } + if (string.Equals(name, ItemSortBy.SimilarityScore, StringComparison.OrdinalIgnoreCase)) + { + return ItemSortBy.SimilarityScore; + } + // Unknown SortBy, just sort by the SortName. return ItemSortBy.SortName; } @@ -3846,7 +3851,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); + clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); if (statement != null) { statement.TryBind(paramName, artistId); @@ -3867,7 +3872,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))"); + clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))"); if (statement != null) { statement.TryBind(paramName, artistId); @@ -3888,7 +3893,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@ArtistIds" + index; - clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type=1))"); + clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from ItemValues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from ItemValues where ItemId=Guid and Type=1))"); if (statement != null) { statement.TryBind(paramName, artistId); @@ -3930,7 +3935,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@ExcludeArtistId" + index; - clauses.Add("(guid not in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); + clauses.Add("(guid not in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); if (statement != null) { statement.TryBind(paramName, artistId); @@ -3951,7 +3956,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@GenreId" + index; - clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))"); + clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))"); if (statement != null) { statement.TryBind(paramName, genreId); @@ -3970,7 +3975,7 @@ namespace Emby.Server.Implementations.Data var index = 0; foreach (var item in query.Genres) { - clauses.Add("@Genre" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=2)"); + clauses.Add("@Genre" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=2)"); if (statement != null) { statement.TryBind("@Genre" + index, GetCleanValue(item)); @@ -3989,7 +3994,7 @@ namespace Emby.Server.Implementations.Data var index = 0; foreach (var item in tags) { - clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)"); + clauses.Add("@Tag" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); if (statement != null) { statement.TryBind("@Tag" + index, GetCleanValue(item)); @@ -4008,7 +4013,7 @@ namespace Emby.Server.Implementations.Data var index = 0; foreach (var item in excludeTags) { - clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from itemvalues where ItemId=Guid and Type=4)"); + clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); if (statement != null) { statement.TryBind("@ExcludeTag" + index, GetCleanValue(item)); @@ -4029,7 +4034,7 @@ namespace Emby.Server.Implementations.Data { var paramName = "@StudioId" + index; - clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))"); + clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))"); if (statement != null) { @@ -4508,7 +4513,7 @@ namespace Emby.Server.Implementations.Data { int index = 0; string excludedTags = string.Join(',', query.ExcludeInheritedTags.Select(_ => paramName + index++)); - whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)"); + whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)"); } else { @@ -4743,11 +4748,11 @@ namespace Emby.Server.Implementations.Data ';', new string[] { - "delete from itemvalues where type = 6", + "delete from ItemValues where type = 6", - "insert into itemvalues (ItemId, Type, Value, CleanValue) select ItemId, 6, Value, CleanValue from ItemValues where Type=4", + "insert into ItemValues (ItemId, Type, Value, CleanValue) select ItemId, 6, Value, CleanValue from ItemValues where Type=4", - @"insert into itemvalues (ItemId, Type, Value, CleanValue) select AncestorIds.itemid, 6, ItemValues.Value, ItemValues.CleanValue + @"insert into ItemValues (ItemId, Type, Value, CleanValue) select AncestorIds.itemid, 6, ItemValues.Value, ItemValues.CleanValue FROM AncestorIds LEFT JOIN ItemValues ON (AncestorIds.AncestorId = ItemValues.ItemId) where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type = 4 " diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs index 0a28acf37b5..470507c530f 100644 --- a/MediaBrowser.Model/Querying/ItemSortBy.cs +++ b/MediaBrowser.Model/Querying/ItemSortBy.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - namespace MediaBrowser.Model.Querying { /// @@ -7,6 +5,9 @@ namespace MediaBrowser.Model.Querying /// public static class ItemSortBy { + /// + /// The aired episode order. + /// public const string AiredEpisodeOrder = "AiredEpisodeOrder"; /// @@ -44,6 +45,9 @@ namespace MediaBrowser.Model.Querying /// public const string PremiereDate = "PremiereDate"; + /// + /// The start date. + /// public const string StartDate = "StartDate"; /// @@ -51,6 +55,9 @@ namespace MediaBrowser.Model.Querying /// public const string SortName = "SortName"; + /// + /// The name. + /// public const string Name = "Name"; /// @@ -83,28 +90,69 @@ namespace MediaBrowser.Model.Querying /// public const string CriticRating = "CriticRating"; + /// + /// The IsFolder boolean. + /// public const string IsFolder = "IsFolder"; + /// + /// The IsUnplayed boolean. + /// public const string IsUnplayed = "IsUnplayed"; + /// + /// The IsPlayed boolean. + /// public const string IsPlayed = "IsPlayed"; + /// + /// The series sort. + /// public const string SeriesSortName = "SeriesSortName"; + /// + /// The video bitrate. + /// public const string VideoBitRate = "VideoBitRate"; + /// + /// The air time. + /// public const string AirTime = "AirTime"; + /// + /// The studio. + /// public const string Studio = "Studio"; + /// + /// The IsFavouriteOrLiked boolean. + /// public const string IsFavoriteOrLiked = "IsFavoriteOrLiked"; + /// + /// The last content added date. + /// public const string DateLastContentAdded = "DateLastContentAdded"; + /// + /// The series last played date. + /// public const string SeriesDatePlayed = "SeriesDatePlayed"; + /// + /// The parent index number. + /// public const string ParentIndexNumber = "ParentIndexNumber"; + /// + /// The index number. + /// public const string IndexNumber = "IndexNumber"; + + /// + /// The similarity score. + /// + public const string SimilarityScore = "SimilarityScore"; } } From 255f5a6707be9c7c4a7124b50c4083b4a5a92899 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 15 Jun 2022 23:27:49 +0800 Subject: [PATCH 054/105] Fix the int overflow issue in encoder bufsize --- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 02af2e43532..83c9141a933 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -216,7 +216,7 @@ namespace Jellyfin.Api.Helpers var sdrVideoUrl = ReplaceProfile(playlistUrl, "hevc", string.Join(',', requestedVideoProfiles), "main"); sdrVideoUrl += "&AllowVideoStreamCopy=false"; - var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec) ?? 0; + var sdrOutputVideoBitrate = _encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec); var sdrOutputAudioBitrate = _encodingHelper.GetAudioBitrateParam(state.VideoRequest, state.AudioStream) ?? 0; var sdrTotalBitrate = sdrOutputAudioBitrate + sdrOutputVideoBitrate; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1755b872f4f..5bf6dad7ad3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1893,7 +1893,7 @@ namespace MediaBrowser.Controller.MediaEncoding return request.EnableAutoStreamCopy; } - public int? GetVideoBitrateParamValue(BaseEncodingJobOptions request, MediaStream videoStream, string outputVideoCodec) + public int GetVideoBitrateParamValue(BaseEncodingJobOptions request, MediaStream videoStream, string outputVideoCodec) { var bitrate = request.VideoBitRate; @@ -1925,7 +1925,8 @@ namespace MediaBrowser.Controller.MediaEncoding } } - return bitrate; + // Cap the max target bitrate to intMax/2 to satisify the bufsize=bitrate*2. + return Math.Min(bitrate ?? 0, int.MaxValue / 2); } private int GetMinBitrate(int sourceBitrate, int requestedBitrate) From c7c0cdad9569431604a24a2b987509e66ae479aa Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 15 Jun 2022 23:39:27 +0800 Subject: [PATCH 055/105] Fix the map index of externel audio --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5bf6dad7ad3..04778e69933 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2273,7 +2273,10 @@ namespace MediaBrowser.Controller.MediaEncoding int audioStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.AudioStream); if (state.AudioStream.IsExternal) { - bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream; + bool hasExternalGraphicsSubs = state.SubtitleStream != null + && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode + && state.SubtitleStream.IsExternal + && !state.SubtitleStream.IsTextSubtitleStream; int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1; args += string.Format( From 46491d0813948923a970b9600df54c7059637230 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Sun, 17 Apr 2022 23:24:15 +0200 Subject: [PATCH 056/105] Rewrite Fedora build version detection Rewrite so we don't need to constantly update with every new Fedora release. This is especially useful when Fedora and Jellyfin release cycles don't line up. Version selection is as follows: * TARGET environment variable, which is currently used already * Currently running Fedora version * Hardcoded Fallback version that can be updated occasionally --- fedora/Makefile | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fedora/Makefile b/fedora/Makefile index 261fd262d81..6f9cf47190e 100644 --- a/fedora/Makefile +++ b/fedora/Makefile @@ -8,13 +8,17 @@ TARBALL :=$(NAME)-$(subst -,~,$(VERSION)).tar.gz epel-7-x86_64_repos := https://packages.microsoft.com/rhel/7/prod/ epel-8-x86_64_repos := https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ -fedora_repos := https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ -fedora-34-x86_64_repos := $(fedora_repos) -fedora-35-x86_64_repos := $(fedora_repos) -fedora-34-x86_64_repos := $(fedora_repos) + +fed_ver := $(shell rpm -E %fedora) +# fallback when not running on Fedora +fed_ver ?= 36 +TARGET ?= fedora-$(fed_ver)-x86_64 + +ifeq ($(findstring fedora,$(TARGET)),fedora) +$(TARGET)_repos := https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ +endif outdir ?= $(PWD)/$(DIR)/ -TARGET ?= fedora-35-x86_64 srpm: $(DIR)/$(SRPM) tarball: $(DIR)/$(TARBALL) From c243f588a05fc264b024da3ff725977f886e04d0 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Thu, 21 Apr 2022 17:01:36 +0200 Subject: [PATCH 057/105] Adjust license in Fedora Spec according to LICENSE --- fedora/jellyfin.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 56e7476d208..6ac1daee4d5 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -10,7 +10,7 @@ Name: jellyfin Version: 10.8.0 Release: 1%{?dist} Summary: The Free Software Media System -License: GPLv3 +License: GPLv2 URL: https://jellyfin.org # Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%%{version}.tar.gz` Source0: jellyfin-server-%{version}.tar.gz From 0504ed9fe6bfd16c8cc29567557838e812ab9069 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Sat, 23 Apr 2022 12:50:36 +0200 Subject: [PATCH 058/105] Standardise and cleanup Fedora build * Remove additional dotnet-preview repo from Makefile * Repo doesn't seem maintained, and maintainers actively discourage the usage in production in its description * Considering this, we should build with stable .NET releases, which Fedora and RHEL 8+ repos already provide * Use Fedora-version-specific runtime-identifier for `dotnet publish` * This has no actual effect right now judging by the [RID catalog](https://github.com/dotnet/docs/blob/3efd59151a7421172658a6fbcf88a0bd5fa7a92d/docs/core/rid-catalog.md), so this is just future proofing. * Remove AutoReqProv * There's rarely a reason to use this feature, this is not one of them * In the [proposal of this feature](https://fedoraproject.org/wiki/AutoReqProv_(draft)#Usage) it is stated that this is not to be used on packages with binaries in /usr/bin and others, which is the case in this package. * also didn't seem to work since we were still having dependency issues with implicit dependencies (see jellyfin/jellyfin#7471 ) * Removed DOTNET_SKIP_FIRST_TIME_EXPERIENCE as it is unused in .NET SDK * see dotnet/sdk#9945 * it's already merged for removal in future versions * Move building process `dotnet publish` to %build section * Also removed `--output` from this due to an outstanding bug on SDK's side. This also separates building and installing as intended * define LICENSE as %license, which automatically puts it in a standardised directory --- fedora/Makefile | 5 ----- fedora/jellyfin.spec | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/fedora/Makefile b/fedora/Makefile index 6f9cf47190e..3188cf60392 100644 --- a/fedora/Makefile +++ b/fedora/Makefile @@ -7,17 +7,12 @@ SRPM := jellyfin-$(subst -,~,$(VERSION))-$(RELEASE)$(shell rpm --eval %dist). TARBALL :=$(NAME)-$(subst -,~,$(VERSION)).tar.gz epel-7-x86_64_repos := https://packages.microsoft.com/rhel/7/prod/ -epel-8-x86_64_repos := https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ fed_ver := $(shell rpm -E %fedora) # fallback when not running on Fedora fed_ver ?= 36 TARGET ?= fedora-$(fed_ver)-x86_64 -ifeq ($(findstring fedora,$(TARGET)),fedora) -$(TARGET)_repos := https://download.copr.fedorainfracloud.org/results/@dotnet-sig/dotnet-preview/$(TARGET)/ -endif - outdir ?= $(PWD)/$(DIR)/ srpm: $(DIR)/$(SRPM) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 6ac1daee4d5..586c95c0776 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -1,7 +1,7 @@ %global debug_package %{nil} # Set the dotnet runtime %if 0%{?fedora} -%global dotnet_runtime fedora-x64 +%global dotnet_runtime fedora.%{fedora}-x64 %else %global dotnet_runtime centos-x64 %endif @@ -25,13 +25,10 @@ Source17: jellyfin-server-lowports.conf %{?systemd_requires} BuildRequires: systemd BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel -# Requirements not packaged in main repos -# COPR @dotnet-sig/dotnet or +# Requirements not packaged in RHEL 7 main repos, added via Makefile # https://packages.microsoft.com/rhel/7/prod/ BuildRequires: dotnet-runtime-6.0, dotnet-sdk-6.0 Requires: %{name}-server = %{version}-%{release}, %{name}-web = %{version}-%{release} -# Disable Automatic Dependency Processing -AutoReqProv: no %description Jellyfin is a free software media system that puts you in control of managing and streaming your media. @@ -60,14 +57,17 @@ the Jellyfin server to bind to ports 80 and/or 443 for example. %autosetup -n jellyfin-server-%{version} -b 0 %build +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export PATH=$PATH:/usr/local/bin +# cannot use --output due to https://github.com/dotnet/sdk/issues/22220 +dotnet publish --configuration Release --self-contained --runtime %{dotnet_runtime} \ + "-p:DebugSymbols=false;DebugType=none" Jellyfin.Server + %install -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export PATH=$PATH:/usr/local/bin -dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \ - "-p:DebugSymbols=false;DebugType=none" Jellyfin.Server -%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE +%{__mkdir} -p %{buildroot}%{_libdir}/%{name} %{buildroot}%{_bindir} +%{__cp} -r Jellyfin.Server/bin/Release/net6.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/%{name} + %{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf %{__install} -D -m 0644 %{SOURCE17} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf %{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json @@ -106,7 +106,7 @@ EOF %attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin %attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin %attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin -%{_datadir}/licenses/jellyfin/LICENSE +%license LICENSE %files server-lowports %{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf From 19ccf414acd4d7e867b4877cd6379a3fc8e725d2 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Mon, 25 Apr 2022 05:45:36 +0200 Subject: [PATCH 059/105] Fedora build: Filter dependency to fix F36 build * fixes #7471 * Filter for F36+ since we don't know when (if ever) this issue will be fixed (it's up to .NET SDK). If it's ever fixed we can adjust or remove this condition. --- fedora/jellyfin.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 586c95c0776..ae0c96f103e 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -30,6 +30,12 @@ BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, BuildRequires: dotnet-runtime-6.0, dotnet-sdk-6.0 Requires: %{name}-server = %{version}-%{release}, %{name}-web = %{version}-%{release} +# Temporary (hopefully?) fix for https://github.com/jellyfin/jellyfin/issues/7471 +%if 0%{?fedora} >= 36 +%global __requires_exclude ^liblttng-ust\\.so\\.0.*$ +%endif + + %description Jellyfin is a free software media system that puts you in control of managing and streaming your media. @@ -108,6 +114,7 @@ EOF %attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin %license LICENSE + %files server-lowports %{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf From 9b805c9e83f69e97fe560ae060f371b74b8ebd00 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Mon, 25 Apr 2022 08:04:31 +0200 Subject: [PATCH 060/105] Use Fedora 36 image in Fedora Docker builds * fixes #7504 --- deployment/Dockerfile.fedora.amd64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 4308d86b46c..853a76303d2 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -1,4 +1,4 @@ -FROM fedora:33 +FROM fedora:36 # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist From f336647d57ddb55801ab1c85c6942981ad4bc95f Mon Sep 17 00:00:00 2001 From: Tarulia Date: Mon, 25 Apr 2022 22:00:15 +0200 Subject: [PATCH 061/105] Remove env-var after moving web-files on Fedora * when running Jellyfin as a user from a terminal without passing arguments, it would not find the web-files. They were moved in the JF-web build for Fedora, so this env-var would point to an invalid location. By removing it, JF now checks the default location. * fixes jellyfin/jellyfin-web#2059 --- fedora/jellyfin.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fedora/jellyfin.env b/fedora/jellyfin.env index 56b7a3558db..89cad1a2d3f 100644 --- a/fedora/jellyfin.env +++ b/fedora/jellyfin.env @@ -21,7 +21,7 @@ JELLYFIN_LOG_DIR="/var/log/jellyfin" JELLYFIN_CACHE_DIR="/var/cache/jellyfin" # web client path, installed by the jellyfin-web package -JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin-web" +# JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin-web" # In-App service control JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" From 5912a49d1d01855be0953ee25a2eb87abf2f9157 Mon Sep 17 00:00:00 2001 From: Tarulia Date: Tue, 26 Apr 2022 00:19:17 +0200 Subject: [PATCH 062/105] Further cleanup of Fedora spec-file * Replaced shell script used to start JF with a symlink, since it didn't do anything else. * Sorted installs by category/path to make it easier to maintain the spec. * Defined `%defattr` in file section so we don't need to set it on a by-file level. This also helps with the messed up default file permissions `dotnet publish` produces. * Removed some duplicate attribute definitions in `%install` and `%files` sections. They are now all in the `%install` section because of `%defattr` being specified and overriding them otherwise. --- fedora/jellyfin.spec | 64 +++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index ae0c96f103e..366cd74d4c1 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -62,6 +62,7 @@ the Jellyfin server to bind to ports 80 and/or 443 for example. %prep %autosetup -n jellyfin-server-%{version} -b 0 + %build export DOTNET_CLI_TELEMETRY_OPTOUT=1 export PATH=$PATH:/usr/local/bin @@ -71,47 +72,62 @@ dotnet publish --configuration Release --self-contained --runtime %{dotnet_runti %install -%{__mkdir} -p %{buildroot}%{_libdir}/%{name} %{buildroot}%{_bindir} -%{__cp} -r Jellyfin.Server/bin/Release/net6.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/%{name} +# Jellyfin files +%{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir} +%{__cp} -r Jellyfin.Server/bin/Release/net6.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin +ln -srf %{_libdir}/jellyfin/jellyfin %{buildroot}%{_bindir}/jellyfin +%{__install} -D %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh -%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf -%{__install} -D -m 0644 %{SOURCE17} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf -%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json -%{__mkdir} -p %{buildroot}%{_bindir} -tee %{buildroot}%{_bindir}/jellyfin << EOF -#!/bin/sh -exec %{_libdir}/jellyfin/jellyfin \${@} -EOF +# Jellyfin config +%{__install} -D Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json +%{__install} -D %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/jellyfin + +# system config +%{__install} -D %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml +%{__install} -D %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/jellyfin-sudoers +%{__install} -D %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf +%{__install} -D %{SOURCE11} %{buildroot}%{_unitdir}/jellyfin.service + +# empty directories %{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin %{__mkdir} -p %{buildroot}%{_sysconfdir}/jellyfin -%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin %{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin +%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin + +# jellyfin-server-lowports subpackage +%{__install} -D -m 0644 %{SOURCE17} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf -%{__install} -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/jellyfin.service -%{__install} -D -m 0644 %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/jellyfin -%{__install} -D -m 0600 %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/jellyfin-sudoers -%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh -%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml %files # empty as this is just a meta-package %files server -%attr(755,root,root) %{_bindir}/jellyfin -%{_libdir}/jellyfin/* +%defattr(644,root,root,755) + +# Jellyfin files +%{_bindir}/jellyfin # Needs 755 else only root can run it since binary build by dotnet is 722 +%attr(755,root,root) %{_libdir}/jellyfin/createdump %attr(755,root,root) %{_libdir}/jellyfin/jellyfin -%{_unitdir}/jellyfin.service -%{_libexecdir}/jellyfin/restart.sh -%{_prefix}/lib/firewalld/services/jellyfin.xml -%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/jellyfin +%{_libdir}/jellyfin/* +%attr(755,root,root) %{_libexecdir}/jellyfin/restart.sh + +# Jellyfin config +%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/jellyfin/logging.json %config %{_sysconfdir}/sysconfig/jellyfin + +# system config +%{_prefix}/lib/firewalld/services/jellyfin.xml +%{_unitdir}/jellyfin.service %config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/jellyfin-sudoers %config(noreplace) %{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf -%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/jellyfin/logging.json + +# empty directories %attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin -%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin +%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/jellyfin %attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin +%attr(-, jellyfin,jellyfin) %dir %{_var}/log/jellyfin + %license LICENSE From 3491f0968b13d780ea37ad0d63c2d82d8166eb92 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 16 Jun 2022 12:22:46 +0200 Subject: [PATCH 063/105] feat: partially handle SVG files and remove exceptions from blurhash and dimensions --- Emby.Drawing/ImageProcessor.cs | 71 ++++++++++--------- .../Library/LibraryManager.cs | 13 ++-- .../Jellyfin.Drawing.Skia.csproj | 1 + Jellyfin.Drawing.Skia/SkiaEncoder.cs | 48 ++++++++++--- Jellyfin.Drawing.Skia/SkiaHelper.cs | 13 ---- .../Drawing/IImageProcessor.cs | 8 +++ 6 files changed, 92 insertions(+), 62 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 18b41396464..f1aa6531f54 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -395,7 +395,13 @@ namespace Emby.Drawing public string GetImageBlurHash(string path) { var size = GetImageDimensions(path); - if (size.Width <= 0 || size.Height <= 0) + return GetImageBlurHash(path, size); + } + + /// + public string GetImageBlurHash(string path, ImageDimensions imageDimensions) + { + if (imageDimensions.Width <= 0 || imageDimensions.Height <= 0) { return string.Empty; } @@ -403,8 +409,8 @@ namespace Emby.Drawing // We want tiles to be as close to square as possible, and to *mostly* keep under 16 tiles for performance. // One tile is (width / xComp) x (height / yComp) pixels, which means that ideally yComp = xComp * height / width. // See more at https://github.com/woltapp/blurhash/#how-do-i-pick-the-number-of-x-and-y-components - float xCompF = MathF.Sqrt(16.0f * size.Width / size.Height); - float yCompF = xCompF * size.Height / size.Width; + float xCompF = MathF.Sqrt(16.0f * imageDimensions.Width / imageDimensions.Height); + float yCompF = xCompF * imageDimensions.Height / imageDimensions.Width; int xComp = Math.Min((int)xCompF + 1, 9); int yComp = Math.Min((int)yCompF + 1, 9); @@ -441,9 +447,7 @@ namespace Emby.Drawing private async Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) { - var inputFormat = Path.GetExtension(originalImagePath) - .TrimStart('.') - .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase); + var inputFormat = Path.GetExtension(originalImagePath.AsSpan()).TrimStart('.').ToString(); // These are just jpg files renamed as tbn if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase)) @@ -451,33 +455,34 @@ namespace Emby.Drawing return (originalImagePath, dateModified); } - if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat)) - { - try - { - string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); - - string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png"; - var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); - - var file = _fileSystem.GetFileInfo(outputPath); - if (!file.Exists) - { - await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); - dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath); - } - else - { - dateModified = file.LastWriteTimeUtc; - } - - originalImagePath = outputPath; - } - catch (Exception ex) - { - _logger.LogError(ex, "Image conversion failed for {Path}", originalImagePath); - } - } + // TODO _mediaEncoder.ConvertImage is not implemented + // if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat)) + // { + // try + // { + // string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); + // + // string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png"; + // var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); + // + // var file = _fileSystem.GetFileInfo(outputPath); + // if (!file.Exists) + // { + // await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); + // dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath); + // } + // else + // { + // dateModified = file.LastWriteTimeUtc; + // } + // + // originalImagePath = outputPath; + // } + // catch (Exception ex) + // { + // _logger.LogError(ex, "Image conversion failed for {Path}", originalImagePath); + // } + // } return (originalImagePath, dateModified); } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index d6754ad4a87..c54945c93be 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1860,7 +1860,9 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(item)); } - var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray(); + var outdated = forceUpdate + ? item.ImageInfos.Where(i => i.Path != null).ToArray() + : item.ImageInfos.Where(ImageNeedsRefresh).ToArray(); // Skip image processing if current or live tv source if (outdated.Length == 0 || item.SourceType != SourceType.Library) { @@ -1883,7 +1885,7 @@ namespace Emby.Server.Implementations.Library _logger.LogWarning("Cannot get image index for {ImagePath}", img.Path); continue; } - catch (Exception ex) when (ex is InvalidOperationException || ex is IOException) + catch (Exception ex) when (ex is InvalidOperationException or IOException) { _logger.LogWarning(ex, "Cannot fetch image from {ImagePath}", img.Path); continue; @@ -1895,23 +1897,24 @@ namespace Emby.Server.Implementations.Library } } + ImageDimensions size; try { - ImageDimensions size = _imageProcessor.GetImageDimensions(item, image); + size = _imageProcessor.GetImageDimensions(item, image); image.Width = size.Width; image.Height = size.Height; } catch (Exception ex) { _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path); + size = new ImageDimensions(0, 0); image.Width = 0; image.Height = 0; - continue; } try { - image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path); + image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path, size); } catch (Exception ex) { diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 5d645bddfe1..2f67ea3fa1d 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -20,6 +20,7 @@ + diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 2358fe6238a..68752823184 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -10,7 +10,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.Drawing; using Microsoft.Extensions.Logging; using SkiaSharp; -using static Jellyfin.Drawing.Skia.SkiaHelper; +using SKSvg = SkiaSharp.Extended.Svg.SKSvg; namespace Jellyfin.Drawing.Skia { @@ -19,8 +19,7 @@ namespace Jellyfin.Drawing.Skia /// public class SkiaEncoder : IImageEncoder { - private static readonly HashSet _transparentImageTypes - = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; + private static readonly HashSet _transparentImageTypes = new(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -71,7 +70,7 @@ namespace Jellyfin.Drawing.Skia /// public IReadOnlyCollection SupportedOutputFormats - => new HashSet() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; + => new HashSet { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; /// /// Check if the native lib is available. @@ -109,9 +108,7 @@ namespace Jellyfin.Drawing.Skia } /// - /// The path is null. /// The path is not valid. - /// The file at the specified path could not be used to generate a codec. public ImageDimensions GetImageSize(string path) { if (!File.Exists(path)) @@ -119,12 +116,27 @@ namespace Jellyfin.Drawing.Skia throw new FileNotFoundException("File not found", path); } + var extension = Path.GetExtension(path.AsSpan()); + if (extension.Equals(".svg", StringComparison.OrdinalIgnoreCase)) + { + var svg = new SKSvg(); + svg.Load(path); + return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height)); + } + using var codec = SKCodec.Create(path, out SKCodecResult result); - EnsureSuccess(result); - - var info = codec.Info; - - return new ImageDimensions(info.Width, info.Height); + switch (result) + { + case SKCodecResult.Success: + var info = codec.Info; + return new ImageDimensions(info.Width, info.Height); + case SKCodecResult.Unimplemented: + _logger.LogDebug("Image format not supported: {FilePath}", path); + return new ImageDimensions(0, 0); + default: + _logger.LogError("Unable to determine image dimensions for {FilePath}: {SkCodecResult}", path, result); + return new ImageDimensions(0, 0); + } } /// @@ -138,6 +150,13 @@ namespace Jellyfin.Drawing.Skia throw new ArgumentNullException(nameof(path)); } + var extension = Path.GetExtension(path.AsSpan()).TrimStart('.'); + if (!SupportedInputFormats.Contains(extension, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Unable to compute blur hash due to unsupported format: {ImagePath}", path); + return string.Empty; + } + // Any larger than 128x128 is too slow and there's no visually discernible difference return BlurHashEncoder.Encode(xComp, yComp, path, 128, 128); } @@ -378,6 +397,13 @@ namespace Jellyfin.Drawing.Skia throw new ArgumentException("String can't be empty.", nameof(outputPath)); } + var inputFormat = Path.GetExtension(inputPath.AsSpan()).TrimStart('.'); + if (!SupportedInputFormats.Contains(inputFormat, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Unable to encode image due to unsupported format: {ImagePath}", inputPath); + return inputPath; + } + var skiaOutputFormat = GetImageFormat(outputFormat); var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor); diff --git a/Jellyfin.Drawing.Skia/SkiaHelper.cs b/Jellyfin.Drawing.Skia/SkiaHelper.cs index 35dcebdaba3..c001c32b8c6 100644 --- a/Jellyfin.Drawing.Skia/SkiaHelper.cs +++ b/Jellyfin.Drawing.Skia/SkiaHelper.cs @@ -8,19 +8,6 @@ namespace Jellyfin.Drawing.Skia /// public static class SkiaHelper { - /// - /// Ensures the result is a success - /// by throwing an exception when that's not the case. - /// - /// The result returned by Skia. - public static void EnsureSuccess(SKCodecResult result) - { - if (result != SKCodecResult.Success) - { - throw new SkiaCodecException(result); - } - } - /// /// Gets the next valid image as a bitmap. /// diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 03882a0b97e..e5ce0aa2102 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -50,6 +50,14 @@ namespace MediaBrowser.Controller.Drawing /// BlurHash. string GetImageBlurHash(string path); + /// + /// Gets the blurhash of the image. + /// + /// Path to the image file. + /// The image dimensions. + /// BlurHash. + string GetImageBlurHash(string path, ImageDimensions imageDimensions); + /// /// Gets the image cache tag. /// From c4051ac16dc9a2eb98843fbb17db0cb002c479bf Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 16 Jun 2022 12:31:50 +0200 Subject: [PATCH 064/105] fix release build --- Emby.Drawing/ImageProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index f1aa6531f54..11256dafde1 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -445,14 +445,14 @@ namespace Emby.Drawing .ToString("N", CultureInfo.InvariantCulture); } - private async Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) + private Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) { var inputFormat = Path.GetExtension(originalImagePath.AsSpan()).TrimStart('.').ToString(); // These are just jpg files renamed as tbn if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase)) { - return (originalImagePath, dateModified); + return Task.FromResult((originalImagePath, dateModified)); } // TODO _mediaEncoder.ConvertImage is not implemented @@ -484,7 +484,7 @@ namespace Emby.Drawing // } // } - return (originalImagePath, dateModified); + return Task.FromResult((originalImagePath, dateModified)); } /// From be72001ff91d61a51b5746e5686d806308f0aa39 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 16 Jun 2022 21:32:54 +0800 Subject: [PATCH 065/105] Add VideoRangeType to video conditions This is used to distinguish whether the client supports specific VideoRangeType, such as SDR, HDR10, HLG and DOVI. Usage is similar to Video Profile condition. --- Emby.Dlna/Didl/DidlBuilder.cs | 2 + Emby.Dlna/PlayTo/PlayToController.cs | 1 + Jellyfin.Api/Helpers/StreamingHelpers.cs | 3 +- .../MediaEncoding/BaseEncodingJobOptions.cs | 6 ++ .../MediaEncoding/EncodingHelper.cs | 15 ++++ .../MediaEncoding/EncodingJobInfo.cs | 42 +++++++++++ MediaBrowser.Model/Dlna/ConditionProcessor.cs | 3 + .../Dlna/ContentFeatureBuilder.cs | 2 + MediaBrowser.Model/Dlna/DeviceProfile.cs | 4 +- .../Dlna/ProfileConditionValue.cs | 3 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 45 ++++++++++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 23 ++++++ MediaBrowser.Model/Entities/MediaStream.cs | 71 +++++++++++++------ MediaBrowser.Model/Session/TranscodeReason.cs | 1 + 14 files changed, 191 insertions(+), 30 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 6ab5843c153..df6539a5a61 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -221,6 +221,7 @@ namespace Emby.Dlna.Didl streamInfo.IsDirectStream, streamInfo.RunTimeTicks ?? 0, streamInfo.TargetVideoProfile, + streamInfo.TargetVideoRangeType, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate ?? 0, streamInfo.TargetPacketLength, @@ -376,6 +377,7 @@ namespace Emby.Dlna.Didl targetHeight, streamInfo.TargetVideoBitDepth, streamInfo.TargetVideoProfile, + streamInfo.TargetVideoRangeType, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate ?? 0, streamInfo.TargetPacketLength, diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index e27a8975b73..b73ce00b6f6 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -561,6 +561,7 @@ namespace Emby.Dlna.PlayTo streamInfo.IsDirectStream, streamInfo.RunTimeTicks ?? 0, streamInfo.TargetVideoProfile, + streamInfo.TargetVideoRangeType, streamInfo.TargetVideoLevel, streamInfo.TargetFramerate ?? 0, streamInfo.TargetPacketLength, diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 34dab75b821..c96ca87a566 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -312,7 +312,7 @@ namespace Jellyfin.Api.Helpers responseHeaders.Add( "contentFeatures.dlna.org", - ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty); + ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoRangeType, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty); } } @@ -533,6 +533,7 @@ namespace Jellyfin.Api.Helpers state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetVideoProfile, + state.TargetVideoRangeType, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs index 462585ce354..fb4e7bd1f5b 100644 --- a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs @@ -75,6 +75,12 @@ namespace MediaBrowser.Controller.MediaEncoding /// The profile. public string Profile { get; set; } + /// + /// Gets or sets the video range type. + /// + /// The video range type. + public string VideoRangeType { get; set; } + /// /// Gets or sets the level. /// diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 04778e69933..e0a3069e7ca 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1753,6 +1753,21 @@ namespace MediaBrowser.Controller.MediaEncoding } } + var requestedRangeTypes = state.GetRequestedRangeTypes(videoStream.Codec); + if (requestedProfiles.Length > 0) + { + if (string.IsNullOrEmpty(videoStream.VideoRangeType)) + { + return false; + } + + if (!string.IsNullOrEmpty(videoStream.VideoRangeType) + && !requestedRangeTypes.Contains(videoStream.VideoRangeType, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + // Video width must fall within requested value if (request.MaxWidth.HasValue && (!videoStream.Width.HasValue || videoStream.Width.Value > request.MaxWidth.Value)) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 0824590f229..491662861f5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -366,6 +366,28 @@ namespace MediaBrowser.Controller.MediaEncoding } } + /// + /// Gets the target video range type. + /// + public string TargetVideoRangeType + { + get + { + if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) + { + return VideoStream?.VideoRangeType; + } + + var requestedRangeType = GetRequestedRangeTypes(ActualOutputVideoCodec).FirstOrDefault(); + if (!string.IsNullOrEmpty(requestedRangeType)) + { + return requestedRangeType; + } + + return null; + } + } + public string TargetVideoCodecTag { get @@ -579,6 +601,26 @@ namespace MediaBrowser.Controller.MediaEncoding return Array.Empty(); } + public string[] GetRequestedRangeTypes(string codec) + { + if (!string.IsNullOrEmpty(BaseRequest.VideoRangeType)) + { + return BaseRequest.VideoRangeType.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + if (!string.IsNullOrEmpty(codec)) + { + var rangetype = BaseRequest.GetOption(codec, "rangetype"); + + if (!string.IsNullOrEmpty(rangetype)) + { + return rangetype.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries); + } + } + + return Array.Empty(); + } + public string GetRequestedLevel(string codec) { if (!string.IsNullOrEmpty(BaseRequest.Level)) diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 8d03b4c0b33..5734224167b 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.Model.Dlna int? videoBitDepth, int? videoBitrate, string? videoProfile, + string? videoRangeType, double? videoLevel, float? videoFramerate, int? packetLength, @@ -42,6 +43,8 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, videoLevel); case ProfileConditionValue.VideoProfile: return IsConditionSatisfied(condition, videoProfile); + case ProfileConditionValue.VideoRangeType: + return IsConditionSatisfied(condition, videoRangeType); case ProfileConditionValue.VideoCodecTag: return IsConditionSatisfied(condition, videoCodecTag); case ProfileConditionValue.PacketLength: diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 6e129246b00..47c36494bdd 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -128,6 +128,7 @@ namespace MediaBrowser.Model.Dlna bool isDirectStream, long? runtimeTicks, string videoProfile, + string videoRangeType, double? videoLevel, float? videoFramerate, int? packetLength, @@ -176,6 +177,7 @@ namespace MediaBrowser.Model.Dlna bitDepth, videoBitrate, videoProfile, + videoRangeType, videoLevel, videoFramerate, packetLength, diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 6170ff5bd61..79ae9517086 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -423,6 +423,7 @@ namespace MediaBrowser.Model.Dlna /// The bit depth. /// The video bitrate. /// The video profile. + /// The video range type. /// The video level. /// The video framerate. /// The packet length. @@ -444,6 +445,7 @@ namespace MediaBrowser.Model.Dlna int? bitDepth, int? videoBitrate, string videoProfile, + string videoRangeType, double? videoLevel, float? videoFramerate, int? packetLength, @@ -483,7 +485,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; foreach (ProfileCondition c in i.Conditions) { - if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { anyOff = true; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index eb81fde751c..a32433e185c 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Model.Dlna IsAvc = 20, IsInterlaced = 21, AudioSampleRate = 22, - AudioBitDepth = 23 + AudioBitDepth = 23, + VideoRangeType = 24 } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index fe9ff2ebe8c..bfab185342d 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -221,6 +221,9 @@ namespace MediaBrowser.Model.Dlna case ProfileConditionValue.VideoProfile: return TranscodeReason.VideoProfileNotSupported; + case ProfileConditionValue.VideoRangeType: + return TranscodeReason.VideoRangeTypeNotSupported; + case ProfileConditionValue.VideoTimestamp: // TODO return 0; @@ -748,9 +751,9 @@ namespace MediaBrowser.Model.Dlna var appliedVideoConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container) && - i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC))) + i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC))) .Select(i => - i.Conditions.All(condition => ConditionProcessor.IsVideoConditionSatisfied(condition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC))); + i.Conditions.All(condition => ConditionProcessor.IsVideoConditionSatisfied(condition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC))); // An empty appliedVideoConditions means that the codec has no conditions for the current video stream var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied); @@ -834,6 +837,7 @@ namespace MediaBrowser.Model.Dlna int? videoBitrate = videoStream?.BitRate; double? videoLevel = videoStream?.Level; string videoProfile = videoStream?.Profile; + string videoRangeType = videoStream?.VideoRangeType; float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; bool? isAnamorphic = videoStream?.IsAnamorphic; bool? isInterlaced = videoStream?.IsInterlaced; @@ -850,7 +854,7 @@ namespace MediaBrowser.Model.Dlna var appliedVideoConditions = options.Profile.CodecProfiles .Where(i => i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container) && - i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))); + i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))); var isFirstAppliedCodecProfile = true; foreach (var i in appliedVideoConditions) { @@ -1081,6 +1085,7 @@ namespace MediaBrowser.Model.Dlna int? videoBitrate = videoStream?.BitRate; double? videoLevel = videoStream?.Level; string videoProfile = videoStream?.Profile; + string videoRangeType = videoStream?.VideoRangeType; float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; bool? isAnamorphic = videoStream?.IsAnamorphic; bool? isInterlaced = videoStream?.IsInterlaced; @@ -1098,7 +1103,7 @@ namespace MediaBrowser.Model.Dlna int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); var checkVideoConditions = (ProfileCondition[] conditions) => - conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)); + conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)); // Check container conditions var containerProfileReasons = AggregateFailureConditions( @@ -1852,6 +1857,38 @@ namespace MediaBrowser.Model.Dlna break; } + case ProfileConditionValue.VideoRangeType: + { + if (string.IsNullOrEmpty(qualifier)) + { + continue; + } + + // change from split by | to comma + // strip spaces to avoid having to encode + var values = value + .Split('|', StringSplitOptions.RemoveEmptyEntries); + + if (condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "rangetype", string.Join(',', values)); + } + else if (condition.Condition == ProfileConditionType.EqualsAny) + { + var currentValue = item.GetOption(qualifier, "rangetype"); + if (!string.IsNullOrEmpty(currentValue) && values.Any(value => value == currentValue)) + { + item.SetOption(qualifier, "rangetype", currentValue); + } + else + { + item.SetOption(qualifier, "rangetype", string.Join(',', values)); + } + } + + break; + } + case ProfileConditionValue.Height: { if (!enableNonQualifiedConditions) diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index da089602f1c..0c66351c70d 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -280,6 +280,29 @@ namespace MediaBrowser.Model.Dlna } } + /// + /// Gets the target video range type that will be in the output stream. + /// + public string TargetVideoRangeType + { + get + { + if (IsDirectStream) + { + return TargetVideoStream?.VideoRangeType; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetOption(videoCodec, "rangetype"); + } + + return TargetVideoStream?.VideoRangeType; + } + } + /// /// Gets the target video codec tag. /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 96b48ca52aa..46a481092e1 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -104,32 +104,23 @@ namespace MediaBrowser.Model.Entities { get { - if (Type != MediaStreamType.Video) - { - return null; - } + var (videoRange, videoRangeType) = getVideoColorRange(); - var colorTransfer = ColorTransfer; + return videoRange; + } + } - if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) - || string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) - { - return "HDR"; - } + /// + /// Gets the video range type. + /// + /// The video range type. + public string VideoRangeType + { + get + { + var (videoRange, videoRangeType) = getVideoColorRange(); - // For some Dolby Vision files, no color transfer is provided, so check the codec - - var codecTag = CodecTag; - - if (string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase) - || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase) - || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase) - || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase)) - { - return "HDR"; - } - - return "SDR"; + return videoRangeType; } } @@ -571,5 +562,39 @@ namespace MediaBrowser.Model.Entities return true; } + + public (string VideoRange, string VideoRangeType) getVideoColorRange() + { + if (Type != MediaStreamType.Video) + { + return (null, null); + } + + var colorTransfer = ColorTransfer; + + if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)) + { + return ("HDR", "HDR10"); + } + + if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) + { + return ("HDR", "HLG"); + } + + // For some Dolby Vision files, no color transfer is provided, so check the codec + + var codecTag = CodecTag; + + if (string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase) + || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase) + || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase) + || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase)) + { + return ("HDR", "DOVI"); + } + + return ("SDR", "SDR"); + } } } diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs index 9da9f3323b3..bbdf4536b7c 100644 --- a/MediaBrowser.Model/Session/TranscodeReason.cs +++ b/MediaBrowser.Model/Session/TranscodeReason.cs @@ -17,6 +17,7 @@ namespace MediaBrowser.Model.Session // Video Constraints VideoProfileNotSupported = 1 << 6, + VideoRangeTypeNotSupported = 1 << 24, VideoLevelNotSupported = 1 << 7, VideoResolutionNotSupported = 1 << 8, VideoBitDepthNotSupported = 1 << 9, From 477b922e4a1d74f4af91d2561c9f30d527489105 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 16 Jun 2022 22:11:06 +0800 Subject: [PATCH 066/105] Apply suggestions from code review Co-authored-by: Cody Robibero --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 +-- MediaBrowser.Model/Dlna/StreamBuilder.cs | 4 ++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +- MediaBrowser.Model/Entities/MediaStream.cs | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e0a3069e7ca..d5b0aca3c35 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1761,8 +1761,7 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - if (!string.IsNullOrEmpty(videoStream.VideoRangeType) - && !requestedRangeTypes.Contains(videoStream.VideoRangeType, StringComparison.OrdinalIgnoreCase)) + if (!requestedRangeTypes.Contains(videoStream.VideoRangeType, StringComparison.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bfab185342d..fdb9fd5d54f 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1867,7 +1867,7 @@ namespace MediaBrowser.Model.Dlna // change from split by | to comma // strip spaces to avoid having to encode var values = value - .Split('|', StringSplitOptions.RemoveEmptyEntries); + .Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (condition.Condition == ProfileConditionType.Equals) { @@ -1876,7 +1876,7 @@ namespace MediaBrowser.Model.Dlna else if (condition.Condition == ProfileConditionType.EqualsAny) { var currentValue = item.GetOption(qualifier, "rangetype"); - if (!string.IsNullOrEmpty(currentValue) && values.Any(value => value == currentValue)) + if (!string.IsNullOrEmpty(currentValue) && values.Any(v => string.Equals(v, currentValue, StringComparison.OrdinalIgnoreCase))) { item.SetOption(qualifier, "rangetype", currentValue); } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 0c66351c70d..4fcc54a0384 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -293,7 +293,7 @@ namespace MediaBrowser.Model.Dlna } var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + var videoCodec = targetVideoCodecs.FirstOrDefault(); if (!string.IsNullOrEmpty(videoCodec)) { return GetOption(videoCodec, "rangetype"); diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 46a481092e1..48408e5847b 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -104,7 +104,7 @@ namespace MediaBrowser.Model.Entities { get { - var (videoRange, videoRangeType) = getVideoColorRange(); + var (videoRange, _) = GetVideoColorRange(); return videoRange; } @@ -118,7 +118,7 @@ namespace MediaBrowser.Model.Entities { get { - var (videoRange, videoRangeType) = getVideoColorRange(); + var (_, videoRangeType) = GetVideoColorRange(); return videoRangeType; } @@ -563,7 +563,7 @@ namespace MediaBrowser.Model.Entities return true; } - public (string VideoRange, string VideoRangeType) getVideoColorRange() + public (string VideoRange, string VideoRangeType) GetVideoColorRange() { if (Type != MediaStreamType.Video) { From f7813803c25fd0db73b3020972ab9b900396a895 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 16 Jun 2022 23:29:36 +0800 Subject: [PATCH 067/105] Brighter VPP tone-mapping on Intel --- .../MediaEncoding/EncodingHelper.cs | 14 +++++++++++++- .../Encoder/EncoderValidator.cs | 1 + .../Configuration/EncodingOptions.cs | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 04778e69933..3b0b8426678 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -125,6 +125,7 @@ namespace MediaBrowser.Controller.MediaEncoding && _mediaEncoder.SupportsFilter("scale_vaapi") && _mediaEncoder.SupportsFilter("deinterlace_vaapi") && _mediaEncoder.SupportsFilter("tonemap_vaapi") + && _mediaEncoder.SupportsFilter("procamp_vaapi") && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVaapiFrameSync) && _mediaEncoder.SupportsFilter("hwupload_vaapi"); } @@ -2704,7 +2705,18 @@ namespace MediaBrowser.Controller.MediaEncoding var args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709"; - if (!hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) + if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) + { + args += ",procamp_vaapi=b={2}:c={3}:extra_hw_frames=16"; + return string.Format( + CultureInfo.InvariantCulture, + args, + hwTonemapSuffix, + videoFormat ?? "nv12", + options.VppTonemappingBrightness, + options.VppTonemappingContrast); + } + else { args += ":tonemap={2}:peak={3}:desat={4}"; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 20d372d7aae..d378c6e1345 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -100,6 +100,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "scale_vaapi", "deinterlace_vaapi", "tonemap_vaapi", + "procamp_vaapi", "overlay_vaapi", "hwupload_vaapi" }; diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index fce0b6d6f65..73ebfba70ee 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -26,6 +26,8 @@ namespace MediaBrowser.Model.Configuration TonemappingThreshold = 0.8; TonemappingPeak = 100; TonemappingParam = 0; + VppTonemappingBrightness = 0; + VppTonemappingContrast = 1.2; H264Crf = 23; H265Crf = 28; DeinterlaceDoubleRate = false; @@ -89,6 +91,10 @@ namespace MediaBrowser.Model.Configuration public double TonemappingParam { get; set; } + public double VppTonemappingBrightness { get; set; } + + public double VppTonemappingContrast { get; set; } + public int H264Crf { get; set; } public int H265Crf { get; set; } From 3275f83c3bd11d28e3d9b525cf5d6b28216fe94f Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 17 Jun 2022 08:57:59 +0200 Subject: [PATCH 068/105] fix: use proper bind address for DLNA location url (#7953) --- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- Emby.Server.Implementations/ApplicationHost.cs | 12 +++++++----- MediaBrowser.Controller/IServerApplicationHost.cs | 4 +++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 2a535d55657..15021c19d68 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -313,7 +313,7 @@ namespace Emby.Dlna.Main _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address); - var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(false) + descriptorUri); + var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(address, false) + descriptorUri); var device = new SsdpRootDevice { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 82294644b88..f3aaef0778a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1114,13 +1114,13 @@ namespace Emby.Server.Implementations } /// - public string GetApiUrlForLocalAccess(bool allowHttps = true) + public string GetApiUrlForLocalAccess(IPObject hostname = null, bool allowHttps = true) { // With an empty source, the port will be null - string smart = NetManager.GetBindInterface(string.Empty, out _); + var smart = NetManager.GetBindInterface(hostname ?? IPHost.None, out _); var scheme = !allowHttps ? Uri.UriSchemeHttp : null; int? port = !allowHttps ? HttpPort : null; - return GetLocalApiUrl(smart.Trim('/'), scheme, port); + return GetLocalApiUrl(smart, scheme, port); } /// @@ -1134,11 +1134,13 @@ namespace Emby.Server.Implementations // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // not. For consistency, always trim the trailing slash. + scheme ??= ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; + var isHttps = scheme == Uri.UriSchemeHttps; return new UriBuilder { - Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), + Scheme = scheme, Host = hostname, - Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), + Port = port ?? (isHttps ? HttpsPort : HttpPort), Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl }.ToString().TrimEnd('/'); } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 75ec5f213f5..11afdc4aed9 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -4,6 +4,7 @@ using System.Net; using MediaBrowser.Common; +using MediaBrowser.Common.Net; using MediaBrowser.Model.System; using Microsoft.AspNetCore.Http; @@ -74,9 +75,10 @@ namespace MediaBrowser.Controller /// /// Gets an URL that can be used to access the API over LAN. /// + /// An optional hostname to use. /// A value indicating whether to allow HTTPS. /// The API URL. - string GetApiUrlForLocalAccess(bool allowHttps = true); + string GetApiUrlForLocalAccess(IPObject hostname = null, bool allowHttps = true); /// /// Gets a local (LAN) URL that can be used to access the API. From f020bd6f3baf58cf37f798551d2f1a28133fe2a0 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 17 Jun 2022 09:00:59 +0200 Subject: [PATCH 069/105] use ignore case for scheme comparison --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f3aaef0778a..32289625fe2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1135,7 +1135,7 @@ namespace Emby.Server.Implementations // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // not. For consistency, always trim the trailing slash. scheme ??= ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp; - var isHttps = scheme == Uri.UriSchemeHttps; + var isHttps = string.Equals(scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); return new UriBuilder { Scheme = scheme, From 0b6fbebf72d650fe6708f298ce7a45663c9c833d Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 17 Jun 2022 16:49:16 +0800 Subject: [PATCH 070/105] Apply suggestions from code review Co-authored-by: Claus Vium --- MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 4fcc54a0384..0c66351c70d 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -293,7 +293,7 @@ namespace MediaBrowser.Model.Dlna } var targetVideoCodecs = TargetVideoCodec; - var videoCodec = targetVideoCodecs.FirstOrDefault(); + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; if (!string.IsNullOrEmpty(videoCodec)) { return GetOption(videoCodec, "rangetype"); From ae22d0b7a530c1ae3188c2ec32bd03dc840c1762 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 18 Jun 2022 00:25:55 +0800 Subject: [PATCH 071/105] Fix output extension if user has no transcoding permission --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 34dab75b821..54234c56a39 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Helpers { containerInternal = streamingRequest.Static ? StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, null, DlnaProfileType.Audio) - : GetOutputFileExtension(state); + : GetOutputFileExtension(state, mediaSource); } state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.'); @@ -235,7 +235,7 @@ namespace Jellyfin.Api.Helpers ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static); var ext = string.IsNullOrWhiteSpace(state.OutputContainer) - ? GetOutputFileExtension(state) + ? GetOutputFileExtension(state, mediaSource) : ("." + state.OutputContainer); state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId); @@ -409,8 +409,9 @@ namespace Jellyfin.Api.Helpers /// Gets the output file extension. /// /// The state. + /// The mediaSource. /// System.String. - private static string? GetOutputFileExtension(StreamState state) + private static string? GetOutputFileExtension(StreamState state, MediaSourceInfo? mediaSource) { var ext = Path.GetExtension(state.RequestedUrl); @@ -425,7 +426,7 @@ namespace Jellyfin.Api.Helpers var videoCodec = state.Request.VideoCodec; if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) || - string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase)) + string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) { return ".ts"; } @@ -474,6 +475,17 @@ namespace Jellyfin.Api.Helpers } } + // Fallback to the container of mediaSource + if (mediaSource != null && !string.IsNullOrEmpty(mediaSource.Container)) + { + var containers = mediaSource.Container.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + if (containers.Length > 0) + { + return '.' + containers[0]; + } + } + return null; } From 56e135f5e664835a89e52d0ced91b9009b390818 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 18 Jun 2022 01:50:08 +0800 Subject: [PATCH 072/105] Apply suggestions from code review Co-authored-by: Claus Vium --- Jellyfin.Api/Helpers/StreamingHelpers.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 54234c56a39..87544b3e3ec 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -476,14 +476,10 @@ namespace Jellyfin.Api.Helpers } // Fallback to the container of mediaSource - if (mediaSource != null && !string.IsNullOrEmpty(mediaSource.Container)) + if (!string.IsNullOrEmpty(mediaSource?.Container)) { - var containers = mediaSource.Container.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - - if (containers.Length > 0) - { - return '.' + containers[0]; - } + var idx = mediaSource.Container.IndexOf(',', StringComparison.OrdinalIgnoreCase); + return '.' + (idx == -1 ? mediaSource.Container : mediaSource.Container[..idx]).Trim(); } return null; From a64e21f57a2998363fd1216b5d09d56cbdfefe55 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 17 Jun 2022 23:17:23 +0200 Subject: [PATCH 073/105] Fix subtitle encoder if subrip is requested --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 49bc2d775bb..49173b3d0a8 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -294,7 +294,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return true; } - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, "subrip", StringComparison.OrdinalIgnoreCase)) { value = new SrtWriter(); return true; From ef037ad37129607c5edaf5455e9911493e5a0293 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 18 Jun 2022 00:07:54 +0200 Subject: [PATCH 074/105] refactor: use season number and episode number for NextUp ordering instead of SortName --- .../TV/TVSeriesManager.cs | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 727b9d4b5c9..e78f031bbf3 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -135,20 +135,15 @@ namespace Emby.Server.Implementations.TV return GetResult(episodes, request); } - public IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) + private IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) { - // Avoid implicitly captured closure - var currentUser = user; - - var allNextUp = seriesKeys - .Select(i => GetNextUp(i, currentUser, dtoOptions, false)); + var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false)); if (request.EnableRewatching) { allNextUp = allNextUp.Concat( - seriesKeys.Select(i => GetNextUp(i, currentUser, dtoOptions, true)) - ) - .OrderByDescending(i => i.Item1); + seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, true))) + .OrderByDescending(i => i.LastWatchedDate); } // If viewing all next up for all series, remove first episodes @@ -161,23 +156,18 @@ namespace Emby.Server.Implementations.TV { if (request.DisableFirstEpisode) { - return i.Item1 != DateTime.MinValue; + return i.LastWatchedDate != DateTime.MinValue; } - if (alwaysEnableFirstEpisode || (i.Item1 != DateTime.MinValue && i.Item1.Date >= request.NextUpDateCutoff)) + if (alwaysEnableFirstEpisode || (i.LastWatchedDate != DateTime.MinValue && i.LastWatchedDate.Date >= request.NextUpDateCutoff)) { anyFound = true; return true; } - if (!anyFound && i.Item1 == DateTime.MinValue) - { - return true; - } - - return false; + return !anyFound && i.LastWatchedDate == DateTime.MinValue; }) - .Select(i => i.Item2()) + .Select(i => i.GetEpisodeFunction()) .Where(i => i != null); } @@ -195,14 +185,14 @@ namespace Emby.Server.Implementations.TV /// Gets the next up. /// /// Task{Episode}. - private Tuple> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) + private (DateTime LastWatchedDate, Func GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) { var lastQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Descending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, @@ -216,24 +206,23 @@ namespace Emby.Server.Implementations.TV if (rewatching) { // find last watched by date played, not by newest episode watched - lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }; + lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; } var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast().FirstOrDefault(); - Func getEpisode = () => + Episode GetEpisode() { var nextQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending) }, Limit = 1, IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, - MinSortName = lastWatchedEpisode?.SortName, DtoOptions = dtoOptions }; @@ -297,7 +286,7 @@ namespace Emby.Server.Implementations.TV } return nextEpisode; - }; + } if (lastWatchedEpisode != null) { @@ -305,11 +294,11 @@ namespace Emby.Server.Implementations.TV var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); - return new Tuple>(lastWatchedDate, getEpisode); + return (lastWatchedDate, GetEpisode); } // Return the first episode - return new Tuple>(DateTime.MinValue, getEpisode); + return (DateTime.MinValue, GetEpisode); } private static QueryResult GetResult(IEnumerable items, NextUpQuery query) From 24c56328f28f19e8442c7f4db85a89c78730b4c4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 18 Jun 2022 01:20:05 +0200 Subject: [PATCH 075/105] Add subrip to SubtitleFormat --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 2 +- MediaBrowser.Model/MediaInfo/SubtitleFormat.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 49173b3d0a8..c54cd37605e 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -294,7 +294,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return true; } - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, "subrip", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format,SubtitleFormat.SUBRIP, StringComparison.OrdinalIgnoreCase)) { value = new SrtWriter(); return true; diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs index 9bc5c31f621..85de9169405 100644 --- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs +++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs @@ -5,6 +5,7 @@ namespace MediaBrowser.Model.MediaInfo public static class SubtitleFormat { public const string SRT = "srt"; + public const string SUBRIP = "subrip"; public const string SSA = "ssa"; public const string ASS = "ass"; public const string VTT = "vtt"; From 6d5c697183df6bed4d2617c56de4350d00750e4d Mon Sep 17 00:00:00 2001 From: Tarulia Date: Sat, 18 Jun 2022 22:35:36 +0200 Subject: [PATCH 076/105] Add make in Fedora Docker install Fedora 36 doesn't seem to ship make, so add it manually. --- deployment/Dockerfile.fedora.amd64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 853a76303d2..0833cccc8fd 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -9,7 +9,7 @@ ENV IS_DOCKER=YES # Prepare Fedora environment RUN dnf update -yq \ - && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget + && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ From b60905f991e3fac3ad293addd2ceb417243e8934 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 18 Jun 2022 01:21:20 +0200 Subject: [PATCH 077/105] Add barebone ASS/SSA writers to SubtitleEncoder --- .../Subtitles/AssWriter.cs | 54 +++++++++++++++++++ .../Subtitles/SsaWriter.cs | 54 +++++++++++++++++++ .../Subtitles/SubtitleEncoder.cs | 12 +++++ 3 files changed, 120 insertions(+) create mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs new file mode 100644 index 00000000000..0d1cf6e258f --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using MediaBrowser.Model.MediaInfo; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// ASS subtitle writer. + /// + public class AssWriter : ISubtitleWriter + { + /// + public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) + { + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + var trackEvents = info.TrackEvents; + var timeFormat = @"hh\:mm\:ss\.ff"; + + // Write ASS header + writer.WriteLine("[Script Info]"); + writer.WriteLine("Title: Jellyfin transcoded ASS subtitle"); + writer.WriteLine("ScriptType: v4.00+"); + writer.WriteLine(); + writer.WriteLine("[V4+ Styles]"); + writer.WriteLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding"); + writer.WriteLine("Style: Default,Arial,20,&H00FFFFFF,&H00FFFFFF,&H19333333,&H910E0807,0,0,0,0,100,100,0,0,0,1,0,2,10,10,10,1"); + writer.WriteLine(); + writer.WriteLine("[Events]"); + writer.WriteLine("Format: Layer, Start, End, Style, Text"); + + for (int i = 0; i < trackEvents.Count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + var trackEvent = trackEvents[i]; + var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); + var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); + var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase); + + writer.WriteLine( + "Dialogue: 0,{0},{1},Default,{2}", + startTime, + endTime, + text); + } + } + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs new file mode 100644 index 00000000000..6761cd30992 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using MediaBrowser.Model.MediaInfo; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// SSA subtitle writer. + /// + public class SsaWriter : ISubtitleWriter + { + /// + public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) + { + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + var trackEvents = info.TrackEvents; + var timeFormat = @"hh\:mm\:ss\.ff"; + + // Write SSA header + writer.WriteLine("[Script Info]"); + writer.WriteLine("Title: Jellyfin transcoded SSA subtitle"); + writer.WriteLine("ScriptType: v4.00"); + writer.WriteLine(); + writer.WriteLine("[V4 Styles]"); + writer.WriteLine("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); + writer.WriteLine("Style: Default,Arial,20,&H00FFFFFF,&H00FFFFFF,&H19333333,&H19333333,0,0,0,1,0,2,10,10,10,0,1"); + writer.WriteLine(); + writer.WriteLine("[Events]"); + writer.WriteLine("Format: Layer, Start, End, Style, Text"); + + for (int i = 0; i < trackEvents.Count; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + var trackEvent = trackEvents[i]; + var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); + var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture); + var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase); + + writer.WriteLine( + "Dialogue: 0,{0},{1},Default,{2}", + startTime, + endTime, + text); + } + } + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index c54cd37605e..b9b8a89eba3 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -283,6 +283,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { + if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) + { + value = new AssWriter(); + return true; + } + if (string.IsNullOrEmpty(format)) { throw new ArgumentNullException(nameof(format)); @@ -300,6 +306,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles return true; } + if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) + { + value = new SsaWriter(); + return true; + } + if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase)) { value = new VttWriter(); From 73117b079ca2fb37065a5d1a131b0ee33432bfd6 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 19 Jun 2022 19:14:55 +0800 Subject: [PATCH 078/105] Fix HWA decoders are not applied to BluRay folders fixes #6834 --- .../MediaEncoding/EncodingHelper.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2a5018c0578..68fcb71ad83 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4317,14 +4317,18 @@ namespace MediaBrowser.Controller.MediaEncoding protected string GetHardwareVideoDecoder(EncodingJobInfo state, EncodingOptions options) { var videoStream = state.VideoStream; - if (videoStream == null) + var mediaSource = state.MediaSource; + if (videoStream == null || mediaSource == null) { return null; } - // Only use alternative encoders for video files. - var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile; - if (videoType != VideoType.VideoFile) + // HWA decoders can handle both video files and video folders. + var videoType = mediaSource.VideoType; + if (videoType != VideoType.VideoFile + && videoType != VideoType.Iso + && videoType != VideoType.Dvd + && videoType != VideoType.BluRay) { return null; } From bdd52df230493c6f362487a2bf0c58cff67c5f96 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 20 Jun 2022 18:59:54 +0800 Subject: [PATCH 079/105] Override the VAAPI driver env if i965 device is known --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 2a5018c0578..3a6cb754502 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -714,6 +714,9 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (_mediaEncoder.IsVaapiDeviceInteli965) { + // Only override i965 since it has lower priority than iHD in libva lookup. + Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME", "i965"); + Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME_JELLYFIN", "i965"); args.Append(GetVaapiDeviceArgs(null, "i965", null, VaapiAlias)); } else From ec2ad4ec8c42ea1f5b2d078e66a670c8479012b4 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 20 Jun 2022 08:48:40 -0600 Subject: [PATCH 080/105] Upgrade to dotnet 6.0.6 --- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 43d1c387858..5b1523576bf 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index f6f370fbf33..249dc400a10 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 8057c25dae9..0fd01bea7dc 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index f1bfaec7fa6..9b8bf718714 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 8637fcc0711..1122d842230 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -40,7 +40,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 2d574fa74cc..20847fd258c 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 4308d86b46c..64ee201eea1 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 7143a39f3e5..34ef0c20de0 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 625b0791ef1..f3a7de56d7a 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index c457aa0bbe7..fa21daf6696 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/dc930bff-ef3d-4f6f-8799-6eb60390f5b4/1efee2a8ea0180c94aff8f15eb3af981/dotnet-sdk-6.0.300-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index f9e9a0ff382..a6b8888fce8 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,7 +15,7 @@ - + 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 e8b336c09b8..98fd1a03fa0 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index f5226c5cae2..e8cdf0434d7 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From 9898c108800df314f842711d5d26e9a9bab0db6a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 20 Jun 2022 08:52:30 -0600 Subject: [PATCH 081/105] Update remaining dependencies --- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 5b1523576bf..f91ac5e3e8d 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,7 @@ - + diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 2f67ea3fa1d..ceaa3fc9c06 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -18,8 +18,8 @@ - - + + From 34785542492d2db67eb82e1dfc2b141f2f9b5b2a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 20 Jun 2022 08:54:46 -0600 Subject: [PATCH 082/105] Fix build --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index b9b8a89eba3..7091af734a7 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -300,7 +300,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return true; } - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format,SubtitleFormat.SUBRIP, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, SubtitleFormat.SUBRIP, StringComparison.OrdinalIgnoreCase)) { value = new SrtWriter(); return true; From 7884e7e829f7c24260bc221366c85f5140c788af Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 20 Jun 2022 08:55:19 -0600 Subject: [PATCH 083/105] Revert "refactor: use season number and episode number for NextUp ordering instead of SortName" --- .../TV/TVSeriesManager.cs | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index e78f031bbf3..727b9d4b5c9 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -135,15 +135,20 @@ namespace Emby.Server.Implementations.TV return GetResult(episodes, request); } - private IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) + public IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) { - var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false)); + // Avoid implicitly captured closure + var currentUser = user; + + var allNextUp = seriesKeys + .Select(i => GetNextUp(i, currentUser, dtoOptions, false)); if (request.EnableRewatching) { allNextUp = allNextUp.Concat( - seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, true))) - .OrderByDescending(i => i.LastWatchedDate); + seriesKeys.Select(i => GetNextUp(i, currentUser, dtoOptions, true)) + ) + .OrderByDescending(i => i.Item1); } // If viewing all next up for all series, remove first episodes @@ -156,18 +161,23 @@ namespace Emby.Server.Implementations.TV { if (request.DisableFirstEpisode) { - return i.LastWatchedDate != DateTime.MinValue; + return i.Item1 != DateTime.MinValue; } - if (alwaysEnableFirstEpisode || (i.LastWatchedDate != DateTime.MinValue && i.LastWatchedDate.Date >= request.NextUpDateCutoff)) + if (alwaysEnableFirstEpisode || (i.Item1 != DateTime.MinValue && i.Item1.Date >= request.NextUpDateCutoff)) { anyFound = true; return true; } - return !anyFound && i.LastWatchedDate == DateTime.MinValue; + if (!anyFound && i.Item1 == DateTime.MinValue) + { + return true; + } + + return false; }) - .Select(i => i.GetEpisodeFunction()) + .Select(i => i.Item2()) .Where(i => i != null); } @@ -185,14 +195,14 @@ namespace Emby.Server.Implementations.TV /// Gets the next up. /// /// Task{Episode}. - private (DateTime LastWatchedDate, Func GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) + private Tuple> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) { var lastQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }, + OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Descending) }, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, @@ -206,23 +216,24 @@ namespace Emby.Server.Implementations.TV if (rewatching) { // find last watched by date played, not by newest episode watched - lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; + lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }; } var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast().FirstOrDefault(); - Episode GetEpisode() + Func getEpisode = () => { var nextQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending) }, + OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, Limit = 1, IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, + MinSortName = lastWatchedEpisode?.SortName, DtoOptions = dtoOptions }; @@ -286,7 +297,7 @@ namespace Emby.Server.Implementations.TV } return nextEpisode; - } + }; if (lastWatchedEpisode != null) { @@ -294,11 +305,11 @@ namespace Emby.Server.Implementations.TV var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); - return (lastWatchedDate, GetEpisode); + return new Tuple>(lastWatchedDate, getEpisode); } // Return the first episode - return (DateTime.MinValue, GetEpisode); + return new Tuple>(DateTime.MinValue, getEpisode); } private static QueryResult GetResult(IEnumerable items, NextUpQuery query) From 8ea8dcf128a8eb0e12f85ca535e676e4316e5989 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 21 Jun 2022 17:29:56 +0800 Subject: [PATCH 084/105] Catch external streams exceptions --- .../MediaInfo/MediaInfoResolver.cs | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index 6d9aac2c0fe..c856a0912ef 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -101,34 +101,41 @@ namespace MediaBrowser.Providers.MediaInfo { if (!pathInfo.Path.AsSpan().EndsWith(".strm", StringComparison.OrdinalIgnoreCase)) { - var mediaInfo = await GetMediaInfo(pathInfo.Path, _type, cancellationToken).ConfigureAwait(false); - - if (mediaInfo.MediaStreams.Count == 1) + try { - MediaStream mediaStream = mediaInfo.MediaStreams[0]; + var mediaInfo = await GetMediaInfo(pathInfo.Path, _type, cancellationToken).ConfigureAwait(false); - if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio) - || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle)) + if (mediaInfo.MediaStreams.Count == 1) { - mediaStream.Index = startIndex++; - mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; - mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; + MediaStream mediaStream = mediaInfo.MediaStreams[0]; - mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); - } - } - else - { - foreach (MediaStream mediaStream in mediaInfo.MediaStreams) - { if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio) || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle)) { mediaStream.Index = startIndex++; + mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; + mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); } } + else + { + foreach (MediaStream mediaStream in mediaInfo.MediaStreams) + { + if ((mediaStream.Type == MediaStreamType.Audio && _type == DlnaProfileType.Audio) + || (mediaStream.Type == MediaStreamType.Subtitle && _type == DlnaProfileType.Subtitle)) + { + mediaStream.Index = startIndex++; + + mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); + } + } + } + } + catch + { + continue; } } } From c85255a615fd4249c9529b55080772cd551749d8 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 21 Jun 2022 19:59:23 +0800 Subject: [PATCH 085/105] Log external streams exceptions --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 4 ++++ MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs | 8 ++++---- MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs | 9 ++++++++- MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs | 4 ++++ .../MediaInfo/AudioResolverTests.cs | 3 ++- .../MediaInfo/MediaInfoResolverTests.cs | 9 +++++---- .../MediaInfo/SubtitleResolverTests.cs | 3 ++- 7 files changed, 29 insertions(+), 11 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 0bdf447ba77..17164ee5c40 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo { @@ -15,16 +16,19 @@ namespace MediaBrowser.Providers.MediaInfo /// /// Initializes a new instance of the class for external audio file processing. /// + /// The logger. /// The localization manager. /// The media encoder. /// The file system. /// The object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters. public AudioResolver( + ILogger logger, ILocalizationManager localizationManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, NamingOptions namingOptions) : base( + logger, localizationManager, mediaEncoder, fileSystem, diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index fcd3f28d480..e58c0e281b1 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -47,7 +47,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); public FFProbeProvider( - ILogger logger, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, @@ -59,11 +58,12 @@ namespace MediaBrowser.Providers.MediaInfo IChapterManager chapterManager, ILibraryManager libraryManager, IFileSystem fileSystem, + ILoggerFactory loggerFactory, NamingOptions namingOptions) { - _logger = logger; - _audioResolver = new AudioResolver(localization, mediaEncoder, fileSystem, namingOptions); - _subtitleResolver = new SubtitleResolver(localization, mediaEncoder, fileSystem, namingOptions); + _logger = loggerFactory.CreateLogger(); + _audioResolver = new AudioResolver(loggerFactory.CreateLogger(), localization, mediaEncoder, fileSystem, namingOptions); + _subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger(), localization, mediaEncoder, fileSystem, namingOptions); _videoProber = new FFProbeVideoInfo( _logger, mediaSourceManager, diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index c856a0912ef..d55cc449140 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -15,6 +15,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo { @@ -33,6 +34,7 @@ namespace MediaBrowser.Providers.MediaInfo /// private readonly IMediaEncoder _mediaEncoder; + private readonly ILogger _logger; private readonly IFileSystem _fileSystem; /// @@ -48,18 +50,21 @@ namespace MediaBrowser.Providers.MediaInfo /// /// Initializes a new instance of the class. /// + /// The logger. /// The localization manager. /// The media encoder. /// The file system. /// The object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters. /// The of the parsed file. protected MediaInfoResolver( + ILogger logger, ILocalizationManager localizationManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, NamingOptions namingOptions, DlnaProfileType type) { + _logger = logger; _mediaEncoder = mediaEncoder; _fileSystem = fileSystem; _namingOptions = namingOptions; @@ -133,8 +138,10 @@ namespace MediaBrowser.Providers.MediaInfo } } } - catch + catch (Exception ex) { + _logger.LogError(ex, "Error getting external streams from {Path}", pathInfo.Path); + continue; } } diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 4b9ba944a1e..70e5bd783f1 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo { @@ -15,16 +16,19 @@ namespace MediaBrowser.Providers.MediaInfo /// /// Initializes a new instance of the class for external subtitle file processing. /// + /// The logger. /// The localization manager. /// The media encoder. /// The file system. /// The object containing FileExtensions, MediaDefaultFlags, MediaForcedFlags and MediaFlagDelimiters. public SubtitleResolver( + ILogger logger, ILocalizationManager localizationManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, NamingOptions namingOptions) : base( + logger, localizationManager, mediaEncoder, fileSystem, diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs index 92968920944..33a9aca3129 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/AudioResolverTests.cs @@ -13,6 +13,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Providers.MediaInfo; +using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -55,7 +56,7 @@ public class AudioResolverTests fileSystem.Setup(fs => fs.DirectoryExists(It.IsRegex(MediaInfoResolverTests.MetadataDirectoryRegex))) .Returns(true); - _audioResolver = new AudioResolver(localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); + _audioResolver = new AudioResolver(Mock.Of>(), localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); } [Theory] diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs index 7c3027f9496..91f61868b9a 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs @@ -18,6 +18,7 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Providers.MediaInfo; +using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -70,7 +71,7 @@ public class MediaInfoResolverTests fileSystem.Setup(fs => fs.DirectoryExists(It.IsRegex(MetadataDirectoryRegex))) .Returns(true); - _subtitleResolver = new SubtitleResolver(_localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); + _subtitleResolver = new SubtitleResolver(Mock.Of>(), _localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); } [Fact] @@ -201,7 +202,7 @@ public class MediaInfoResolverTests var mediaEncoder = Mock.Of(MockBehavior.Strict); var fileSystem = Mock.Of(); - var subtitleResolver = new SubtitleResolver(_localizationManager, mediaEncoder, fileSystem, new NamingOptions()); + var subtitleResolver = new SubtitleResolver(Mock.Of>(), _localizationManager, mediaEncoder, fileSystem, new NamingOptions()); var streams = await subtitleResolver.GetExternalStreamsAsync(video, 0, directoryService.Object, false, CancellationToken.None); @@ -306,7 +307,7 @@ public class MediaInfoResolverTests fileSystem.Setup(fs => fs.DirectoryExists(It.IsRegex(MetadataDirectoryRegex))) .Returns(true); - var subtitleResolver = new SubtitleResolver(_localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); + var subtitleResolver = new SubtitleResolver(Mock.Of>(), _localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); var directoryService = GetDirectoryServiceForExternalFile(file); var streams = await subtitleResolver.GetExternalStreamsAsync(video, 0, directoryService, false, CancellationToken.None); @@ -381,7 +382,7 @@ public class MediaInfoResolverTests fileSystem.Setup(fs => fs.DirectoryExists(It.IsRegex(MetadataDirectoryRegex))) .Returns(true); - var subtitleResolver = new SubtitleResolver(_localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); + var subtitleResolver = new SubtitleResolver(Mock.Of>(), _localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); int startIndex = 1; var streams = await subtitleResolver.GetExternalStreamsAsync(video, startIndex, directoryService.Object, false, CancellationToken.None); diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs index 6de6d296e52..0c1c269a4c5 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/SubtitleResolverTests.cs @@ -13,6 +13,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Providers.MediaInfo; +using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -55,7 +56,7 @@ public class SubtitleResolverTests fileSystem.Setup(fs => fs.DirectoryExists(It.IsRegex(MediaInfoResolverTests.MetadataDirectoryRegex))) .Returns(true); - _subtitleResolver = new SubtitleResolver(localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); + _subtitleResolver = new SubtitleResolver(Mock.Of>(), localizationManager, mediaEncoder.Object, fileSystem.Object, new NamingOptions()); } [Theory] From c35fc382d46ad599502cd11f7603c05821b8d038 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Fri, 24 Jun 2022 19:33:53 +0800 Subject: [PATCH 086/105] Fix yuvj420p pixel format hardware decoding --- .../MediaEncoding/EncodingHelper.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3824b005569..20979cc5e22 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -4288,6 +4288,7 @@ namespace MediaBrowser.Controller.MediaEncoding return videoStream.BitDepth.Value; } else if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase) || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase)) { return 8; @@ -4578,7 +4579,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hwSurface = (isIntelDx11OclSupported || isIntelVaapiOclSupported) && _mediaEncoder.SupportsFilter("alphasrc"); - var is8bitSwFormatsQsv = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8bitSwFormatsQsv = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsQsv = is8bitSwFormatsQsv || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); // TODO: add more 8/10bit and 4:4:4 formats for Qsv after finishing the ffcheck tool @@ -4637,7 +4639,8 @@ namespace MediaBrowser.Controller.MediaEncoding } var hwSurface = IsCudaFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); - var is8bitSwFormatsNvdec = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8bitSwFormatsNvdec = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsNvdec = is8bitSwFormatsNvdec || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); // TODO: add more 8/10/12bit and 4:4:4 formats for Nvdec after finishing the ffcheck tool @@ -4703,7 +4706,8 @@ namespace MediaBrowser.Controller.MediaEncoding var hwSurface = _mediaEncoder.SupportsHwaccel("d3d11va") && IsOpenclFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); - var is8bitSwFormatsAmf = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8bitSwFormatsAmf = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsAmf = is8bitSwFormatsAmf || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); if (is8bitSwFormatsAmf) @@ -4759,7 +4763,8 @@ namespace MediaBrowser.Controller.MediaEncoding && IsVaapiFullSupported() && IsOpenclFullSupported() && _mediaEncoder.SupportsFilter("alphasrc"); - var is8bitSwFormatsVaapi = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8bitSwFormatsVaapi = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVaapi = is8bitSwFormatsVaapi || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); if (is8bitSwFormatsVaapi) @@ -4816,7 +4821,8 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - var is8bitSwFormatsVt = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); + var is8bitSwFormatsVt = string.Equals("yuv420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase) + || string.Equals("yuvj420p", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); var is8_10bitSwFormatsVt = is8bitSwFormatsVt || string.Equals("yuv420p10le", videoStream.PixelFormat, StringComparison.OrdinalIgnoreCase); if (is8bitSwFormatsVt) From 3f37ef70e14c897acbe3ad6cc39e122a05345bb4 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 25 Jun 2022 18:42:55 +0800 Subject: [PATCH 087/105] Add json parser for DOVI side_data --- .../Probing/MediaStreamInfo.cs | 7 ++ .../Probing/MediaStreamInfoSideData.cs | 74 +++++++++++++++++++ .../Probing/ProbeResultNormalizer.cs | 21 ++++++ MediaBrowser.Model/Entities/MediaStream.cs | 48 ++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index c9c8c34c2ec..eab8f79bb34 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -310,5 +310,12 @@ namespace MediaBrowser.MediaEncoding.Probing /// The color primaries. [JsonPropertyName("color_primaries")] public string ColorPrimaries { get; set; } + + /// + /// Gets or sets the side_data_list. + /// + /// The side_data_list. + [JsonPropertyName("side_data_list")] + public IReadOnlyList SideDataList { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs new file mode 100644 index 00000000000..095757bef71 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace MediaBrowser.MediaEncoding.Probing +{ + /// + /// Class MediaStreamInfoSideData. + /// + public class MediaStreamInfoSideData + { + /// + /// Gets or sets the SideDataType. + /// + /// The SideDataType. + [JsonPropertyName("side_data_type")] + public string? SideDataType { get; set; } + + /// + /// Gets or sets the DvVersionMajor. + /// + /// The DvVersionMajor. + [JsonPropertyName("dv_version_major")] + public int? DvVersionMajor { get; set; } + + /// + /// Gets or sets the DvVersionMinor. + /// + /// The DvVersionMinor. + [JsonPropertyName("dv_version_minor")] + public int? DvVersionMinor { get; set; } + + /// + /// Gets or sets the DvProfile. + /// + /// The DvProfile. + [JsonPropertyName("dv_profile")] + public int? DvProfile { get; set; } + + /// + /// Gets or sets the DvLevel. + /// + /// The DvLevel. + [JsonPropertyName("dv_level")] + public int? DvLevel { get; set; } + + /// + /// Gets or sets the RpuPresentFlag. + /// + /// The RpuPresentFlag. + [JsonPropertyName("rpu_present_flag")] + public int? RpuPresentFlag { get; set; } + + /// + /// Gets or sets the ElPresentFlag. + /// + /// The ElPresentFlag. + [JsonPropertyName("el_present_flag")] + public int? ElPresentFlag { get; set; } + + /// + /// Gets or sets the BlPresentFlag. + /// + /// The BlPresentFlag. + [JsonPropertyName("bl_present_flag")] + public int? BlPresentFlag { get; set; } + + /// + /// Gets or sets the DvBlSignalCompatibilityId. + /// + /// The DvBlSignalCompatibilityId. + [JsonPropertyName("dv_bl_signal_compatibility_id")] + public int? DvBlSignalCompatibilityId { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 3f78d0d42c6..74d7341e913 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -841,6 +841,27 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.ColorPrimaries = streamInfo.ColorPrimaries; } + + if (streamInfo.SideDataList != null) + { + foreach (var data in streamInfo.SideDataList) + { + // Parse Dolby Vision metadata from side_data + if (string.Equals(data.SideDataType, "DOVI configuration record", StringComparison.OrdinalIgnoreCase)) + { + stream.DvVersionMajor = data.DvVersionMajor; + stream.DvVersionMinor = data.DvVersionMinor; + stream.DvProfile = data.DvProfile; + stream.DvLevel = data.DvLevel; + stream.RpuPresentFlag = data.RpuPresentFlag; + stream.ElPresentFlag = data.ElPresentFlag; + stream.BlPresentFlag = data.BlPresentFlag; + stream.DvBlSignalCompatibilityId = data.DvBlSignalCompatibilityId; + + break; + } + } + } } else { diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 48408e5847b..0619a8c0877 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -72,6 +72,54 @@ namespace MediaBrowser.Model.Entities /// The color primaries. public string ColorPrimaries { get; set; } + /// + /// Gets or sets the Dolby Vision version major. + /// + /// The Dolby Vision version major. + public int? DvVersionMajor { get; set; } + + /// + /// Gets or sets the Dolby Vision version minor. + /// + /// The Dolby Vision version minor. + public int? DvVersionMinor { get; set; } + + /// + /// Gets or sets the Dolby Vision profile. + /// + /// The Dolby Vision profile. + public int? DvProfile { get; set; } + + /// + /// Gets or sets the Dolby Vision level. + /// + /// The Dolby Vision level. + public int? DvLevel { get; set; } + + /// + /// Gets or sets the Dolby Vision rpu present flag. + /// + /// The Dolby Vision rpu present flag. + public int? RpuPresentFlag { get; set; } + + /// + /// Gets or sets the Dolby Vision el present flag. + /// + /// The Dolby Vision el present flag. + public int? ElPresentFlag { get; set; } + + /// + /// Gets or sets the Dolby Vision bl present flag. + /// + /// The Dolby Vision bl present flag. + public int? BlPresentFlag { get; set; } + + /// + /// Gets or sets the Dolby Vision bl signal compatibility id. + /// + /// The Dolby Vision bl signal compatibility id. + public int? DvBlSignalCompatibilityId { get; set; } + /// /// Gets or sets the comment. /// From d2caed25fbbfbd047f6b52c50a501a6dcba415df Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 18 Jun 2022 19:41:17 +0800 Subject: [PATCH 088/105] Register DOVI side_data in db --- .../Data/SqliteItemRepository.cs | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 964a630b265..4361440d753 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -170,7 +170,15 @@ namespace Emby.Server.Implementations.Data "CodecTimeBase", "ColorPrimaries", "ColorSpace", - "ColorTransfer" + "ColorTransfer", + "DvVersionMajor", + "DvVersionMinor", + "DvProfile", + "DvLevel", + "RpuPresentFlag", + "ElPresentFlag", + "BlPresentFlag", + "DvBlSignalCompatibilityId" }; private static readonly string _mediaStreamSaveColumnsInsertQuery = @@ -341,7 +349,7 @@ namespace Emby.Server.Implementations.Data public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) { const string CreateMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, PRIMARY KEY (ItemId, StreamIndex))"; const string CreateMediaAttachmentsTableCommand = "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))"; @@ -555,6 +563,15 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames); AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames); AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames); + + AddColumn(db, "MediaStreams", "DvVersionMajor", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "DvVersionMinor", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "DvProfile", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "DvLevel", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "RpuPresentFlag", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "ElPresentFlag", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "BlPresentFlag", "INT", existingColumnNames); + AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames); }, TransactionMode); @@ -5859,6 +5876,15 @@ AND Type = @InternalPersonType)"); statement.TryBind("@ColorPrimaries" + index, stream.ColorPrimaries); statement.TryBind("@ColorSpace" + index, stream.ColorSpace); statement.TryBind("@ColorTransfer" + index, stream.ColorTransfer); + + statement.TryBind("@DvVersionMajor" + index, stream.DvVersionMajor); + statement.TryBind("@DvVersionMinor" + index, stream.DvVersionMinor); + statement.TryBind("@DvProfile" + index, stream.DvProfile); + statement.TryBind("@DvLevel" + index, stream.DvLevel); + statement.TryBind("@RpuPresentFlag" + index, stream.RpuPresentFlag); + statement.TryBind("@ElPresentFlag" + index, stream.ElPresentFlag); + statement.TryBind("@BlPresentFlag" + index, stream.BlPresentFlag); + statement.TryBind("@DvBlSignalCompatibilityId" + index, stream.DvBlSignalCompatibilityId); } statement.Reset(); @@ -6030,6 +6056,46 @@ AND Type = @InternalPersonType)"); item.ColorTransfer = colorTransfer; } + if (reader.TryGetInt32(35, out var dvVersionMajor)) + { + item.DvVersionMajor = dvVersionMajor; + } + + if (reader.TryGetInt32(36, out var dvVersionMinor)) + { + item.DvVersionMinor = dvVersionMinor; + } + + if (reader.TryGetInt32(37, out var dvProfile)) + { + item.DvProfile = dvProfile; + } + + if (reader.TryGetInt32(38, out var dvLevel)) + { + item.DvLevel = dvLevel; + } + + if (reader.TryGetInt32(39, out var rpuPresentFlag)) + { + item.RpuPresentFlag = rpuPresentFlag; + } + + if (reader.TryGetInt32(40, out var elPresentFlag)) + { + item.ElPresentFlag = elPresentFlag; + } + + if (reader.TryGetInt32(41, out var blPresentFlag)) + { + item.BlPresentFlag = blPresentFlag; + } + + if (reader.TryGetInt32(42, out var dvBlSignalCompatibilityId)) + { + item.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId; + } + if (item.Type == MediaStreamType.Subtitle) { item.LocalizedUndefined = _localization.GetLocalizedString("Undefined"); From e931f5a32b361fc3f60e6528085b2ee5962b04b6 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 18 Jun 2022 19:41:53 +0800 Subject: [PATCH 089/105] Add tests --- .../Probing/ProbeResultNormalizerTests.cs | 8 ++++++++ .../Test Data/Probing/video_metadata.json | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 53e1550ed15..13cfe885f89 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -75,6 +75,14 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Equal(1, res.VideoStream.RefFrames); Assert.Equal("1/1000", res.VideoStream.TimeBase); Assert.Equal(MediaStreamType.Video, res.VideoStream.Type); + Assert.Equal(1, res.VideoStream.DvVersionMajor); + Assert.Equal(0, res.VideoStream.DvVersionMinor); + Assert.Equal(5, res.VideoStream.DvProfile); + Assert.Equal(6, res.VideoStream.DvLevel); + Assert.Equal(1, res.VideoStream.RpuPresentFlag); + Assert.Equal(0, res.VideoStream.ElPresentFlag); + Assert.Equal(1, res.VideoStream.BlPresentFlag); + Assert.Equal(0, res.VideoStream.DvBlSignalCompatibilityId); Assert.Empty(res.Chapters); Assert.Equal("Just color bars", res.Overview); diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json index 720fc5c8fa8..519d81179cb 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json @@ -47,7 +47,20 @@ "tags": { "ENCODER": "Lavc57.107.100 libx264", "DURATION": "00:00:01.000000000" - } + }, + "side_data_list": [ + { + "side_data_type": "DOVI configuration record", + "dv_version_major": 1, + "dv_version_minor": 0, + "dv_profile": 5, + "dv_level": 6, + "rpu_present_flag": 1, + "el_present_flag": 0, + "bl_present_flag": 1, + "dv_bl_signal_compatibility_id": 0 + } + ] } ], "chapters": [ From 50bc41d84d7f3f7ecdccbb6ed1885426f87c0d9a Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Fri, 24 Jun 2022 22:30:49 +0800 Subject: [PATCH 090/105] Add VideoDoViTitle to display DV compatibility --- .../MediaEncoding/EncodingHelper.cs | 20 +++---- MediaBrowser.Model/Entities/MediaStream.cs | 53 +++++++++++++++++-- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 20979cc5e22..6fb5c887407 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -157,9 +157,9 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - if (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(state.VideoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.VideoStream.VideoRangeType, "DOVI", StringComparison.OrdinalIgnoreCase)) { // Only native SW decoder and HW accelerator can parse dovi rpu. var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty; @@ -170,22 +170,24 @@ namespace MediaBrowser.Controller.MediaEncoding return isSwDecoder || isNvdecDecoder || isVaapiDecoder || isD3d11vaDecoder; } - return string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.VideoStream.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase); + return string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.VideoStream.VideoRangeType, "HDR10", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.VideoStream.VideoRangeType, "HLG", StringComparison.OrdinalIgnoreCase)); } private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options) { - if (state.VideoStream == null) + if (state.VideoStream == null + || !options.EnableVppTonemapping + || GetVideoColorBitDepth(state) != 10) { return false; } // Native VPP tonemapping may come to QSV in the future. - return options.EnableVppTonemapping - && string.Equals(state.VideoStream.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) - && GetVideoColorBitDepth(state) == 10; + return string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase) + && string.Equals(state.VideoStream.VideoRangeType, "HDR10", StringComparison.OrdinalIgnoreCase); } /// diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 0619a8c0877..58988b6fae7 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -172,6 +172,47 @@ namespace MediaBrowser.Model.Entities } } + /// + /// Gets the video dovi title. + /// + /// The video dovi title. + public string VideoDoViTitle + { + get + { + var dvProfile = DvProfile; + var rpuPresentFlag = RpuPresentFlag == 1; + var blPresentFlag = BlPresentFlag == 1; + var dvBlCompatId = DvBlSignalCompatibilityId; + + if (rpuPresentFlag + && blPresentFlag + && (dvProfile == 4 + || dvProfile == 5 + || dvProfile == 7 + || dvProfile == 8 + || dvProfile == 9)) + { + var title = "DV Profile " + dvProfile; + + if (dvBlCompatId > 0) + { + title += "." + dvBlCompatId; + } + + return dvBlCompatId switch + { + 1 => title + " (HDR10)", + 2 => title + " (SDR)", + 4 => title + " (HLG)", + _ => title + }; + } + + return null; + } + } + public string LocalizedUndefined { get; set; } public string LocalizedDefault { get; set; } @@ -630,11 +671,17 @@ namespace MediaBrowser.Model.Entities return ("HDR", "HLG"); } - // For some Dolby Vision files, no color transfer is provided, so check the codec - var codecTag = CodecTag; + var dvProfile = DvProfile; + var rpuPresentFlag = RpuPresentFlag == 1; + var blPresentFlag = BlPresentFlag == 1; + var dvBlCompatId = DvBlSignalCompatibilityId; - if (string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase) + var isDoViHDRProfile = dvProfile == 5 || dvProfile == 7 || dvProfile == 8; + var isDoViHDRFlag = rpuPresentFlag && blPresentFlag && (dvBlCompatId == 0 || dvBlCompatId == 1 || dvBlCompatId == 4); + + if ((isDoViHDRProfile && isDoViHDRFlag) + || string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase) || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase)) From 4c178e918836ebb88af8d59740b2392e87ba127d Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 23 Jun 2022 16:54:32 +0800 Subject: [PATCH 091/105] Remove 'using' from HLS/Progressive StreamState --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 ++-- Jellyfin.Api/Controllers/VideosController.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 8127193c209..365e44e1a3a 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -285,7 +285,7 @@ namespace Jellyfin.Api.Controllers // Due to CTS.Token calling ThrowIfDisposed (https://github.com/dotnet/runtime/issues/29970) we have to "cache" the token // since it gets disposed when ffmpeg exits var cancellationToken = cancellationTokenSource.Token; - using var state = await StreamingHelpers.GetStreamingState( + var state = await StreamingHelpers.GetStreamingState( streamingRequest, Request, _authContext, @@ -1432,7 +1432,7 @@ namespace Jellyfin.Api.Controllers var cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = cancellationTokenSource.Token; - using var state = await StreamingHelpers.GetStreamingState( + var state = await StreamingHelpers.GetStreamingState( streamingRequest, Request, _authContext, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 62c05331ede..4e289593456 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -427,7 +427,7 @@ namespace Jellyfin.Api.Controllers StreamOptions = streamOptions }; - using var state = await StreamingHelpers.GetStreamingState( + var state = await StreamingHelpers.GetStreamingState( streamingRequest, Request, _authContext, From 5dbe16d3e6ab7b81ca4ce4b6f2d6e9c157b87efc Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 23 Jun 2022 16:54:36 +0800 Subject: [PATCH 092/105] Re-enable throttler for HWA and Copy --- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 17 +++++------------ .../MediaEncoding/JobLogger.cs | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 416418dc688..a22bda9652b 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -663,18 +663,11 @@ namespace Jellyfin.Api.Helpers { var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); - // enable throttling when NOT using hardware acceleration - if (string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) - { - return state.InputProtocol == MediaProtocol.File && - state.RunTimeTicks.HasValue && - state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && - state.IsInputVideo && - state.VideoType == VideoType.VideoFile && - !EncodingHelper.IsCopyCodec(state.OutputVideoCodec); - } - - return false; + return state.InputProtocol == MediaProtocol.File && + state.RunTimeTicks.HasValue && + state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && + state.IsInputVideo && + state.VideoType == VideoType.VideoFile; } /// diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index 8b2837ee3c0..d8475f12ae0 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.MediaEncoding percent = 100.0 * currentMs / totalMs; - transcodingPosition = val; + transcodingPosition = TimeSpan.FromMilliseconds(currentMs); } } else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase)) From 506ed6940be8fe0bec99ce1b79ac83d2c2582480 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Fri, 24 Jun 2022 23:09:28 +0800 Subject: [PATCH 093/105] Detach TranscodingJob from StreamState --- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 4 ++-- Jellyfin.Api/Models/StreamingDtos/StreamState.cs | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index a22bda9652b..13dc878c1bf 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -654,8 +654,8 @@ namespace Jellyfin.Api.Helpers { if (EnableThrottling(state)) { - transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, new Logger(new LoggerFactory()), _serverConfigurationManager, _fileSystem); - state.TranscodingThrottler.Start(); + transcodingJob.TranscodingThrottler = new TranscodingThrottler(transcodingJob, new Logger(new LoggerFactory()), _serverConfigurationManager, _fileSystem); + transcodingJob.TranscodingThrottler.Start(); } } diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs index cbabf087bcb..192f33ebd1f 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs +++ b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs @@ -47,11 +47,6 @@ namespace Jellyfin.Api.Models.StreamingDtos } } - /// - /// Gets or sets the transcoding throttler. - /// - public TranscodingThrottler? TranscodingThrottler { get; set; } - /// /// Gets the video request. /// @@ -191,11 +186,8 @@ namespace Jellyfin.Api.Models.StreamingDtos { _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).GetAwaiter().GetResult(); } - - TranscodingThrottler?.Dispose(); } - TranscodingThrottler = null; TranscodingJob = null; _disposed = true; From 7efa4e38c1c7aa3d234aad929bf6184a408d1428 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Sun, 26 Jun 2022 12:43:17 +0200 Subject: [PATCH 094/105] Fix password change during parental schedule --- Jellyfin.Api/Controllers/UserController.cs | 5 +++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 +++-- MediaBrowser.Controller/Library/IUserManager.cs | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 6d15d918580..6fb295eb890 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -256,7 +256,7 @@ namespace Jellyfin.Api.Controllers /// User not found. /// A indicating success or a or a on failure. [HttpPost("{userId}/Password")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize(Policy = Policies.IgnoreParentalControl)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -287,7 +287,8 @@ namespace Jellyfin.Api.Controllers request.CurrentPw, request.CurrentPw, HttpContext.GetNormalizedRemoteIp().ToString(), - false).ConfigureAwait(false); + false, + ignoreParentalSchedule: true).ConfigureAwait(false); if (success == null) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 2100fa6d595..09ee7fc6e6c 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -382,7 +382,8 @@ namespace Jellyfin.Server.Implementations.Users string password, string passwordSha1, string remoteEndPoint, - bool isUserSession) + bool isUserSession, + bool ignoreParentalSchedule = false) { if (string.IsNullOrWhiteSpace(username)) { @@ -458,7 +459,7 @@ namespace Jellyfin.Server.Implementations.Users throw new SecurityException("Forbidden."); } - if (!user.IsParentalScheduleAllowed()) + if (!ignoreParentalSchedule && !user.IsParentalScheduleAllowed()) { _logger.LogInformation( "Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 993e3e18f9d..78843626358 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -138,8 +138,9 @@ namespace MediaBrowser.Controller.Library /// Hash of password. /// Remove endpoint to use. /// Specifies if a user session. + /// Ignore parental control schedule during authentication. /// User wrapped in awaitable task. - Task AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession); + Task AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession, bool ignoreParentalSchedule = false); /// /// Starts the forgot password process. From 54778d875d0ea16892743b1b78f993c8a1b23e1c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 26 Jun 2022 21:00:05 -0400 Subject: [PATCH 095/105] Bump version to 10.8.1 --- Emby.Naming/Emby.Naming.csproj | 2 +- Jellyfin.Data/Jellyfin.Data.csproj | 2 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 2 +- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- SharedVersion.cs | 4 ++-- build.yaml | 2 +- debian/changelog | 6 ++++++ debian/metapackage/jellyfin | 2 +- fedora/jellyfin.spec | 4 +++- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 2 +- 11 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index ea7309b1353..090f0a1f5fb 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -36,7 +36,7 @@ Jellyfin Contributors Jellyfin.Naming - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index d39c75e368c..fb2438939e0 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -18,7 +18,7 @@ Jellyfin Contributors Jellyfin.Data - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 35231a792e3..1770ab59a9e 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Common - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ecb8ef104df..32ead061b6d 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Controller - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 1122d842230..caef198e49a 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Model - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/SharedVersion.cs b/SharedVersion.cs index 5e2f151a221..d296c8a7c2a 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.8.0")] -[assembly: AssemblyFileVersion("10.8.0")] +[assembly: AssemblyVersion("10.8.1")] +[assembly: AssemblyFileVersion("10.8.1")] diff --git a/build.yaml b/build.yaml index 18434ee0035..d2662f136e6 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,7 @@ --- # We just wrap `build` so this is really it name: "jellyfin" -version: "10.8.0" +version: "10.8.1" packages: - debian.amd64 - debian.arm64 diff --git a/debian/changelog b/debian/changelog index ea135796114..e4028294f43 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.8.1-1) unstable; urgency=medium + + * New upstream version 10.8.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.1 + + -- Jellyfin Packaging Team Sun, 26 Jun 2022 20:59:36 -0400 + jellyfin-server (10.8.0-1) unstable; urgency=medium * New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0 diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin index eaab8fc9a1b..6bd2d637ed0 100644 --- a/debian/metapackage/jellyfin +++ b/debian/metapackage/jellyfin @@ -5,7 +5,7 @@ Homepage: https://jellyfin.org Standards-Version: 3.9.2 Package: jellyfin -Version: 10.8.0~beta3 +Version: 10.8.1 Maintainer: Jellyfin Packaging Team Depends: jellyfin-server, jellyfin-web Description: Provides the Jellyfin Free Software Media System diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 366cd74d4c1..26b768bbf63 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.0 +Version: 10.8.1 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv2 @@ -176,6 +176,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Sun Jun 26 2022 Jellyfin Packaging Team +- New upstream version 10.8.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.1 * Fri Jun 10 2022 Jellyfin Packaging Team - New upstream version 10.8.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.0 * Mon Nov 29 2021 Brian J. Murrell diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 460c43829bf..447b4d501d0 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -13,7 +13,7 @@ Jellyfin Contributors Jellyfin.Extensions - 10.8.0 + 10.8.1 https://github.com/jellyfin/jellyfin GPL-3.0-only From 891ccd7bb27cebf88293e3fe82bc527f23f0d42e Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 30 Jun 2022 14:53:30 +0200 Subject: [PATCH 096/105] Remove mount and unmount permissions for jellyfin group from sudoers --- debian/conf/jellyfin-sudoers | 4 ---- fedora/README.md | 10 +--------- fedora/jellyfin.sudoers | 4 ---- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/debian/conf/jellyfin-sudoers b/debian/conf/jellyfin-sudoers index f84e7454ff0..795fd17e830 100644 --- a/debian/conf/jellyfin-sudoers +++ b/debian/conf/jellyfin-sudoers @@ -30,8 +30,4 @@ Defaults!RESTARTSERVER_INITD !requiretty Defaults!STARTSERVER_INITD !requiretty Defaults!STOPSERVER_INITD !requiretty -#Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - Defaults:jellyfin !requiretty diff --git a/fedora/README.md b/fedora/README.md index 7ed6f7efc63..d449b51c169 100644 --- a/fedora/README.md +++ b/fedora/README.md @@ -18,14 +18,6 @@ $ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-re $ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm ``` -## ISO mounting - -To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers` -``` -# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount -# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount -``` - ## Building with dotnet Jellyfin is build with `--self-contained` so no dotnet required for runtime. @@ -40,4 +32,4 @@ $ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft- ## TODO -- [ ] OpenSUSE \ No newline at end of file +- [ ] OpenSUSE diff --git a/fedora/jellyfin.sudoers b/fedora/jellyfin.sudoers index 57a9e7b6713..01c7f4e11fe 100644 --- a/fedora/jellyfin.sudoers +++ b/fedora/jellyfin.sudoers @@ -11,8 +11,4 @@ Defaults!RESTARTSERVER_SYSTEMD !requiretty Defaults!STARTSERVER_SYSTEMD !requiretty Defaults!STOPSERVER_SYSTEMD !requiretty -# Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - Defaults:jellyfin !requiretty From 5f3dbd82942593352d1cad2f7f168ab1aabe8b70 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 4 Jul 2022 18:16:36 +0200 Subject: [PATCH 097/105] Allow administrator to always change password --- Jellyfin.Api/Controllers/UserController.cs | 23 ++++++++++++---------- Jellyfin.Api/Helpers/RequestHelpers.cs | 12 +++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 6fb295eb890..25dc6a785c1 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -282,17 +282,20 @@ namespace Jellyfin.Api.Controllers } else { - var success = await _userManager.AuthenticateUser( - user.Username, - request.CurrentPw, - request.CurrentPw, - HttpContext.GetNormalizedRemoteIp().ToString(), - false, - ignoreParentalSchedule: true).ConfigureAwait(false); - - if (success == null) + if (await RequestHelpers.IsUserAdministrator(_authContext, HttpContext.Request).ConfigureAwait(false)) { - return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered."); + var success = await _userManager.AuthenticateUser( + user.Username, + request.CurrentPw, + request.CurrentPw, + HttpContext.GetNormalizedRemoteIp().ToString(), + false, + ignoreParentalSchedule: true).ConfigureAwait(false); + + if (success == null) + { + return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered."); + } } await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 20427d7fab2..f79a3013418 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -76,6 +76,18 @@ namespace Jellyfin.Api.Helpers return true; } + /// + /// Checks if the user is administrator. + /// + /// Instance of the interface. + /// The . + /// A whether the user can update the entry. + internal static async Task IsUserAdministrator(IAuthorizationContext authContext, HttpRequest requestContext) + { + var auth = await authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); + return auth.User.HasPermission(PermissionKind.IsAdministrator); + } + internal static async Task GetSession(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) { var authorization = await authContext.GetAuthorizationInfo(request).ConfigureAwait(false); From f9d26ea1bcf14c148b2eceff56923f9316a85a12 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 4 Jul 2022 19:08:40 +0200 Subject: [PATCH 098/105] Use IsInRole --- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Api/Helpers/RequestHelpers.cs | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 25dc6a785c1..0c705175316 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -282,7 +282,7 @@ namespace Jellyfin.Api.Controllers } else { - if (await RequestHelpers.IsUserAdministrator(_authContext, HttpContext.Request).ConfigureAwait(false)) + if (HttpContext.User.IsInRole(UserRoles.Administrator)) { var success = await _userManager.AuthenticateUser( user.Username, diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index f79a3013418..20427d7fab2 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -76,18 +76,6 @@ namespace Jellyfin.Api.Helpers return true; } - /// - /// Checks if the user is administrator. - /// - /// Instance of the interface. - /// The . - /// A whether the user can update the entry. - internal static async Task IsUserAdministrator(IAuthorizationContext authContext, HttpRequest requestContext) - { - var auth = await authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - return auth.User.HasPermission(PermissionKind.IsAdministrator); - } - internal static async Task GetSession(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) { var authorization = await authContext.GetAuthorizationInfo(request).ConfigureAwait(false); From 81e535fc6214ba6b92f0135750831fc717edfb70 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 4 Jul 2022 19:10:37 +0200 Subject: [PATCH 099/105] Rollback changes in IUserManager --- Jellyfin.Api/Controllers/UserController.cs | 7 +++---- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 ++--- MediaBrowser.Controller/Library/IUserManager.cs | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0c705175316..82c8563a80b 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -256,7 +256,7 @@ namespace Jellyfin.Api.Controllers /// User not found. /// A indicating success or a or a on failure. [HttpPost("{userId}/Password")] - [Authorize(Policy = Policies.IgnoreParentalControl)] + [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -282,15 +282,14 @@ namespace Jellyfin.Api.Controllers } else { - if (HttpContext.User.IsInRole(UserRoles.Administrator)) + if (!HttpContext.User.IsInRole(UserRoles.Administrator)) { var success = await _userManager.AuthenticateUser( user.Username, request.CurrentPw, request.CurrentPw, HttpContext.GetNormalizedRemoteIp().ToString(), - false, - ignoreParentalSchedule: true).ConfigureAwait(false); + false).ConfigureAwait(false); if (success == null) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 09ee7fc6e6c..2100fa6d595 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -382,8 +382,7 @@ namespace Jellyfin.Server.Implementations.Users string password, string passwordSha1, string remoteEndPoint, - bool isUserSession, - bool ignoreParentalSchedule = false) + bool isUserSession) { if (string.IsNullOrWhiteSpace(username)) { @@ -459,7 +458,7 @@ namespace Jellyfin.Server.Implementations.Users throw new SecurityException("Forbidden."); } - if (!ignoreParentalSchedule && !user.IsParentalScheduleAllowed()) + if (!user.IsParentalScheduleAllowed()) { _logger.LogInformation( "Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 78843626358..993e3e18f9d 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -138,9 +138,8 @@ namespace MediaBrowser.Controller.Library /// Hash of password. /// Remove endpoint to use. /// Specifies if a user session. - /// Ignore parental control schedule during authentication. /// User wrapped in awaitable task. - Task AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession, bool ignoreParentalSchedule = false); + Task AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession); /// /// Starts the forgot password process. From a41c67d16b35d3ebcc9256068209f2cb44036051 Mon Sep 17 00:00:00 2001 From: Andy Walsh Date: Fri, 8 Jul 2022 13:56:09 +0200 Subject: [PATCH 100/105] fix copy&paste error for requestedRangeTypes preventing stream copy - add >=0 check to subtitle index check - fixes #8070, #7880 --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6fb5c887407..684ea157fdc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1713,6 +1713,7 @@ namespace MediaBrowser.Controller.MediaEncoding // Can't stream copy if we're burning in subtitles if (request.SubtitleStreamIndex.HasValue + && request.SubtitleStreamIndex.Value >= 0 && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode) { return false; @@ -1760,7 +1761,7 @@ namespace MediaBrowser.Controller.MediaEncoding } var requestedRangeTypes = state.GetRequestedRangeTypes(videoStream.Codec); - if (requestedProfiles.Length > 0) + if (requestedRangeTypes.Length > 0) { if (string.IsNullOrEmpty(videoStream.VideoRangeType)) { From 72da42cb0a3c06267073a4db2b4b3e25efdf661b Mon Sep 17 00:00:00 2001 From: Andy Walsh Date: Sun, 10 Jul 2022 02:14:49 +0200 Subject: [PATCH 101/105] allow higher opus, vorbis transcode bitrates --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6fb5c887407..c91f6eeb87e 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2026,6 +2026,8 @@ namespace MediaBrowser.Controller.MediaEncoding { if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) { From f8ea4577abcc66b789c672fd0421f6158d2421f4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 11 Jul 2022 15:45:11 +0200 Subject: [PATCH 102/105] Add resolution text output for more resolutions --- MediaBrowser.Model/Entities/MediaStream.cs | 19 ++++++++++----- .../Entities/MediaStreamTests.cs | 23 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 58988b6fae7..ce21707938f 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -588,15 +588,22 @@ namespace MediaBrowser.Model.Entities return Width switch { - <= 720 when Height <= 480 => IsInterlaced ? "480i" : "480p", - // 720x576 (PAL) (768 when rescaled for square pixels) - <= 768 when Height <= 576 => IsInterlaced ? "576i" : "576p", - // 960x540 (sometimes 544 which is multiple of 16) + // 256x144 (16:9 square pixel format) + <= 256 when Height <= 144 => IsInterlaced ? "144i" : "144p", + // 426x240 (16:9 square pixel format) + <= 426 when Height <= 240 => IsInterlaced ? "240i" : "240p", + // 640x360 (16:9 square pixel format) + <= 640 when Height <= 360 => IsInterlaced ? "360i" : "360p", + // 854x480 (16:9 square pixel format) + <= 854 when Height <= 480 => IsInterlaced ? "480i" : "480p", + // 960x544 (16:9 square pixel format) <= 960 when Height <= 544 => IsInterlaced ? "540i" : "540p", + // 1024x576 (16:9 square pixel format) + <= 1024 when Height <= 576 => IsInterlaced ? "576i" : "576p", // 1280x720 <= 1280 when Height <= 962 => IsInterlaced ? "720i" : "720p", - // 1920x1080 - <= 1920 when Height <= 1440 => IsInterlaced ? "1080i" : "1080p", + // 2560x1080 (FHD ultra wide 21:9) using 1440px width to accomodate WQHD + <= 2560 when Height <= 1440 => IsInterlaced ? "1080i" : "1080p", // 4K <= 4096 when Height <= 3072 => "4K", // 8K diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index 9fcf8189f3f..5e9e0d45d3d 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -109,25 +109,32 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(null, null, false, null)] [InlineData(null, 0, false, null)] [InlineData(0, null, false, null)] - [InlineData(640, 480, false, "480p")] - [InlineData(640, 480, true, "480i")] + [InlineData(256, 144, false, "144p")] + [InlineData(256, 144, true, "144i")] + [InlineData(426, 240, false, "240p")] + [InlineData(426, 240, true, "240i")] + [InlineData(640, 360, false, "360p")] + [InlineData(640, 360, true, "360i")] + [InlineData(854, 480, false, "480p")] + [InlineData(854, 480, true, "480i")] + [InlineData(960, 544, false, "540p")] + [InlineData(960, 544, true, "540i")] [InlineData(720, 576, false, "576p")] [InlineData(720, 576, true, "576i")] - [InlineData(960, 540, false, "540p")] - [InlineData(960, 540, true, "540i")] [InlineData(1280, 720, false, "720p")] [InlineData(1280, 720, true, "720i")] [InlineData(1920, 1080, false, "1080p")] [InlineData(1920, 1080, true, "1080i")] [InlineData(4096, 3072, false, "4K")] [InlineData(8192, 6144, false, "8K")] + [InlineData(576, 336, false, "360p")] + [InlineData(624, 352, false, "360p")] + [InlineData(640, 352, false, "360p")] [InlineData(512, 384, false, "480p")] - [InlineData(576, 336, false, "480p")] - [InlineData(624, 352, false, "480p")] - [InlineData(640, 352, false, "480p")] [InlineData(704, 396, false, "480p")] [InlineData(720, 404, false, "480p")] [InlineData(720, 480, false, "480p")] + [InlineData(640, 480, false, "480p")] [InlineData(768, 576, false, "576p")] [InlineData(960, 720, false, "720p")] [InlineData(1280, 528, false, "720p")] @@ -156,6 +163,8 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(1920, 1072, false, "1080p")] [InlineData(1440, 1072, false, "1080p")] [InlineData(1440, 1080, false, "1080p")] + [InlineData(1440, 1440, false, "1080p")] + [InlineData(1920, 1440, false, "1080p")] [InlineData(3840, 1600, false, "4K")] [InlineData(3840, 1606, false, "4K")] [InlineData(3840, 1608, false, "4K")] From 7eaa0600e0109ad81e7b0684b4bdc7c40e446eae Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 12 Jul 2022 17:41:22 -0600 Subject: [PATCH 103/105] Update to dotnet 6.0.7 --- .../Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index f91ac5e3e8d..36daf37ff1c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 249dc400a10..91bf62f0621 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 0fd01bea7dc..1d54c428061 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 9b8bf718714..60496afb2fb 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 20847fd258c..89c74aadbe3 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 5da6fbf7744..2135d6f01ff 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 34ef0c20de0..24330f629e6 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index f3a7de56d7a..507f446cc29 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index fa21daf6696..31513541cf9 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/77d472e5-194c-421e-992d-e4ca1d08e6cc/56c61ac303ddf1b12026151f4f000a2b/dotnet-sdk-6.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index a6b8888fce8..b44ecd16651 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,7 +15,7 @@ - + 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 98fd1a03fa0..73a41791589 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index e8cdf0434d7..7405362416d 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From b9da0e7f83b5bf6ce91c41f8f98c4930020bee2a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 12 Jul 2022 17:41:32 -0600 Subject: [PATCH 104/105] Update remaining dependencies --- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 4 ++-- MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index ceaa3fc9c06..2448e2e2bc9 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 7c70d37a632..98658dbab5f 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -30,7 +30,7 @@ - + From d694a6c09aaebe6e9b4aeadf849d8bce00dd61c4 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 16 Jul 2022 10:16:33 +0200 Subject: [PATCH 105/105] Add more MediaStream resolution tests, sort them by width --- .../Entities/MediaStreamTests.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index 5e9e0d45d3d..7c8a90605b7 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -117,25 +117,29 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(640, 360, true, "360i")] [InlineData(854, 480, false, "480p")] [InlineData(854, 480, true, "480i")] - [InlineData(960, 544, false, "540p")] - [InlineData(960, 544, true, "540i")] - [InlineData(720, 576, false, "576p")] - [InlineData(720, 576, true, "576i")] + [InlineData(960, 540, false, "540p")] + [InlineData(960, 540, true, "540i")] + [InlineData(1024, 576, false, "576p")] + [InlineData(1024, 576, true, "576i")] [InlineData(1280, 720, false, "720p")] [InlineData(1280, 720, true, "720i")] - [InlineData(1920, 1080, false, "1080p")] - [InlineData(1920, 1080, true, "1080i")] + [InlineData(2560, 1080, false, "1080p")] + [InlineData(2560, 1080, true, "1080i")] [InlineData(4096, 3072, false, "4K")] [InlineData(8192, 6144, false, "8K")] + [InlineData(512, 384, false, "480p")] [InlineData(576, 336, false, "360p")] + [InlineData(576, 336, true, "360i")] [InlineData(624, 352, false, "360p")] [InlineData(640, 352, false, "360p")] - [InlineData(512, 384, false, "480p")] + [InlineData(640, 480, false, "480p")] [InlineData(704, 396, false, "480p")] [InlineData(720, 404, false, "480p")] [InlineData(720, 480, false, "480p")] - [InlineData(640, 480, false, "480p")] + [InlineData(720, 576, false, "576p")] [InlineData(768, 576, false, "576p")] + [InlineData(960, 544, false, "540p")] + [InlineData(960, 544, true, "540i")] [InlineData(960, 720, false, "720p")] [InlineData(1280, 528, false, "720p")] [InlineData(1280, 532, false, "720p")] @@ -147,6 +151,11 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(1280, 696, false, "720p")] [InlineData(1280, 716, false, "720p")] [InlineData(1280, 718, false, "720p")] + [InlineData(1920, 1080, false, "1080p")] + [InlineData(1440, 1070, false, "1080p")] + [InlineData(1440, 1072, false, "1080p")] + [InlineData(1440, 1080, false, "1080p")] + [InlineData(1440, 1440, false, "1080p")] [InlineData(1912, 792, false, "1080p")] [InlineData(1916, 1076, false, "1080p")] [InlineData(1918, 1080, false, "1080p")] @@ -160,16 +169,16 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(1920, 960, false, "1080p")] [InlineData(1920, 1024, false, "1080p")] [InlineData(1920, 1040, false, "1080p")] + [InlineData(1920, 1070, false, "1080p")] [InlineData(1920, 1072, false, "1080p")] - [InlineData(1440, 1072, false, "1080p")] - [InlineData(1440, 1080, false, "1080p")] - [InlineData(1440, 1440, false, "1080p")] [InlineData(1920, 1440, false, "1080p")] [InlineData(3840, 1600, false, "4K")] [InlineData(3840, 1606, false, "4K")] [InlineData(3840, 1608, false, "4K")] [InlineData(3840, 2160, false, "4K")] + [InlineData(4090, 3070, false, "4K")] [InlineData(7680, 4320, false, "8K")] + [InlineData(8190, 6140, false, "8K")] public void GetResolutionText_Valid(int? width, int? height, bool interlaced, string expected) { var mediaStream = new MediaStream()