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 ee9cfeeca8..ce75e678ea 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 0035dc6653..9baf6877d9 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 719f553ce8..53637b7931 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 79b1f4fdb1..d3ef22c256 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 ee9cfeeca8..9702412cb2 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 9702412cb2..d49b92cd37 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 399ece7fd0..120b1812a7 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 d49b92cd37..56d1ebf547 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 54b41a6d25..bffd3a1ca6 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 71962f229c..e016d7e51f 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 1e7fedb36f..68059f9806 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 ad3bce86ea..c4d313bdba 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 78da3a2b28..9c608e2baa 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 9096d8b59d..eaab8fc9a1 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 439943b44e..1a5c1b2973 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 6ba9500800..3418e7697c 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 5e99b9c32a..3add8f4fff 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 94c9c3000d..8057c25dae 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 fda603c1fa..e32a2b9336 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 8853c92952..59661d13bc 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 837f416d86..2d574fa74c 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 4a4e204269..4308d86b46 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 383aace069..7143a39f3e 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 1864bd1a59..625b0791ef 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 995b71010c..c457aa0bbe 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 cd40c30af3..291722db91 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 6b79e48d76..6cd4361807 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 d59d36e88e..9f813f532c 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 3df8481fd4..66fa3bc31b 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 2794a06f30..58caae9f85 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 5cb7468b24..790d6e64d8 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 31b9798365..e5e3688676 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 e2fa93a380..d08fedd111 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 e5e3688676..5c05c57a61 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 2188c0d991..ae78ce95a4 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 ae78ce95a4..814253e0f0 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 1421d0183d..6d9aac2c0f 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 aec523882d..9296892094 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 98b4a6ccf6..7c3027f949 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 0e6457ce37..6de6d296e5 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 d08fedd111..8c8d088b6a 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 0f10605ea1..80e6d4e0b0 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 8c8d088b6a..eaf2c25daf 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 9fa92a53a1..d21b6a929c 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 c865560958..b16399598c 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 eaf2c25daf..d25376297f 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 3418e7697c..43d1c38785 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 3add8f4fff..f6f370fbf3 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 e32a2b9336..f1bfaec7fa 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 59661d13bc..8637fcc071 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 8d242d13d1..b2824db110 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 0da00bb0af..f9e9a0ff38 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 ee51562b40..c56a3ad9ef 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 d2087b0230..5942891784 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 c8c526f4d1..9377d595f3 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 bd8667a847..e4361e1bf4 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 cc36dc4fa0..16aeac5fb0 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 de1fcab59b..8235b07756 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 e186a488a9..88d45e255b 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 5fdb22546e..b538ad542e 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 69ba5d86a6..626ba863be 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 ba39cc0ff5..a8db7753d2 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 6d52a3cd6b..0fe0ee140f 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 2d0a7c0313..76067cdb2a 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 291722db91..e8b336c09b 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 6cd4361807..f5226c5cae 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 d8bc4d5960..dc7949c81e 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 1323d8a480..2bda8d2905 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 f6b7efb1e9..49bc2d775b 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 814253e0f0..5d645bddfe 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 06931ac3b5..fce0b6d6f6 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 bffd3a1ca6..ed5ebc8a3d 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 6347b908ca..9173487dd8 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 ed5ebc8a3d..ce5a9fe3f2 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 4742d21e9e..96b48ca52a 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 ce5a9fe3f2..32348c6370 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 1bac4b1875..7233f21b51 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 7233f21b51..77b97c9b48 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 32348c6370..aaab7954dc 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 aaab7954dc..eb344b5287 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 44e5b0d1e7..487948f815 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 0000000000..d3a6ac9c81 --- /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 b1c576c330..401c0197ac 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 91fac4bef5..e95a878c67 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 9c608e2baa..ea13579611 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 1a5c1b2973..56e7476d20 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 a9d16a49e4..fd73ed5f80 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 6d767914f7..5a2936bd8b 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 1b69dd74ef..1f92d7d94e 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 064e105373..2f97c46549 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 140c4272e1..fe4ccd6acb 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 757b19b317..dfbb616aa8 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 6ffedccbd4..07e113ad3e 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 7785cfb495..ba99bb534d 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 9173487dd8..8127193c20 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 ac28ca26ab..8572a5eaed 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 3382ba2511..07a6d6050b 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 da9b691365..1c25696cd1 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 eb344b5287..3b7fd482a1 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 eb344b5287..ad988872fe 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 3b7fd482a1..07a20baa0b 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 05d80ba35d..6c7842c7b9 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 cc42c1718a..a0adb56ef7 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 5ae5ff3bec..4bf66c0988 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 d59f2f4e51..2b1c6e937b 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 c5abb9a0a3..20a2edb05a 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 2b1c6e937b..538010f6c0 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 6c243050d4..964a630b26 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 0a28acf37b..470507c530 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 02af2e4353..83c9141a93 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 1755b872f4..5bf6dad7ad 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 5bf6dad7ad..04778e6993 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 261fd262d8..6f9cf47190 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 56e7476d20..6ac1daee4d 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 6f9cf47190..3188cf6039 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 6ac1daee4d..586c95c077 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 586c95c077..ae0c96f103 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 4308d86b46..853a76303d 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 56b7a3558d..89cad1a2d3 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 ae0c96f103..366cd74d4c 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 18b4139646..f1aa6531f5 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 d6754ad4a8..c54945c93b 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 5d645bddfe..2f67ea3fa1 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 2358fe6238..6875282318 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 35dcebdaba..c001c32b8c 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 03882a0b97..e5ce0aa210 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 f1aa6531f5..11256dafde 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 6ab5843c15..df6539a5a6 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 e27a8975b7..b73ce00b6f 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 34dab75b82..c96ca87a56 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 462585ce35..fb4e7bd1f5 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 04778e6993..e0a3069e7c 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 0824590f22..491662861f 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 8d03b4c0b3..5734224167 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 6e129246b0..47c36494bd 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 6170ff5bd6..79ae951708 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 eb81fde751..a32433e185 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 fe9ff2ebe8..bfab185342 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 da089602f1..0c66351c70 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 96b48ca52a..46a481092e 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 9da9f3323b..bbdf4536b7 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 e0a3069e7c..d5b0aca3c3 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 bfab185342..fdb9fd5d54 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 0c66351c70..4fcc54a038 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 46a481092e..48408e5847 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 04778e6993..3b0b842667 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 20d372d7aa..d378c6e134 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 fce0b6d6f6..73ebfba70e 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 2a535d5565..15021c19d6 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 82294644b8..f3aaef0778 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 75ec5f213f..11afdc4aed 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 f3aaef0778..32289625fe 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 4fcc54a038..0c66351c70 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 34dab75b82..54234c56a3 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 54234c56a3..87544b3e3e 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 49bc2d775b..49173b3d0a 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 727b9d4b5c..e78f031bbf 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 49173b3d0a..c54cd37605 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 9bc5c31f62..85de916940 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 853a76303d..0833cccc8f 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 0000000000..0d1cf6e258 --- /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 0000000000..6761cd3099 --- /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 c54cd37605..b9b8a89eba 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 2a5018c057..68fcb71ad8 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 2a5018c057..3a6cb75450 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 43d1c38785..5b1523576b 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 f6f370fbf3..249dc400a1 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 8057c25dae..0fd01bea7d 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 f1bfaec7fa..9b8bf71871 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 8637fcc071..1122d84223 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 2d574fa74c..20847fd258 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 4308d86b46..64ee201eea 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 7143a39f3e..34ef0c20de 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 625b0791ef..f3a7de56d7 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 c457aa0bbe..fa21daf669 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 f9e9a0ff38..a6b8888fce 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 e8b336c09b..98fd1a03fa 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 f5226c5cae..e8cdf0434d 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 5b1523576b..f91ac5e3e8 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 2f67ea3fa1..ceaa3fc9c0 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 b9b8a89eba..7091af734a 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 e78f031bbf..727b9d4b5c 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 6d9aac2c0f..c856a0912e 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 0bdf447ba7..17164ee5c4 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 fcd3f28d48..e58c0e281b 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 c856a0912e..d55cc44914 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 4b9ba944a1..70e5bd783f 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 9296892094..33a9aca312 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 7c3027f949..91f61868b9 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 6de6d296e5..0c1c269a4c 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 3824b00556..20979cc5e2 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 c9c8c34c2e..eab8f79bb3 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 0000000000..095757bef7 --- /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 3f78d0d42c..74d7341e91 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 48408e5847..0619a8c087 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 964a630b26..4361440d75 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 53e1550ed1..13cfe885f8 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 720fc5c8fa..519d81179c 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 20979cc5e2..6fb5c88740 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 0619a8c087..58988b6fae 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 8127193c20..365e44e1a3 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 62c05331ed..4e28959345 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 416418dc68..a22bda9652 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 8b2837ee3c..d8475f12ae 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 a22bda9652..13dc878c1b 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 cbabf087bc..192f33ebd1 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 6d15d91858..6fb295eb89 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 2100fa6d59..09ee7fc6e6 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 993e3e18f9..7884362635 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 ea7309b135..090f0a1f5f 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 d39c75e368..fb2438939e 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 35231a792e..1770ab59a9 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 ecb8ef104d..32ead061b6 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 1122d84223..caef198e49 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 5e2f151a22..d296c8a7c2 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 18434ee003..d2662f136e 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 ea13579611..e4028294f4 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 eaab8fc9a1..6bd2d637ed 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 366cd74d4c..26b768bbf6 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 460c43829b..447b4d501d 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 f84e7454ff..795fd17e83 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 7ed6f7efc6..d449b51c16 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 57a9e7b671..01c7f4e11f 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 6fb295eb89..25dc6a785c 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 20427d7fab..f79a301341 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 25dc6a785c..0c70517531 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 f79a301341..20427d7fab 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 0c70517531..82c8563a80 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 09ee7fc6e6..2100fa6d59 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 7884362635..993e3e18f9 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 6fb5c88740..684ea157fd 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 6fb5c88740..c91f6eeb87 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 58988b6fae..ce21707938 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 9fcf8189f3..5e9e0d45d3 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 f91ac5e3e8..36daf37ff1 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 249dc400a1..91bf62f062 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 0fd01bea7d..1d54c42806 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 9b8bf71871..60496afb2f 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 20847fd258..89c74aadbe 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 5da6fbf774..2135d6f01f 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 34ef0c20de..24330f629e 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 f3a7de56d7..507f446cc2 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 fa21daf669..31513541cf 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 a6b8888fce..b44ecd1665 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 98fd1a03fa..73a4179158 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 e8cdf0434d..7405362416 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 ceaa3fc9c0..2448e2e2bc 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 7c70d37a63..98658dbab5 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 5e9e0d45d3..7c8a90605b 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()