From 56573f14b03d4703bd1ffe8c9d29f97fe5398b1c Mon Sep 17 00:00:00 2001 From: Jellifi007 Date: Fri, 15 Apr 2022 20:14:13 +0200 Subject: [PATCH 01/17] Fixes diactritics regressions --- Emby.Dlna/Service/BaseControlHandler.cs | 2 +- .../Data/SqliteItemRepository.cs | 1 - .../Library/SearchEngine.cs | 2 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 2 +- .../Entities/Audio/MusicArtist.cs | 2 +- .../Entities/Audio/MusicGenre.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 1 - MediaBrowser.Controller/Entities/Genre.cs | 2 +- MediaBrowser.Controller/Entities/Person.cs | 2 +- MediaBrowser.Controller/Entities/Studio.cs | 2 +- .../Library/NameExtensions.cs | 2 +- .../MediaBrowser.Controller.csproj | 1 - .../Manager/MetadataService.cs | 2 +- .../MusicBrainz/MusicBrainzArtistProvider.cs | 2 +- src/Jellyfin.Extensions/StringExtensions.cs | 42 +++++++++++++++++++ .../StringExtensionsTests.cs | 28 +++++++++++++ 16 files changed, 81 insertions(+), 14 deletions(-) diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 7bec2eb728..9c423b3958 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -6,8 +6,8 @@ using System.IO; using System.Text; using System.Threading.Tasks; using System.Xml; -using Diacritics.Extensions; using Emby.Dlna.Didl; +using Jellyfin.Extensions; using MediaBrowser.Controller.Configuration; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 5729a70ace..4eecab60e9 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Text; using System.Text.Json; using System.Threading; -using Diacritics.Extensions; using Emby.Server.Implementations.Playlists; using Jellyfin.Data.Enums; using Jellyfin.Extensions; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 96702d1520..60778a443e 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -5,9 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 1fa8e570da..2358fe6238 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using BlurHashSharp.SkiaSharp; -using Diacritics.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Drawing; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 0f2d7e62dc..15a79fa1fc 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -8,9 +8,9 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 73a25232e4..7448d02ea5 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Diacritics.Extensions; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities.Audio diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2bb966d2c5..5cee6ce406 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -11,7 +11,6 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 4be6732372..ddf62dd4cb 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Diacritics.Extensions; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 045c1b89fd..7f265084fb 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Diacritics.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Providers; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index c8feb1c946..a3736a4bfc 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Diacritics.Extensions; +using Jellyfin.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs index d2ed3465a8..9d78b8b6c8 100644 --- a/MediaBrowser.Controller/Library/NameExtensions.cs +++ b/MediaBrowser.Controller/Library/NameExtensions.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Diacritics.Extensions; +using Jellyfin.Extensions; namespace MediaBrowser.Controller.Library { diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6164e51cd3..ecb8ef104d 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -18,7 +18,6 @@ - diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 0c52d26736..6d767914f7 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Diacritics.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs index 1feb7f4eab..906a42f36d 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs @@ -13,7 +13,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Diacritics.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index 3a77072539..dadc9f1d5c 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -1,4 +1,8 @@ using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; namespace Jellyfin.Extensions { @@ -7,6 +11,44 @@ namespace Jellyfin.Extensions /// public static class StringExtensions { + // Matches non-conforming unicode chars + // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/ + private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((? + /// Removes the diacritics character from the strings. + /// + /// The string to act on. + /// The string without diacritics character. + public static string RemoveDiacritics(this string text) + { + string withDiactritics = _nonConformingUnicode + .Replace(text, string.Empty) + .Normalize(NormalizationForm.FormD); + + var withoutDiactritics = new StringBuilder(); + foreach (char c in withDiactritics) + { + UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c); + if (uc != UnicodeCategory.NonSpacingMark) + { + withoutDiactritics.Append(c); + } + } + + return withoutDiactritics.ToString().Normalize(NormalizationForm.FormC); + } + + /// + /// Checks wether or not the specified string has diacritics in it. + /// + /// The string to check. + /// True if the string has diacritics, false otherwise. + public static bool HasDiacritics(this string text) + { + return !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); + } + /// /// Counts the number of occurrences of [needle] in the string. /// diff --git a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs index 7186cc0236..b3615e50a3 100644 --- a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs +++ b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs @@ -5,6 +5,34 @@ namespace Jellyfin.Extensions.Tests { public class StringExtensionsTests { + [Theory] + [InlineData("", "")] // Identity edge-case (no diactritics) + [InlineData("Indiana Jones", "Indiana Jones")] // Identity (no diactritics) + [InlineData("a\ud800b", "ab")] // Invalid UTF-16 char stripping + [InlineData("Jön", "Jon")] // Issue #7484 + [InlineData("Jönssonligan", "Jonssonligan")] // Issue #7484 + [InlineData("Kieślowski", "Kieslowski")] // Issue #7450 + [InlineData("Cidadão Kane", "Cidadao Kane")] // Issue #7560 + public void RemoveDiacritics_ValidInput_Corrects(string input, string expectedResult) + { + string result = input.RemoveDiacritics(); + Assert.Equal(expectedResult, result); + } + + [Theory] + [InlineData("", false)] // Identity edge-case (no diactritics) + [InlineData("Indiana Jones", false)] // Identity (no diactritics) + [InlineData("a\ud800b", true)] // Invalid UTF-16 char stripping + [InlineData("Jön", true)] // Issue #7484 + [InlineData("Jönssonligan", true)] // Issue #7484 + [InlineData("Kieślowski", true)] // Issue #7450 + [InlineData("Cidadão Kane", true)] // Issue #7560 + public void HasDiacritics_ValidInput_Corrects(string input, bool expectedResult) + { + bool result = input.HasDiacritics(); + Assert.Equal(expectedResult, result); + } + [Theory] [InlineData("", '_', 0)] [InlineData("___", '_', 3)] From 2b7d139b5b6a86bc742cfcd2312379d5c70f7529 Mon Sep 17 00:00:00 2001 From: JaanTaponen Date: Sun, 17 Apr 2022 16:46:01 +0300 Subject: [PATCH 02/17] Fix XMLTV edge case where title & sub-title causes a too long filename error Limit the series title to 241 bytes (so file extensions can be added). If the end result is longer, the title will be dropped and only series name with timestamp is used. --- .../LiveTv/EmbyTV/RecordingHelper.cs | 14 +++++++++++--- .../LiveTv/RecordingHelperTests.cs | 11 +++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 32245f899e..87f94cb085 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -3,6 +3,7 @@ using System; using System.Globalization; using MediaBrowser.Controller.LiveTv; +using System.Text; namespace Emby.Server.Implementations.LiveTv.EmbyTV { @@ -48,12 +49,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (!string.IsNullOrWhiteSpace(info.EpisodeTitle)) { + var tmpName = name; if (addHyphen) { - name += " -"; + tmpName += " -"; + } + // Calculate the length of the resulting filename + var recordingNameLength = Encoding.UTF8.GetByteCount(tmpName) + Encoding.UTF8.GetByteCount(info.EpisodeTitle); + // Since the filename will be used with file ext. (.mp4, .ts, etc) + if (recordingNameLength < 250) + { + tmpName += " " + info.EpisodeTitle; + name = tmpName; } - - name += " " + info.EpisodeTitle; } } else if (info.IsMovie && info.ProductionYear != null) diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs index 09aec82b07..f107b1ef97 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs @@ -85,6 +85,17 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv EpisodeTitle = "The VCR Illumination" }); + data.Add( + "Lorem ipsum dolor sit amet: consect 2018_12_06_21_06_00", + new TimerInfo + { + Name = "Lorem ipsum dolor sit amet: consect", + IsProgramSeries = true, + StartDate = new DateTime(2018, 12, 6, 21, 6, 0, DateTimeKind.Local), + OriginalAirDate = new DateTime(2018, 12, 6), + EpisodeTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor" + }); + return data; } From 727402f7f7ae8db5bbc678470fb3e0709f6984fb Mon Sep 17 00:00:00 2001 From: Jellifi007 Date: Thu, 21 Apr 2022 22:17:25 +0200 Subject: [PATCH 03/17] Adding Korean language tests Addresses #6393 --- src/Jellyfin.Extensions/StringExtensions.cs | 3 ++- tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index dadc9f1d5c..0699ba7f3a 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -36,7 +36,8 @@ namespace Jellyfin.Extensions } } - return withoutDiactritics.ToString().Normalize(NormalizationForm.FormC); + string normalizedText = withoutDiactritics.ToString().Normalize(NormalizationForm.FormC); + return normalizedText; } /// diff --git a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs index b3615e50a3..903d88caa1 100644 --- a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs +++ b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs @@ -13,6 +13,8 @@ namespace Jellyfin.Extensions.Tests [InlineData("Jönssonligan", "Jonssonligan")] // Issue #7484 [InlineData("Kieślowski", "Kieslowski")] // Issue #7450 [InlineData("Cidadão Kane", "Cidadao Kane")] // Issue #7560 + [InlineData("운명처럼 널 사랑해", "운명처럼 널 사랑해")] // Issue #6393 (Korean language support) + [InlineData("애타는 로맨스", "애타는 로맨스")] // Issue #6393 public void RemoveDiacritics_ValidInput_Corrects(string input, string expectedResult) { string result = input.RemoveDiacritics(); @@ -27,6 +29,8 @@ namespace Jellyfin.Extensions.Tests [InlineData("Jönssonligan", true)] // Issue #7484 [InlineData("Kieślowski", true)] // Issue #7450 [InlineData("Cidadão Kane", true)] // Issue #7560 + [InlineData("운명처럼 널 사랑해", false)] // Issue #6393 (Korean language support) + [InlineData("애타는 로맨스", false)] // Issue #6393 public void HasDiacritics_ValidInput_Corrects(string input, bool expectedResult) { bool result = input.HasDiacritics(); From 1002d1d193670770dfc263dba5c527faf3683f92 Mon Sep 17 00:00:00 2001 From: Jellifi007 <93096110+Jellifi007@users.noreply.github.com> Date: Fri, 22 Apr 2022 09:01:19 +0200 Subject: [PATCH 04/17] Update src/Jellyfin.Extensions/StringExtensions.cs Co-authored-by: Cody Robibero --- src/Jellyfin.Extensions/StringExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index 0699ba7f3a..dadc9f1d5c 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -36,8 +36,7 @@ namespace Jellyfin.Extensions } } - string normalizedText = withoutDiactritics.ToString().Normalize(NormalizationForm.FormC); - return normalizedText; + return withoutDiactritics.ToString().Normalize(NormalizationForm.FormC); } /// From b6a2640f22ddfc76a871a1f3bf5fdc223b3b2db4 Mon Sep 17 00:00:00 2001 From: Jaan Taponen Date: Sat, 30 Apr 2022 22:24:11 +0300 Subject: [PATCH 05/17] Update Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs Co-authored-by: Joe Rogers <1337joe@users.noreply.github.com> --- .../LiveTv/EmbyTV/RecordingHelper.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 87f94cb085..6ad9ccdf6e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -54,12 +54,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { tmpName += " -"; } - // Calculate the length of the resulting filename - var recordingNameLength = Encoding.UTF8.GetByteCount(tmpName) + Encoding.UTF8.GetByteCount(info.EpisodeTitle); + + tmpName += " " + info.EpisodeTitle; // Since the filename will be used with file ext. (.mp4, .ts, etc) - if (recordingNameLength < 250) + if (Encoding.UTF8.GetByteCount(tmpName) < 250) { - tmpName += " " + info.EpisodeTitle; name = tmpName; } } From 21ce0e58c659a976b62e1e0bc0ac37c081f41c58 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Thu, 31 Mar 2022 02:34:42 +0200 Subject: [PATCH 06/17] Properly handle stream addition and removal for strm use cases --- .../Library/MediaSourceManager.cs | 4 +++- .../MediaEncoding/EncodingHelper.cs | 3 ++- .../MediaInfo/FFProbeVideoInfo.cs | 20 +++++++++++++------ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index d9a1a54872..41f0401615 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -151,7 +151,9 @@ namespace Emby.Server.Implementations.Library { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); - if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video)) + if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder + && (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video) + || item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio))) { await item.RefreshMetadata( new MetadataRefreshOptions(_directoryService) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 006fd88610..897fd82b48 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2228,13 +2228,14 @@ namespace MediaBrowser.Controller.MediaEncoding } var args = string.Empty; + var numberOfExternalStreams = state.MediaSource.MediaStreams.Where(stream => stream.IsExternal == true).Count(); if (state.VideoStream != null) { args += string.Format( CultureInfo.InvariantCulture, "-map 0:{0}", - state.VideoStream.Index); + state.VideoStream.Index - numberOfExternalStreams); } else { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 4a289b3ab4..6208186ab9 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -173,9 +173,22 @@ namespace MediaBrowser.Providers.MediaInfo IReadOnlyList mediaAttachments; ChapterInfo[] chapters; + mediaStreams = new List(); + + // Add external streams before adding the streams from the file to preserve stream IDs on remote videos + await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + + await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + if (mediaInfo != null) { - mediaStreams = mediaInfo.MediaStreams.ToList(); + var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Select(i => i.Index).Max() + 1); + foreach (var mediaStream in mediaInfo.MediaStreams) + { + mediaStream.Index = startIndex++; + mediaStreams.Add(mediaStream); + } + mediaAttachments = mediaInfo.MediaAttachments; video.TotalBitrate = mediaInfo.Bitrate; @@ -213,15 +226,10 @@ namespace MediaBrowser.Providers.MediaInfo } else { - mediaStreams = new List(); mediaAttachments = Array.Empty(); chapters = Array.Empty(); } - await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - - await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - var libraryOptions = _libraryManager.GetLibraryOptions(video); if (mediaInfo != null) From 128d54622ad07985688cccf30a897257bf9ea2c2 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 1 Apr 2022 02:59:51 +0200 Subject: [PATCH 07/17] Fix stream index and subtitle container handling, preserve attachments and nonexternal streams between scans --- .../Library/MediaSourceManager.cs | 6 ++-- .../MediaEncoding/EncodingHelper.cs | 30 ++++++++++++------- .../MediaEncoding/IMediaEncoder.cs | 7 +++++ .../Encoder/MediaEncoder.cs | 13 ++++++++ .../Subtitles/SubtitleEncoder.cs | 19 ++++++++---- .../MediaInfo/FFProbeVideoInfo.cs | 15 ++++++++-- 6 files changed, 70 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 41f0401615..4af8c9feb5 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -151,9 +151,11 @@ namespace Emby.Server.Implementations.Library { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); + // If file is strm or main media stream is missing, force a metadata refresh with remote probing if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder - && (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video) - || item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio))) + && (item.Path.EndsWith(".strm") + || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video)) + || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio)))) { await item.RefreshMetadata( new MetadataRefreshOptions(_directoryService) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 897fd82b48..adac6d280a 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2228,14 +2228,14 @@ namespace MediaBrowser.Controller.MediaEncoding } var args = string.Empty; - var numberOfExternalStreams = state.MediaSource.MediaStreams.Where(stream => stream.IsExternal == true).Count(); + int videoStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.VideoStream.Path).ToList().IndexOf(state.VideoStream); if (state.VideoStream != null) { args += string.Format( CultureInfo.InvariantCulture, "-map 0:{0}", - state.VideoStream.Index - numberOfExternalStreams); + videoStreamIndex); } else { @@ -2249,20 +2249,22 @@ namespace MediaBrowser.Controller.MediaEncoding { bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream; int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1; - int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); + int externalAudioStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); args += string.Format( CultureInfo.InvariantCulture, " -map {0}:{1}", externalAudioMapIndex, - externalAudioStream); + externalAudioStreamIndex); } else { + int subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); + args += string.Format( CultureInfo.InvariantCulture, " -map 0:{0}", - state.AudioStream.Index); + subtitleStreamIndex); } } else @@ -2277,14 +2279,21 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (subtitleMethod == SubtitleDeliveryMethod.Embed) { + int subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); + args += string.Format( CultureInfo.InvariantCulture, " -map 0:{0}", - state.SubtitleStream.Index); + subtitleStreamIndex); } else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { - args += " -map 1:0 -sn"; + int externalSubtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); + + args += string.Format( + CultureInfo.InvariantCulture, + " -map 1:{0} -sn", + externalSubtitleStreamIndex); } return args; @@ -4130,9 +4139,8 @@ namespace MediaBrowser.Controller.MediaEncoding string.Join(',', overlayFilters)); var mapPrefix = Convert.ToInt32(state.SubtitleStream.IsExternal); - var subtitleStreamIndex = state.SubtitleStream.IsExternal - ? 0 - : state.SubtitleStream.Index; + var subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); + var videoStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.VideoStream.Path).ToList().IndexOf(state.VideoStream); if (hasSubs) { @@ -4153,7 +4161,7 @@ namespace MediaBrowser.Controller.MediaEncoding filterStr, mapPrefix, subtitleStreamIndex, - state.VideoStream.Index, + videoStreamIndex, mainStr, subStr, overlayStr); diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 6bf3e7b469..dae30cd8b3 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -141,6 +141,13 @@ namespace MediaBrowser.Controller.MediaEncoding /// System.String. string GetInputArgument(string inputFile, MediaSourceInfo mediaSource); + /// + /// Gets the input argument for an external subtitle file. + /// + /// The input file. + /// System.String. + string GetExternalSubtitleInputArgument(string inputFile); + /// /// Gets the time parameter. /// diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c796ee7806..7e5d7a25f8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -411,6 +411,19 @@ namespace MediaBrowser.MediaEncoding.Encoder return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol); } + /// + /// Gets the input argument for an external subtitle file. + /// + /// The input file. + /// System.String. + /// Unrecognized InputType. + public string GetExternalSubtitleInputArgument(string inputFile) + { + var prefix = "file"; + + return EncodingUtils.GetInputArgument(prefix, inputFile, MediaProtocol.File); + } + /// /// Gets the media info internal. /// diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index f4842d3682..717624279a 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles MediaStream subtitleStream, CancellationToken cancellationToken) { - if (!subtitleStream.IsExternal) + if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks")) { string outputFormat; string outputCodec; @@ -224,7 +224,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles // Extract var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat); - await ExtractTextSubtitle(mediaSource, subtitleStream.Index, outputCodec, outputPath, cancellationToken) + await ExtractTextSubtitle(mediaSource, subtitleStream, outputCodec, outputPath, cancellationToken) .ConfigureAwait(false); return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false); @@ -494,7 +494,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Extracts the text subtitle. /// /// The mediaSource. - /// Index of the subtitle stream. + /// The subtitle stream. /// The output codec. /// The output path. /// The cancellation token. @@ -502,7 +502,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Must use inputPath list overload. private async Task ExtractTextSubtitle( MediaSourceInfo mediaSource, - int subtitleStreamIndex, + MediaStream subtitleStream, string outputCodec, string outputPath, CancellationToken cancellationToken) @@ -511,12 +511,21 @@ namespace MediaBrowser.MediaEncoding.Subtitles await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + var subtitleStreamIndex = mediaSource.MediaStreams.Where(i => i.Path == subtitleStream.Path).ToList().IndexOf(subtitleStream); + try { if (!File.Exists(outputPath)) { + var args = _mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource); + + if (subtitleStream.IsExternal) + { + args = _mediaEncoder.GetExternalSubtitleInputArgument(subtitleStream.Path); + } + await ExtractTextSubtitleInternal( - _mediaEncoder.GetInputArgument(mediaSource.Path, mediaSource), + args, subtitleStreamIndex, outputCodec, outputPath, diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 6208186ab9..c9f7870483 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -180,9 +180,10 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Select(i => i.Index).Max() + 1); + if (mediaInfo != null) { - var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Select(i => i.Index).Max() + 1); foreach (var mediaStream in mediaInfo.MediaStreams) { mediaStream.Index = startIndex++; @@ -226,6 +227,12 @@ namespace MediaBrowser.Providers.MediaInfo } else { + var nonExternalMediaStreams = video.GetMediaStreams().Where(i => !i.IsExternal); + foreach (var mediaStream in nonExternalMediaStreams) + { + mediaStream.Index = startIndex++; + mediaStreams.Add(mediaStream); + } mediaAttachments = Array.Empty(); chapters = Array.Empty(); } @@ -262,7 +269,11 @@ namespace MediaBrowser.Providers.MediaInfo video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); - _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken); + + if (mediaAttachments.Any()) + { + _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken); + } if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) From 0c8b9091a55f4be1b6dbb4a0725a05aca52b422f Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 3 May 2022 15:48:46 +0200 Subject: [PATCH 08/17] 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 09/17] 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 a4e4b761d5d17d7d17c04096bd0f825f9cbc7bf1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 2 Apr 2022 02:55:42 +0200 Subject: [PATCH 10/17] Apply review suggestions --- .../Data/SqliteItemRepository.cs | 6 +-- .../Library/MediaSourceManager.cs | 6 +-- .../MediaEncoding/EncodingHelper.cs | 45 ++++++++++++++----- .../Encoder/MediaEncoder.cs | 4 +- .../Subtitles/SubtitleEncoder.cs | 4 +- .../MediaInfo/FFProbeVideoInfo.cs | 16 ++++--- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 5729a70ace..cdc14c2605 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5763,7 +5763,7 @@ AND Type = @InternalPersonType)"); { var itemIdBlob = id.ToByteArray(); - // First delete chapters + // Delete existing mediastreams db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob); InsertMediaStreams(itemIdBlob, streams, db); @@ -5867,10 +5867,10 @@ AND Type = @InternalPersonType)"); } /// - /// Gets the chapter. + /// Gets the media stream. /// /// The reader. - /// ChapterInfo. + /// MediaStream. private MediaStream GetMediaStream(IReadOnlyList reader) { var item = new MediaStream diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 4af8c9feb5..c0aef18997 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -153,9 +153,9 @@ namespace Emby.Server.Implementations.Library // If file is strm or main media stream is missing, force a metadata refresh with remote probing if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder - && (item.Path.EndsWith(".strm") - || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video)) - || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio)))) + && (item.Path.EndsWith(".strm", StringComparison.OrdinalIgnoreCase) + || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video)) + || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio)))) { await item.RefreshMetadata( new MetadataRefreshOptions(_directoryService) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index adac6d280a..1851a9f0c5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2215,23 +2215,24 @@ namespace MediaBrowser.Controller.MediaEncoding return state.IsInputVideo ? "-sn" : string.Empty; } - // We have media info, but we don't know the stream indexes + // We have media info, but we don't know the stream index if (state.VideoStream != null && state.VideoStream.Index == -1) { return "-sn"; } - // We have media info, but we don't know the stream indexes + // We have media info, but we don't know the stream index if (state.AudioStream != null && state.AudioStream.Index == -1) { return state.IsInputVideo ? "-sn" : string.Empty; } var args = string.Empty; - int videoStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.VideoStream.Path).ToList().IndexOf(state.VideoStream); if (state.VideoStream != null) { + int videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream); + args += string.Format( CultureInfo.InvariantCulture, "-map 0:{0}", @@ -2245,26 +2246,24 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream != null) { + int audioStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.AudioStream); if (state.AudioStream.IsExternal) { bool hasExternalGraphicsSubs = state.SubtitleStream != null && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream; int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1; - int externalAudioStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); args += string.Format( CultureInfo.InvariantCulture, " -map {0}:{1}", externalAudioMapIndex, - externalAudioStreamIndex); + audioStreamIndex); } else { - int subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); - args += string.Format( CultureInfo.InvariantCulture, " -map 0:{0}", - subtitleStreamIndex); + audioStreamIndex); } } else @@ -2279,7 +2278,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (subtitleMethod == SubtitleDeliveryMethod.Embed) { - int subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); + int subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream); args += string.Format( CultureInfo.InvariantCulture, @@ -2288,7 +2287,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) { - int externalSubtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); + int externalSubtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream); args += string.Format( CultureInfo.InvariantCulture, @@ -4139,8 +4138,8 @@ namespace MediaBrowser.Controller.MediaEncoding string.Join(',', overlayFilters)); var mapPrefix = Convert.ToInt32(state.SubtitleStream.IsExternal); - var subtitleStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.SubtitleStream.Path).ToList().IndexOf(state.SubtitleStream); - var videoStreamIndex = state.MediaSource.MediaStreams.Where(i => i.Path == state.VideoStream.Path).ToList().IndexOf(state.VideoStream); + var subtitleStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.SubtitleStream); + var videoStreamIndex = FindIndex(state.MediaSource.MediaStreams, state.VideoStream); if (hasSubs) { @@ -5398,6 +5397,28 @@ namespace MediaBrowser.Controller.MediaEncoding string.Empty).Trim(); } + public static int FindIndex(IReadOnlyList mediaStreams, MediaStream streamToFind) + { + var index = 0; + var length = mediaStreams.Count; + + for (var i = 0; i < length; i++) + { + var currentMediaStream = mediaStreams[i]; + if (currentMediaStream == streamToFind) + { + return index; + } + + if (string.Equals(currentMediaStream.Path, streamToFind.Path, StringComparison.Ordinal)) + { + index++; + } + } + + return -1; + } + public static bool IsCopyCodec(string codec) { return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7e5d7a25f8..1bac4b1875 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -419,9 +419,9 @@ namespace MediaBrowser.MediaEncoding.Encoder /// Unrecognized InputType. public string GetExternalSubtitleInputArgument(string inputFile) { - var prefix = "file"; + const string Prefix = "file"; - return EncodingUtils.GetInputArgument(prefix, inputFile, MediaProtocol.File); + return EncodingUtils.GetInputArgument(Prefix, inputFile, MediaProtocol.File); } /// diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 717624279a..f6b7efb1e9 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -195,7 +195,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles MediaStream subtitleStream, CancellationToken cancellationToken) { - if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks")) + if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) { string outputFormat; string outputCodec; @@ -511,7 +511,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - var subtitleStreamIndex = mediaSource.MediaStreams.Where(i => i.Path == subtitleStream.Path).ToList().IndexOf(subtitleStream); + var subtitleStreamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream); try { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index c9f7870483..8c08ab30ef 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -180,7 +180,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Select(i => i.Index).Max() + 1); + var startIndex = mediaStreams.Count == 0 ? 0 : (mediaStreams.Max(i => i.Index) + 1); if (mediaInfo != null) { @@ -196,7 +196,7 @@ namespace MediaBrowser.Providers.MediaInfo // video.FormatName = (mediaInfo.Container ?? string.Empty) // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); - // For dvd's this may not always be accurate, so don't set the runtime if the item already has one + // For DVDs this may not always be accurate, so don't set the runtime if the item already has one var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; if (needToSetRuntime) @@ -227,12 +227,16 @@ namespace MediaBrowser.Providers.MediaInfo } else { - var nonExternalMediaStreams = video.GetMediaStreams().Where(i => !i.IsExternal); - foreach (var mediaStream in nonExternalMediaStreams) + var currentMediaStreams = video.GetMediaStreams(); + foreach (var mediaStream in currentMediaStreams) { - mediaStream.Index = startIndex++; - mediaStreams.Add(mediaStream); + if (!mediaStream.IsExternal) + { + mediaStream.Index = startIndex++; + mediaStreams.Add(mediaStream); + } } + mediaAttachments = Array.Empty(); chapters = Array.Empty(); } From c69e79f64d5b007c857792f4add82c27b130756e Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sat, 7 May 2022 22:43:32 +0800 Subject: [PATCH 11/17] Fix the disordered color in Dolby Vision remuxing on Safari --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index f8e8d975cc..6347b908ca 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1773,13 +1773,23 @@ namespace Jellyfin.Api.Controllers var args = "-codec:v:0 " + codec; - // Prefer hvc1 to hev1. if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { - args += " -tag:v:0 hvc1"; + if (EncodingHelper.IsCopyCodec(codec) + && (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"; + } + else + { + // Prefer hvc1 to hev1 + args += " -tag:v:0 hvc1"; + } } // if (state.EnableMpegtsM2TsMode) From 96b7c46df482dc04bc9b75c92c0a0bd276d26a30 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 8 May 2022 13:28:57 -0600 Subject: [PATCH 12/17] Update TMDbLib to 1.9.2 --- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 535f1b16f4..8d242d13d1 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,7 +22,7 @@ - + From 029be321d1c951abdf79cfea038f50ec399fdfb9 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 6 May 2022 23:18:45 +0200 Subject: [PATCH 13/17] Respect limited opus sampling rates when building trancoding command --- .../MediaEncoding/EncodingHelper.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1851a9f0c5..54b41a6d25 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5370,12 +5370,22 @@ namespace MediaBrowser.Controller.MediaEncoding audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); } - // opus will fail on 44100 if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) { - if (state.OutputAudioSampleRate.HasValue) + // opus only supports specific sampling rates + var sampleRate = state.OutputAudioSampleRate; + if (sampleRate.HasValue) { - audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)); + var sampleRateValue = sampleRate.Value switch + { + <= 8000 => 8000, + <= 12000 => 12000, + <= 16000 => 16000, + <= 24000 => 24000, + _ => 48000 + }; + + audioTranscodeParams.Add("-ar " + sampleRateValue.ToString(CultureInfo.InvariantCulture)); } } From 368d10d042e89d47dd493c77c33f8b5bff020918 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Wed, 11 May 2022 19:18:31 +0800 Subject: [PATCH 14/17] 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 15/17] 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 16/17] 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 17/17] 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