From 1db748399cfad140c28e92cd44a5ea630a0bd5ea Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 8 Jul 2022 19:44:15 +0200 Subject: [PATCH 01/28] feat: make subtitleeditparser generic --- .../Subtitles/AssParser.cs | 19 ------------ .../Subtitles/ISubtitleParser.cs | 5 ++-- .../Subtitles/SrtParser.cs | 19 ------------ .../Subtitles/SsaParser.cs | 19 ------------ .../Subtitles/SubtitleEditParser.cs | 29 ++++++++++--------- .../Subtitles/SubtitleEncoder.cs | 24 ++------------- .../Subtitles/AssParserTests.cs | 2 +- .../Subtitles/SrtParserTests.cs | 4 +-- .../Subtitles/SsaParserTests.cs | 6 ++-- 9 files changed, 27 insertions(+), 100 deletions(-) delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssParser.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs deleted file mode 100644 index 08ee5c72e5..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// Advanced SubStation Alpha subtitle parser. - /// - public class AssParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public AssParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index c0023ebf24..66d1776a48 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System.IO; -using System.Threading; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles @@ -12,8 +11,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Parses the specified stream. /// /// The stream. - /// The cancellation token. + /// The file extension. /// SubtitleTrackInfo. - SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken); + SubtitleTrackInfo Parse(Stream stream, string fileExtension); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs deleted file mode 100644 index 78d54ca51f..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SubRip subtitle parser. - /// - public class SrtParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SrtParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs deleted file mode 100644 index 17c2ae40e0..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SubStation Alpha subtitle parser. - /// - public class SsaParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SsaParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 52c1b64677..da57e6f2a1 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,7 +1,7 @@ +using System; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; @@ -14,31 +14,34 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// SubStation Alpha subtitle parser. /// - /// The . - public abstract class SubtitleEditParser : ISubtitleParser - where T : SubtitleFormat, new() + public class SubtitleEditParser : ISubtitleParser { private readonly ILogger _logger; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The logger. - protected SubtitleEditParser(ILogger logger) + public SubtitleEditParser(ILogger logger) { _logger = logger; } /// - public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) + public SubtitleTrackInfo Parse(Stream stream, string fileExtension) { - var subtitle = new Subtitle(); - var subRip = new T(); - var lines = stream.ReadAllLines().ToList(); - subRip.LoadSubtitle(subtitle, lines, "untitled"); - if (subRip.ErrorCount > 0) + var subtitleFormat = SubtitleFormat.AllSubtitleFormats.FirstOrDefault(asf => asf.Extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)); + if (subtitleFormat == null) { - _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount); + throw new ArgumentException("Unsupported format: " + fileExtension); + } + + var lines = stream.ReadAllLines().ToList(); + var subtitle = new Subtitle(); + subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); + if (subtitleFormat.ErrorCount > 0) + { + _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subtitleFormat.ErrorCount); } var trackInfo = new SubtitleTrackInfo(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7091af734a..a0709d1e7b 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -74,7 +74,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { var reader = GetReader(inputFormat); - var trackInfo = reader.Parse(stream, cancellationToken); + var trackInfo = reader.Parse(stream, $".{inputFormat}"); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -249,26 +249,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) { - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) - { - value = new SrtParser(_logger); - return true; - } - - if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) - { - value = new SsaParser(_logger); - return true; - } - - if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) - { - value = new AssParser(_logger); - return true; - } - - value = null; - return false; + value = new SubtitleEditParser(_logger); + return true; } private ISubtitleParser GetReader(string format) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index 3775555dec..395dfa309f 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ass")) { - var parsed = new AssParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".ass"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index c07c9ea7db..e9745f17dc 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.srt")) { - var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; @@ -37,7 +37,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example2.srt")) { - var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 56649db8f8..bb6ed22eb2 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { public class SsaParserTests { - private readonly SsaParser _parser = new SsaParser(new NullLogger()); + private readonly SubtitleEditParser _parser = new SubtitleEditParser(new NullLogger()); [Theory] [MemberData(nameof(Parse_MultipleDialogues_TestData))] @@ -21,7 +21,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None); + SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, ".ssa"); Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count); @@ -76,7 +76,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ssa")) { - var parsed = _parser.Parse(stream, CancellationToken.None); + var parsed = _parser.Parse(stream, ".ssa"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; From 78f437401b836edc8fc535eb5d46506e58a64329 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 8 Jul 2022 20:11:00 +0200 Subject: [PATCH 02/28] loop over all compatible SubtitleFormats --- .../Subtitles/SubtitleEditParser.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index da57e6f2a1..c487f9ac49 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -30,17 +30,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// public SubtitleTrackInfo Parse(Stream stream, string fileExtension) { - var subtitleFormat = SubtitleFormat.AllSubtitleFormats.FirstOrDefault(asf => asf.Extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)); - if (subtitleFormat == null) - { - throw new ArgumentException("Unsupported format: " + fileExtension); - } - - var lines = stream.ReadAllLines().ToList(); var subtitle = new Subtitle(); - subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); - if (subtitleFormat.ErrorCount > 0) + var lines = stream.ReadAllLines().ToList(); + + var subtitleFormats = SubtitleFormat.AllSubtitleFormats.Where(asf => asf.Extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)); + foreach (var subtitleFormat in subtitleFormats) { + if (subtitleFormat == null) + { + throw new ArgumentException("Unsupported format: " + fileExtension); + } + + subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); + if (subtitleFormat.ErrorCount == 0) + { + break; + } + _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subtitleFormat.ErrorCount); } From dbfa0f30276054b63402e95867ed9a9f1b1cca08 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 8 Jul 2022 20:12:00 +0200 Subject: [PATCH 03/28] fix unsupported --- .../Subtitles/SubtitleEditParser.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index c487f9ac49..4e6403e986 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -36,11 +36,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitleFormats = SubtitleFormat.AllSubtitleFormats.Where(asf => asf.Extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)); foreach (var subtitleFormat in subtitleFormats) { - if (subtitleFormat == null) - { - throw new ArgumentException("Unsupported format: " + fileExtension); - } - subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); if (subtitleFormat.ErrorCount == 0) { @@ -50,6 +45,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subtitleFormat.ErrorCount); } + if (subtitle.Paragraphs.Count == 0) + { + throw new ArgumentException("Unsupported format: " + fileExtension); + } + var trackInfo = new SubtitleTrackInfo(); int len = subtitle.Paragraphs.Count; var trackEvents = new SubtitleTrackEvent[len]; From 126da94020385952cbba08dad76a575452b76aa8 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 25 Jul 2022 09:47:21 +0200 Subject: [PATCH 04/28] use reflection to get all subtitle formats without causing libse configuration loading --- .../ApplicationHost.cs | 4 +- .../Subtitles/ISubtitleParser.cs | 7 ++ .../Subtitles/SubtitleEditParser.cs | 118 +++++++++++++++++- .../Subtitles/SubtitleEncoder.cs | 29 ++--- 4 files changed, 131 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 32289625fe..2f47d43984 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -83,6 +83,7 @@ using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; @@ -634,7 +635,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index 66d1776a48..bd13437fb6 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -14,5 +14,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// The file extension. /// SubtitleTrackInfo. SubtitleTrackInfo Parse(Stream stream, string fileExtension); + + /// + /// Determines whether the file extension is supported by the parser. + /// + /// The file extension. + /// A value indicating whether the file extension is supported. + bool SupportsFileExtension(string fileExtension); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 4e6403e986..c94242b4b9 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,12 +1,14 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; -using ILogger = Microsoft.Extensions.Logging.ILogger; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles @@ -16,15 +18,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// public class SubtitleEditParser : ISubtitleParser { - private readonly ILogger _logger; + private readonly ILogger _logger; + private readonly Dictionary _subtitleFormats; /// /// Initializes a new instance of the class. /// /// The logger. - public SubtitleEditParser(ILogger logger) + public SubtitleEditParser(ILogger logger) { _logger = logger; + _subtitleFormats = GetSubtitleFormats() + .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension)) + .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase) + .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); } /// @@ -33,16 +40,28 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitle = new Subtitle(); var lines = stream.ReadAllLines().ToList(); - var subtitleFormats = SubtitleFormat.AllSubtitleFormats.Where(asf => asf.Extension.Equals(fileExtension, StringComparison.OrdinalIgnoreCase)); + if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats)) + { + throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); + } + foreach (var subtitleFormat in subtitleFormats) { + _logger.LogDebug( + "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", + fileExtension, + subtitleFormat.Name); subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); if (subtitleFormat.ErrorCount == 0) { break; } - _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subtitleFormat.ErrorCount); + _logger.LogError( + "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", + subtitleFormat.ErrorCount, + fileExtension, + subtitleFormat.Name); } if (subtitle.Paragraphs.Count == 0) @@ -66,5 +85,94 @@ namespace MediaBrowser.MediaEncoding.Subtitles trackInfo.TrackEvents = trackEvents; return trackInfo; } + + /// + public bool SupportsFileExtension(string fileExtension) + => _subtitleFormats.ContainsKey(fileExtension); + + private IEnumerable GetSubtitleFormats() + { + var subtitleFormats = new List(); + var assembly = Assembly.GetAssembly(typeof(SubtitleFormat)); + if (assembly == null) + { + _logger.LogError("Missing assembly containing {SubtitleFormatName}", nameof(SubtitleFormat)); + return GetFallbackSubtitleFormats(); + } + + foreach (var type in assembly.GetTypes()) + { + if (!type.IsSubclassOf(typeof(SubtitleFormat))) + { + continue; + } + + try + { + // It shouldn't be null, but the exception is caught if it is + var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!; + subtitleFormats.Add(subtitleFormat); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name); + } + } + + return subtitleFormats; + } + + private static IEnumerable GetFallbackSubtitleFormats() + => new SubtitleFormat[] + { + // Preferred and likely more common formats + new SubRip(), + new WebVTT(), + new WebVTTFileWithLineNumber(), + new AdvancedSubStationAlpha(), + new SubStationAlpha(), + new Sami(), + new SamiAvDicPlayer(), + new SamiModern(), + new SamiYouTube(), + new DvdSubtitle(), + new DvdSubtitleSystem(), + new JsonAeneas(), + new JsonTed(), + new Json(), + new JsonType2(), + new JsonType3(), + new JsonType4(), + new JsonType5(), + new JsonType6(), + new JsonType7(), + new JsonType8(), + new JsonType8b(), + new JsonType9(), + new JsonType10(), + new JsonType11(), + new JsonType12(), + new JsonType13(), + new JsonType14(), + new JsonType15(), + new JsonType16(), + new JsonType17(), + new JsonType18(), + new JsonType19(), + new JsonType20(), + new ItunesTimedText(), + new FLVCoreCuePoints(), + new Csv(), + new Csv2(), + new Csv3(), + new Csv4(), + new Csv5(), + new Ebu(), + new NetflixImsc11Japanese(), + new NetflixTimedText(), + new QuickTimeText(), + new RealTime(), + new SmpteTt2052() + }; } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index a0709d1e7b..50c4d92103 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IMediaEncoder _mediaEncoder; private readonly IHttpClientFactory _httpClientFactory; private readonly IMediaSourceManager _mediaSourceManager; + private readonly ISubtitleParser _subtitleParser; /// /// The _semaphoreLocks. @@ -48,7 +49,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles IFileSystem fileSystem, IMediaEncoder mediaEncoder, IHttpClientFactory httpClientFactory, - IMediaSourceManager mediaSourceManager) + IMediaSourceManager mediaSourceManager, + ISubtitleParser subtitleParser) { _logger = logger; _appPaths = appPaths; @@ -56,6 +58,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _mediaEncoder = mediaEncoder; _httpClientFactory = httpClientFactory; _mediaSourceManager = mediaSourceManager; + _subtitleParser = subtitleParser; } private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles"); @@ -73,8 +76,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var reader = GetReader(inputFormat); - var trackInfo = reader.Parse(stream, $".{inputFormat}"); + var trackInfo = _subtitleParser.Parse(stream, inputFormat); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -233,7 +235,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); - if (!TryGetReader(currentFormat, out _)) + // Fallback to ffmpeg conversion + if (!_subtitleParser.SupportsFileExtension(currentFormat)) { // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); @@ -243,26 +246,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); } - // It's possbile that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) + // It's possible that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) return new SubtitleInfo(subtitleStream.Path, _mediaSourceManager.GetPathProtocol(subtitleStream.Path), currentFormat, true); } - private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) - { - value = new SubtitleEditParser(_logger); - return true; - } - - private ISubtitleParser GetReader(string format) - { - if (TryGetReader(format, out var reader)) - { - return reader; - } - - throw new ArgumentException("Unsupported format: " + format); - } - private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) From 3b69f38a1f6c5c9e1ccfa148980180900a10aa85 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 25 Jul 2022 09:51:03 +0200 Subject: [PATCH 05/28] fix tests --- .../Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs | 2 +- .../Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs | 4 ++-- .../Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index 395dfa309f..e14850eed7 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ass")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".ass"); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "ass"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index e9745f17dc..0038b18736 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".srt"); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; @@ -37,7 +37,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example2.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, ".srt"); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index bb6ed22eb2..3b9a71690c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -21,7 +21,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, ".ssa"); + SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, "ssa"); Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count); @@ -76,7 +76,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ssa")) { - var parsed = _parser.Parse(stream, ".ssa"); + var parsed = _parser.Parse(stream, "ssa"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; From 04b73cace60c3bd97812d54b9a6d15ea2a24ba2d Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 26 Jul 2022 23:06:11 +0800 Subject: [PATCH 06/28] Disable auto inserted SW scaler for HW decoders in case of changed resolution --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1d65f9a345..7c59fa300d 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -931,6 +931,13 @@ namespace MediaBrowser.Controller.MediaEncoding arg.Append(" -i \"").Append(state.AudioStream.Path).Append('"'); } + // Disable auto inserted SW scaler for HW decoders in case of changed resolution. + var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options)); + if (!isSwDecoder) + { + arg.Append(" -autoscale 0"); + } + return arg.ToString(); } From 82f362abd9f3f29150106120d08508cb0ec9bb79 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Tue, 26 Jul 2022 23:06:22 +0800 Subject: [PATCH 07/28] Fix DV P5 MKV remuxing --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 365e44e1a3..1e8d038751 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1790,7 +1790,8 @@ namespace Jellyfin.Api.Controllers || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { if (EncodingHelper.IsCopyCodec(codec) - && (string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase) + && (string.Equals(state.VideoStream.VideoRangeType, "DOVI", 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))) { From feb035b9e001374f8830521dbce57539f8fa2f47 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 27 Jul 2022 10:08:53 +0200 Subject: [PATCH 08/28] Extract external subs from container before determining character set --- .../MediaEncoding/EncodingHelper.cs | 13 ++- .../MediaEncoding/ISubtitleEncoder.cs | 9 +- .../Subtitles/SubtitleEncoder.cs | 87 ++++++++++--------- 3 files changed, 59 insertions(+), 50 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 1d65f9a345..abb6de4a4b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1145,16 +1145,15 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.SubtitleStream.IsExternal) { - var subtitlePath = state.SubtitleStream.Path; var charsetParam = string.Empty; if (!string.IsNullOrEmpty(state.SubtitleStream.Language)) { var charenc = _subtitleEncoder.GetSubtitleFileCharacterSet( - subtitlePath, - state.SubtitleStream.Language, - state.MediaSource.Protocol, - CancellationToken.None).GetAwaiter().GetResult(); + state.SubtitleStream, + state.SubtitleStream.Language, + state.MediaSource, + CancellationToken.None).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(charenc)) { @@ -1166,7 +1165,7 @@ namespace MediaBrowser.Controller.MediaEncoding return string.Format( CultureInfo.InvariantCulture, "subtitles=f='{0}'{1}{2}{3}{4}{5}", - _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath), + _mediaEncoder.EscapeSubtitleFilterPath(state.SubtitleStream.Path), charsetParam, alphaParam, sub2videoParam, @@ -5529,7 +5528,7 @@ namespace MediaBrowser.Controller.MediaEncoding return index; } - if (string.Equals(currentMediaStream.Path, streamToFind.Path, StringComparison.Ordinal)) + if (string.Equals(currentMediaStream.Path, streamToFind.Path, StringComparison.Ordinal)) { index++; } diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 4483cf708a..5bf83a9e31 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -6,7 +6,8 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.MediaEncoding { @@ -37,11 +38,11 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Gets the subtitle language encoding parameter. /// - /// The path. + /// The subtitle stream. /// The language. - /// The protocol. + /// The media source. /// The cancellation token. /// System.String. - Task GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken); + Task GetSubtitleFileCharacterSet(MediaStream subtitleStream, string language, MediaSourceInfo mediaSource, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7091af734a..7e42626980 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -238,7 +238,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); - await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false); + await ConvertTextSubtitleToSrt(subtitleStream, mediaSource, outputPath, cancellationToken).ConfigureAwait(false); return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); } @@ -351,13 +351,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// Converts the text subtitle to SRT. /// - /// The input path. - /// The language. + /// The subtitle stream. /// The input mediaSource. /// The output path. /// The cancellation token. /// Task. - private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken) + private async Task ConvertTextSubtitleToSrt(MediaStream subtitleStream, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken) { var semaphore = GetLock(outputPath); @@ -367,7 +366,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (!File.Exists(outputPath)) { - await ConvertTextSubtitleToSrtInternal(inputPath, language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false); + await ConvertTextSubtitleToSrtInternal(subtitleStream, mediaSource, outputPath, cancellationToken).ConfigureAwait(false); } } finally @@ -379,8 +378,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// Converts the text subtitle to SRT internal. /// - /// The input path. - /// The language. + /// The subtitle stream. /// The input mediaSource. /// The output path. /// The cancellation token. @@ -388,8 +386,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// The inputPath or outputPath is null. /// - private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken) + private async Task ConvertTextSubtitleToSrtInternal(MediaStream subtitleStream, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken) { + var inputPath = subtitleStream.Path; if (string.IsNullOrEmpty(inputPath)) { throw new ArgumentNullException(nameof(inputPath)); @@ -402,7 +401,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath))); - var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, mediaSource.Protocol, cancellationToken).ConfigureAwait(false); + var encodingParam = await GetSubtitleFileCharacterSet(subtitleStream, subtitleStream.Language, mediaSource, cancellationToken).ConfigureAwait(false); // FFmpeg automatically convert character encoding when it is UTF-16 // If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event" @@ -420,18 +419,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles int exitCode; using (var process = new Process + { + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = _mediaEncoder.EncoderPath, - Arguments = string.Format(CultureInfo.InvariantCulture, "{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - EnableRaisingEvents = true - }) + CreateNoWindow = true, + UseShellExecute = false, + FileName = _mediaEncoder.EncoderPath, + Arguments = string.Format(CultureInfo.InvariantCulture, "{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + EnableRaisingEvents = true + }) { _logger.LogInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -580,18 +579,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles int exitCode; using (var process = new Process + { + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = _mediaEncoder.EncoderPath, - Arguments = processArgs, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - EnableRaisingEvents = true - }) + CreateNoWindow = true, + UseShellExecute = false, + FileName = _mediaEncoder.EncoderPath, + Arguments = processArgs, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }, + EnableRaisingEvents = true + }) { _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -729,9 +728,19 @@ namespace MediaBrowser.MediaEncoding.Subtitles } /// - public async Task GetSubtitleFileCharacterSet(string path, string language, MediaProtocol protocol, CancellationToken cancellationToken) + public async Task GetSubtitleFileCharacterSet(MediaStream subtitleStream, string language, MediaSourceInfo mediaSource, CancellationToken cancellationToken) { - using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) + var subtitleCodec = subtitleStream.Codec; + var path = subtitleStream.Path; + + if (path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) + { + path = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + subtitleCodec); + await ExtractTextSubtitle(mediaSource, subtitleStream, subtitleCodec, path, cancellationToken) + .ConfigureAwait(false); + } + + using (var stream = await GetStream(path, mediaSource.Protocol, cancellationToken).ConfigureAwait(false)) { var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName ?? string.Empty; @@ -754,12 +763,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles switch (protocol) { case MediaProtocol.Http: - { - using var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .GetAsync(new Uri(path), cancellationToken) - .ConfigureAwait(false); - return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - } + { + using var response = await _httpClientFactory.CreateClient(NamedClient.Default) + .GetAsync(new Uri(path), cancellationToken) + .ConfigureAwait(false); + return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + } case MediaProtocol.File: return AsyncFile.OpenRead(path); From b0b4068ddfaeed023d20c0c96ffa992bcaaee505 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 30 Jul 2022 14:59:00 +0200 Subject: [PATCH 09/28] Update MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs Co-authored-by: Bond-009 --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index c94242b4b9..e95cb12d2c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles foreach (var type in assembly.GetTypes()) { - if (!type.IsSubclassOf(typeof(SubtitleFormat))) + if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract) { continue; } From f2c7bccb891293cec9ca814e285ea1bb7f6c1155 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Sat, 30 Jul 2022 14:59:28 +0200 Subject: [PATCH 10/28] Update MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs Co-authored-by: Bond-009 --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index e95cb12d2c..ef88882714 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private IEnumerable GetSubtitleFormats() { var subtitleFormats = new List(); - var assembly = Assembly.GetAssembly(typeof(SubtitleFormat)); + var assembly = typeof(SubtitleFormat).Assembly; if (assembly == null) { _logger.LogError("Missing assembly containing {SubtitleFormatName}", nameof(SubtitleFormat)); From 56c81696d3c26553a2b366f15c0d685837d4f0bd Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 30 Jul 2022 21:50:53 +0200 Subject: [PATCH 11/28] fix: remove Virtual episodes when their physical counterpart exists --- MediaBrowser.Controller/Entities/TV/Series.cs | 8 +-- .../TV/SeriesMetadataService.cs | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index a3c4a81fdc..d9efd6b736 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -258,14 +258,10 @@ namespace MediaBrowser.Controller.Entities.TV SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season }, OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, - DtoOptions = options + DtoOptions = options, + IsMissing = user?.DisplayMissingEpisodes }; - if (!user.DisplayMissingEpisodes) - { - query.IsMissing = false; - } - var allItems = LibraryManager.GetItemList(query); var allSeriesEpisodes = allItems.OfType().ToList(); diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index f49492f337..c09b6d8138 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -40,6 +41,7 @@ namespace MediaBrowser.Providers.TV { await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false); + RemoveObsoleteEpisodes(item); RemoveObsoleteSeasons(item); await FillInMissingSeasonsAsync(item, cancellationToken).ConfigureAwait(false); } @@ -121,6 +123,61 @@ namespace MediaBrowser.Providers.TV } } + private void RemoveObsoleteEpisodes(Series series) + { + var episodes = series.GetEpisodes(null, new DtoOptions()).OfType().ToList(); + var numberOfEpisodes = episodes.Count; + // TODO: O(n^2), but can it be done faster without overcomplicating it? + for (var i = 0; i < numberOfEpisodes; i++) + { + var currentEpisode = episodes[i]; + // The outer loop only examines virtual episodes + if (!currentEpisode.IsVirtualItem) + { + continue; + } + + // Virtual episodes without an episode number are practically orphaned and should be deleted + if (!currentEpisode.IndexNumber.HasValue) + { + DeleteEpisode(currentEpisode); + continue; + } + + for (var j = i + 1; j < numberOfEpisodes; j++) + { + var comparisonEpisode = episodes[j]; + // The inner loop is only for "physical" episodes + if (comparisonEpisode.IsVirtualItem + || currentEpisode.ParentIndexNumber != comparisonEpisode.ParentIndexNumber + || !comparisonEpisode.ContainsEpisodeNumber(currentEpisode.IndexNumber.Value)) + { + continue; + } + + DeleteEpisode(currentEpisode); + break; + } + } + } + + private void DeleteEpisode(Episode episode) + { + Logger.LogInformation( + "Removing virtual episode S{SeasonNumber}E{EpisodeNumber} in series {SeriesName}", + episode.ParentIndexNumber, + episode.IndexNumber, + episode.SeriesName); + + LibraryManager.DeleteItem( + episode, + new DeleteOptions + { + DeleteFileLocation = true + }, + false); + } + /// /// Creates seasons for all episodes that aren't in a season folder. /// If no season number can be determined, a dummy season will be created. From a380153f92969b151fc0bc26948b6b8d96475846 Mon Sep 17 00:00:00 2001 From: cvium Date: Sat, 30 Jul 2022 21:54:03 +0200 Subject: [PATCH 12/28] remove redundant null check --- MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index ef88882714..87863549c0 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -94,11 +94,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles { var subtitleFormats = new List(); var assembly = typeof(SubtitleFormat).Assembly; - if (assembly == null) - { - _logger.LogError("Missing assembly containing {SubtitleFormatName}", nameof(SubtitleFormat)); - return GetFallbackSubtitleFormats(); - } foreach (var type in assembly.GetTypes()) { From d258a87fdaf58c3a16602301f7e9079ac42200ab Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 1 Aug 2022 18:30:32 +0200 Subject: [PATCH 13/28] Update MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs Co-authored-by: Bond-009 --- .../Subtitles/SubtitleEditParser.cs | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 87863549c0..eb8ff96246 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -116,58 +116,5 @@ namespace MediaBrowser.MediaEncoding.Subtitles return subtitleFormats; } - - private static IEnumerable GetFallbackSubtitleFormats() - => new SubtitleFormat[] - { - // Preferred and likely more common formats - new SubRip(), - new WebVTT(), - new WebVTTFileWithLineNumber(), - new AdvancedSubStationAlpha(), - new SubStationAlpha(), - new Sami(), - new SamiAvDicPlayer(), - new SamiModern(), - new SamiYouTube(), - new DvdSubtitle(), - new DvdSubtitleSystem(), - new JsonAeneas(), - new JsonTed(), - new Json(), - new JsonType2(), - new JsonType3(), - new JsonType4(), - new JsonType5(), - new JsonType6(), - new JsonType7(), - new JsonType8(), - new JsonType8b(), - new JsonType9(), - new JsonType10(), - new JsonType11(), - new JsonType12(), - new JsonType13(), - new JsonType14(), - new JsonType15(), - new JsonType16(), - new JsonType17(), - new JsonType18(), - new JsonType19(), - new JsonType20(), - new ItunesTimedText(), - new FLVCoreCuePoints(), - new Csv(), - new Csv2(), - new Csv3(), - new Csv4(), - new Csv5(), - new Ebu(), - new NetflixImsc11Japanese(), - new NetflixTimedText(), - new QuickTimeText(), - new RealTime(), - new SmpteTt2052() - }; } } From dd97e6bc45f6461a6d3ffafa69dbb6ceee5350b2 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 1 Aug 2022 14:27:30 -0400 Subject: [PATCH 14/28] Bump version to 10.8.2 --- 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 090f0a1f5f..a18659ce61 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -36,7 +36,7 @@ Jellyfin Contributors Jellyfin.Naming - 10.8.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index fb2438939e..c740dfa3e3 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -18,7 +18,7 @@ Jellyfin Contributors Jellyfin.Data - 10.8.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 1770ab59a9..1cefb2ee27 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Common - 10.8.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 32ead061b6..162067ab74 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Controller - 10.8.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index caef198e49..3770f670b4 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Model - 10.8.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/SharedVersion.cs b/SharedVersion.cs index d296c8a7c2..d8c63ccd19 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.8.1")] -[assembly: AssemblyFileVersion("10.8.1")] +[assembly: AssemblyVersion("10.8.2")] +[assembly: AssemblyFileVersion("10.8.2")] diff --git a/build.yaml b/build.yaml index d2662f136e..d55ad55cb6 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.1" +version: "10.8.2" packages: - debian.amd64 - debian.arm64 diff --git a/debian/changelog b/debian/changelog index e4028294f4..bb6aa7e3f7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.8.2-1) unstable; urgency=medium + + * New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.2 + + -- Jellyfin Packaging Team Mon, 01 Aug 2022 14:27:27 -0400 + 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 diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin index 6bd2d637ed..ef1d8c04cf 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.1 +Version: 10.8.2 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 26b768bbf6..b5dde4faf9 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.1 +Version: 10.8.2 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv2 @@ -176,6 +176,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Mon Aug 01 2022 Jellyfin Packaging Team +- New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.2 * 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 diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 447b4d501d..53c6454441 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.1 + 10.8.2 https://github.com/jellyfin/jellyfin GPL-3.0-only From 494ed7e4d2af34db10db3af90ec5d045017066c4 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Mon, 1 Aug 2022 20:19:16 -0400 Subject: [PATCH 15/28] Revert "Merge pull request #8087 from cvium/generic_subtitleparser" This PR was causing breakage in installs - ref #8198 This reverts commit 7323ccfc232d31797af3ceb8bad93cae1ea0898d, reversing changes made to 77a007a24d5eef1209766d31e2f5038b11d1a8d4. --- .../ApplicationHost.cs | 4 +- .../Subtitles/AssParser.cs | 19 +++++ .../Subtitles/ISubtitleParser.cs | 12 +-- .../Subtitles/SrtParser.cs | 19 +++++ .../Subtitles/SsaParser.cs | 19 +++++ .../Subtitles/SubtitleEditParser.cs | 85 +++---------------- .../Subtitles/SubtitleEncoder.cs | 47 ++++++++-- .../Subtitles/AssParserTests.cs | 2 +- .../Subtitles/SrtParserTests.cs | 4 +- .../Subtitles/SsaParserTests.cs | 6 +- 10 files changed, 119 insertions(+), 98 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssParser.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199d..bc55dc6b48 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -83,7 +83,6 @@ using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; -using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; @@ -635,8 +634,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs new file mode 100644 index 0000000000..08ee5c72e5 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// Advanced SubStation Alpha subtitle parser. + /// + public class AssParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public AssParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index bd13437fb6..c0023ebf24 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System.IO; +using System.Threading; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles @@ -11,15 +12,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Parses the specified stream. /// /// The stream. - /// The file extension. + /// The cancellation token. /// SubtitleTrackInfo. - SubtitleTrackInfo Parse(Stream stream, string fileExtension); - - /// - /// Determines whether the file extension is supported by the parser. - /// - /// The file extension. - /// A value indicating whether the file extension is supported. - bool SupportsFileExtension(string fileExtension); + SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs new file mode 100644 index 0000000000..78d54ca51f --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// SubRip subtitle parser. + /// + public class SrtParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public SrtParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs new file mode 100644 index 0000000000..17c2ae40e0 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// SubStation Alpha subtitle parser. + /// + public class SsaParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public SsaParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index eb8ff96246..52c1b64677 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,14 +1,12 @@ -using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; +using System.Threading; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; -using Nikse.SubtitleEdit.Core.SubtitleFormats; +using ILogger = Microsoft.Extensions.Logging.ILogger; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles @@ -16,57 +14,31 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// SubStation Alpha subtitle parser. /// - public class SubtitleEditParser : ISubtitleParser + /// The . + public abstract class SubtitleEditParser : ISubtitleParser + where T : SubtitleFormat, new() { - private readonly ILogger _logger; - private readonly Dictionary _subtitleFormats; + private readonly ILogger _logger; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The logger. - public SubtitleEditParser(ILogger logger) + protected SubtitleEditParser(ILogger logger) { _logger = logger; - _subtitleFormats = GetSubtitleFormats() - .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension)) - .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase) - .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); } /// - public SubtitleTrackInfo Parse(Stream stream, string fileExtension) + public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) { var subtitle = new Subtitle(); + var subRip = new T(); var lines = stream.ReadAllLines().ToList(); - - if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats)) + subRip.LoadSubtitle(subtitle, lines, "untitled"); + if (subRip.ErrorCount > 0) { - throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); - } - - foreach (var subtitleFormat in subtitleFormats) - { - _logger.LogDebug( - "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", - fileExtension, - subtitleFormat.Name); - subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); - if (subtitleFormat.ErrorCount == 0) - { - break; - } - - _logger.LogError( - "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", - subtitleFormat.ErrorCount, - fileExtension, - subtitleFormat.Name); - } - - if (subtitle.Paragraphs.Count == 0) - { - throw new ArgumentException("Unsupported format: " + fileExtension); + _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount); } var trackInfo = new SubtitleTrackInfo(); @@ -85,36 +57,5 @@ namespace MediaBrowser.MediaEncoding.Subtitles trackInfo.TrackEvents = trackEvents; return trackInfo; } - - /// - public bool SupportsFileExtension(string fileExtension) - => _subtitleFormats.ContainsKey(fileExtension); - - private IEnumerable GetSubtitleFormats() - { - var subtitleFormats = new List(); - var assembly = typeof(SubtitleFormat).Assembly; - - foreach (var type in assembly.GetTypes()) - { - if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract) - { - continue; - } - - try - { - // It shouldn't be null, but the exception is caught if it is - var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!; - subtitleFormats.Add(subtitleFormat); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name); - } - } - - return subtitleFormats; - } } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 50c4d92103..7091af734a 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -35,7 +35,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IMediaEncoder _mediaEncoder; private readonly IHttpClientFactory _httpClientFactory; private readonly IMediaSourceManager _mediaSourceManager; - private readonly ISubtitleParser _subtitleParser; /// /// The _semaphoreLocks. @@ -49,8 +48,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles IFileSystem fileSystem, IMediaEncoder mediaEncoder, IHttpClientFactory httpClientFactory, - IMediaSourceManager mediaSourceManager, - ISubtitleParser subtitleParser) + IMediaSourceManager mediaSourceManager) { _logger = logger; _appPaths = appPaths; @@ -58,7 +56,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles _mediaEncoder = mediaEncoder; _httpClientFactory = httpClientFactory; _mediaSourceManager = mediaSourceManager; - _subtitleParser = subtitleParser; } private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles"); @@ -76,7 +73,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var trackInfo = _subtitleParser.Parse(stream, inputFormat); + var reader = GetReader(inputFormat); + var trackInfo = reader.Parse(stream, cancellationToken); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -235,8 +233,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); - // Fallback to ffmpeg conversion - if (!_subtitleParser.SupportsFileExtension(currentFormat)) + if (!TryGetReader(currentFormat, out _)) { // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); @@ -246,10 +243,44 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); } - // It's possible that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) + // It's possbile that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) return new SubtitleInfo(subtitleStream.Path, _mediaSourceManager.GetPathProtocol(subtitleStream.Path), currentFormat, true); } + private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) + { + if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) + { + value = new SrtParser(_logger); + return true; + } + + if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) + { + value = new SsaParser(_logger); + return true; + } + + if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) + { + value = new AssParser(_logger); + return true; + } + + value = null; + return false; + } + + private ISubtitleParser GetReader(string format) + { + if (TryGetReader(format, out var reader)) + { + return reader; + } + + throw new ArgumentException("Unsupported format: " + format); + } + private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index e14850eed7..3775555dec 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ass")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "ass"); + var parsed = new AssParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index 0038b18736..c07c9ea7db 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); + var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; @@ -37,7 +37,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example2.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); + var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 3b9a71690c..56649db8f8 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { public class SsaParserTests { - private readonly SubtitleEditParser _parser = new SubtitleEditParser(new NullLogger()); + private readonly SsaParser _parser = new SsaParser(new NullLogger()); [Theory] [MemberData(nameof(Parse_MultipleDialogues_TestData))] @@ -21,7 +21,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, "ssa"); + SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None); Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count); @@ -76,7 +76,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ssa")) { - var parsed = _parser.Parse(stream, "ssa"); + var parsed = _parser.Parse(stream, CancellationToken.None); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; From c5a2ff8ac44549fe8b69217365720d9c065cb048 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Mon, 1 Aug 2022 20:20:00 -0400 Subject: [PATCH 16/28] Bump version to 10.8.3 --- 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 a18659ce61..9baf6a2be2 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -36,7 +36,7 @@ Jellyfin Contributors Jellyfin.Naming - 10.8.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index c740dfa3e3..381b1b7ce0 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -18,7 +18,7 @@ Jellyfin Contributors Jellyfin.Data - 10.8.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 1cefb2ee27..8d16c8357c 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Common - 10.8.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 162067ab74..abffa0ef60 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Controller - 10.8.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 3770f670b4..9f36fbb5e3 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Model - 10.8.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/SharedVersion.cs b/SharedVersion.cs index d8c63ccd19..6b4b8fea64 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.8.2")] -[assembly: AssemblyFileVersion("10.8.2")] +[assembly: AssemblyVersion("10.8.3")] +[assembly: AssemblyFileVersion("10.8.3")] diff --git a/build.yaml b/build.yaml index d55ad55cb6..189a52a4bf 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.2" +version: "10.8.3" packages: - debian.amd64 - debian.arm64 diff --git a/debian/changelog b/debian/changelog index bb6aa7e3f7..a6313a288d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.8.3-1) unstable; urgency=medium + + * New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.3 + + -- Jellyfin Packaging Team Mon, 01 Aug 2022 20:19:48 -0400 + jellyfin-server (10.8.2-1) unstable; urgency=medium * New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.2 diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin index ef1d8c04cf..7279fe1d47 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.2 +Version: 10.8.3 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 b5dde4faf9..04259b4948 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.2 +Version: 10.8.3 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv2 @@ -177,6 +177,8 @@ fi %changelog * Mon Aug 01 2022 Jellyfin Packaging Team +- New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.3 +* Mon Aug 01 2022 Jellyfin Packaging Team - New upstream version 10.8.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.2 * 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 diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 53c6454441..12ce28ef28 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.2 + 10.8.3 https://github.com/jellyfin/jellyfin GPL-3.0-only From 5bcab0f0f804ab29ba36f37c29f8d458b2571a34 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Mon, 1 Aug 2022 20:40:54 -0400 Subject: [PATCH 17/28] Restore "Merge pull request #8087 from cvium/generic_subtitleparser" After tagging v10.8.3, this can be restored to how it was and corrected as required in a separate PR. This reverts commit 494ed7e4d2af34db10db3af90ec5d045017066c4. --- .../ApplicationHost.cs | 4 +- .../Subtitles/AssParser.cs | 19 ----- .../Subtitles/ISubtitleParser.cs | 12 ++- .../Subtitles/SrtParser.cs | 19 ----- .../Subtitles/SsaParser.cs | 19 ----- .../Subtitles/SubtitleEditParser.cs | 85 ++++++++++++++++--- .../Subtitles/SubtitleEncoder.cs | 47 ++-------- .../Subtitles/AssParserTests.cs | 2 +- .../Subtitles/SrtParserTests.cs | 4 +- .../Subtitles/SsaParserTests.cs | 6 +- 10 files changed, 98 insertions(+), 119 deletions(-) delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssParser.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index bc55dc6b48..91a16c199d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -83,6 +83,7 @@ using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; +using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; @@ -634,7 +635,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs deleted file mode 100644 index 08ee5c72e5..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// Advanced SubStation Alpha subtitle parser. - /// - public class AssParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public AssParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index c0023ebf24..bd13437fb6 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System.IO; -using System.Threading; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles @@ -12,8 +11,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Parses the specified stream. /// /// The stream. - /// The cancellation token. + /// The file extension. /// SubtitleTrackInfo. - SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken); + SubtitleTrackInfo Parse(Stream stream, string fileExtension); + + /// + /// Determines whether the file extension is supported by the parser. + /// + /// The file extension. + /// A value indicating whether the file extension is supported. + bool SupportsFileExtension(string fileExtension); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs deleted file mode 100644 index 78d54ca51f..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SubRip subtitle parser. - /// - public class SrtParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SrtParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs deleted file mode 100644 index 17c2ae40e0..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.SubtitleFormats; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - /// - /// SubStation Alpha subtitle parser. - /// - public class SsaParser : SubtitleEditParser - { - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SsaParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 52c1b64677..eb8ff96246 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,12 +1,14 @@ +using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; +using System.Reflection; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; -using ILogger = Microsoft.Extensions.Logging.ILogger; +using Nikse.SubtitleEdit.Core.SubtitleFormats; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles @@ -14,31 +16,57 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// SubStation Alpha subtitle parser. /// - /// The . - public abstract class SubtitleEditParser : ISubtitleParser - where T : SubtitleFormat, new() + public class SubtitleEditParser : ISubtitleParser { - private readonly ILogger _logger; + private readonly ILogger _logger; + private readonly Dictionary _subtitleFormats; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The logger. - protected SubtitleEditParser(ILogger logger) + public SubtitleEditParser(ILogger logger) { _logger = logger; + _subtitleFormats = GetSubtitleFormats() + .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension)) + .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase) + .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); } /// - public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) + public SubtitleTrackInfo Parse(Stream stream, string fileExtension) { var subtitle = new Subtitle(); - var subRip = new T(); var lines = stream.ReadAllLines().ToList(); - subRip.LoadSubtitle(subtitle, lines, "untitled"); - if (subRip.ErrorCount > 0) + + if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats)) { - _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount); + throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); + } + + foreach (var subtitleFormat in subtitleFormats) + { + _logger.LogDebug( + "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", + fileExtension, + subtitleFormat.Name); + subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); + if (subtitleFormat.ErrorCount == 0) + { + break; + } + + _logger.LogError( + "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", + subtitleFormat.ErrorCount, + fileExtension, + subtitleFormat.Name); + } + + if (subtitle.Paragraphs.Count == 0) + { + throw new ArgumentException("Unsupported format: " + fileExtension); } var trackInfo = new SubtitleTrackInfo(); @@ -57,5 +85,36 @@ namespace MediaBrowser.MediaEncoding.Subtitles trackInfo.TrackEvents = trackEvents; return trackInfo; } + + /// + public bool SupportsFileExtension(string fileExtension) + => _subtitleFormats.ContainsKey(fileExtension); + + private IEnumerable GetSubtitleFormats() + { + var subtitleFormats = new List(); + var assembly = typeof(SubtitleFormat).Assembly; + + foreach (var type in assembly.GetTypes()) + { + if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract) + { + continue; + } + + try + { + // It shouldn't be null, but the exception is caught if it is + var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!; + subtitleFormats.Add(subtitleFormat); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name); + } + } + + return subtitleFormats; + } } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 7091af734a..50c4d92103 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IMediaEncoder _mediaEncoder; private readonly IHttpClientFactory _httpClientFactory; private readonly IMediaSourceManager _mediaSourceManager; + private readonly ISubtitleParser _subtitleParser; /// /// The _semaphoreLocks. @@ -48,7 +49,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles IFileSystem fileSystem, IMediaEncoder mediaEncoder, IHttpClientFactory httpClientFactory, - IMediaSourceManager mediaSourceManager) + IMediaSourceManager mediaSourceManager, + ISubtitleParser subtitleParser) { _logger = logger; _appPaths = appPaths; @@ -56,6 +58,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _mediaEncoder = mediaEncoder; _httpClientFactory = httpClientFactory; _mediaSourceManager = mediaSourceManager; + _subtitleParser = subtitleParser; } private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles"); @@ -73,8 +76,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var reader = GetReader(inputFormat); - var trackInfo = reader.Parse(stream, cancellationToken); + var trackInfo = _subtitleParser.Parse(stream, inputFormat); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -233,7 +235,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); - if (!TryGetReader(currentFormat, out _)) + // Fallback to ffmpeg conversion + if (!_subtitleParser.SupportsFileExtension(currentFormat)) { // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); @@ -243,44 +246,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); } - // It's possbile that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) + // It's possible that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) return new SubtitleInfo(subtitleStream.Path, _mediaSourceManager.GetPathProtocol(subtitleStream.Path), currentFormat, true); } - private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) - { - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) - { - value = new SrtParser(_logger); - return true; - } - - if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) - { - value = new SsaParser(_logger); - return true; - } - - if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) - { - value = new AssParser(_logger); - return true; - } - - value = null; - return false; - } - - private ISubtitleParser GetReader(string format) - { - if (TryGetReader(format, out var reader)) - { - return reader; - } - - throw new ArgumentException("Unsupported format: " + format); - } - private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index 3775555dec..e14850eed7 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ass")) { - var parsed = new AssParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "ass"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index c07c9ea7db..0038b18736 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.srt")) { - var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; @@ -37,7 +37,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example2.srt")) { - var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); + var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 56649db8f8..3b9a71690c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { public class SsaParserTests { - private readonly SsaParser _parser = new SsaParser(new NullLogger()); + private readonly SubtitleEditParser _parser = new SubtitleEditParser(new NullLogger()); [Theory] [MemberData(nameof(Parse_MultipleDialogues_TestData))] @@ -21,7 +21,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None); + SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, "ssa"); Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count); @@ -76,7 +76,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ssa")) { - var parsed = _parser.Parse(stream, CancellationToken.None); + var parsed = _parser.Parse(stream, "ssa"); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; From f49a051a5fa1073ae70f9be1cd3414836647d127 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Tue, 2 Aug 2022 13:21:10 +0200 Subject: [PATCH 18/28] Respect timestamps when extracting subtitles --- 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 7e42626980..1148b4042c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -570,7 +570,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var processArgs = string.Format( CultureInfo.InvariantCulture, - "-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", + "-i {0} -copyts -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath, subtitleStreamIndex, outputCodec, From a9249393e1a9054157bbf1d7562e3a34f6b584c6 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 5 Aug 2022 19:38:33 +0200 Subject: [PATCH 19/28] Fix series query including missing episodes when it should not --- MediaBrowser.Controller/Entities/TV/Series.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index d9efd6b736..9d5c9180de 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -258,10 +258,14 @@ namespace MediaBrowser.Controller.Entities.TV SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season }, OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, - DtoOptions = options, - IsMissing = user?.DisplayMissingEpisodes + DtoOptions = options }; + if (user == null || !user.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + var allItems = LibraryManager.GetItemList(query); var allSeriesEpisodes = allItems.OfType().ToList(); From 30f62638060545b775ba2fab66d821559f8fdada Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Fri, 5 Aug 2022 23:32:22 -0500 Subject: [PATCH 20/28] Add resolution text for 384 sized video --- MediaBrowser.Model/Entities/MediaStream.cs | 2 ++ tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index ce21707938..c05f836325 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -594,6 +594,8 @@ namespace MediaBrowser.Model.Entities <= 426 when Height <= 240 => IsInterlaced ? "240i" : "240p", // 640x360 (16:9 square pixel format) <= 640 when Height <= 360 => IsInterlaced ? "360i" : "360p", + // 682x384 (16:9 square pixel format) + <= 682 when Height <= 384 => IsInterlaced ? "384i" : "384p", // 854x480 (16:9 square pixel format) <= 854 when Height <= 480 => IsInterlaced ? "480i" : "480p", // 960x544 (16:9 square pixel format) diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index 7c8a90605b..8f033973ec 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -127,7 +127,7 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(2560, 1080, true, "1080i")] [InlineData(4096, 3072, false, "4K")] [InlineData(8192, 6144, false, "8K")] - [InlineData(512, 384, false, "480p")] + [InlineData(512, 384, false, "384p")] [InlineData(576, 336, false, "360p")] [InlineData(576, 336, true, "360i")] [InlineData(624, 352, false, "360p")] From 2320f0666675f5a7e62cb9ca756cee698ca12e4d Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sun, 7 Aug 2022 18:01:26 +0800 Subject: [PATCH 21/28] Move Fedora service hardening options to override config --- fedora/jellyfin.override.conf | 46 +++++++++++++++++++++++++++++++++++ fedora/jellyfin.service | 34 -------------------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/fedora/jellyfin.override.conf b/fedora/jellyfin.override.conf index 8652450bb4..48b4de1e9a 100644 --- a/fedora/jellyfin.override.conf +++ b/fedora/jellyfin.override.conf @@ -5,3 +5,49 @@ [Service] #User = jellyfin #EnvironmentFile = /etc/sysconfig/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 +#ProtectClock=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/fedora/jellyfin.service b/fedora/jellyfin.service index 1193ddb5be..eb0d640871 100644 --- a/fedora/jellyfin.service +++ b/fedora/jellyfin.service @@ -13,39 +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 -ProtectClock=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 3b6e00302905ccdd7c73e62b2a0cc3d99d0115ce Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Tue, 9 Aug 2022 12:09:29 -0500 Subject: [PATCH 22/28] Add 404p Resolution Text --- MediaBrowser.Model/Entities/MediaStream.cs | 2 ++ tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index c05f836325..ae8f3b0edf 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -596,6 +596,8 @@ namespace MediaBrowser.Model.Entities <= 640 when Height <= 360 => IsInterlaced ? "360i" : "360p", // 682x384 (16:9 square pixel format) <= 682 when Height <= 384 => IsInterlaced ? "384i" : "384p", + // 720x404 (16:9 square pixel format) + <= 720 when Height <= 404 => IsInterlaced ? "404i" : "404p", // 854x480 (16:9 square pixel format) <= 854 when Height <= 480 => IsInterlaced ? "480i" : "480p", // 960x544 (16:9 square pixel format) diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index 8f033973ec..80c38affe1 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -133,8 +133,8 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(624, 352, false, "360p")] [InlineData(640, 352, false, "360p")] [InlineData(640, 480, false, "480p")] - [InlineData(704, 396, false, "480p")] - [InlineData(720, 404, false, "480p")] + [InlineData(704, 396, false, "404p")] + [InlineData(720, 404, false, "404p")] [InlineData(720, 480, false, "480p")] [InlineData(720, 576, false, "576p")] [InlineData(768, 576, false, "576p")] From 71ed7f7676d6c2209a3db88cf6a18153646f14aa Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 9 Aug 2022 17:57:21 -0600 Subject: [PATCH 23/28] update to dotnet 6.0.8 --- .../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 36daf37ff1..7eda208369 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 91bf62f062..25be914a09 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 1d54c42806..dba6b95166 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 60496afb2f..67160f864e 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 89c74aadbe..81d75c1aae 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/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-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 2135d6f01f..4139ed96d3 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/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-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 24330f629e..313a3e5cb4 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/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-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 507f446cc2..693ee7c276 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/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-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 31513541cf..e7765a5b27 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/0e83f50a-0619-45e6-8f16-dc4f41d1bb16/e0de908b2f070ef9e7e3b6ddea9d268c/dotnet-sdk-6.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-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 b44ecd1665..9db2b926fb 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 73a4179158..93bf737c83 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 7405362416..fe56cae8a2 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + From ae9fd4ab3544beced627bbb32307d7be3ca96a71 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Tue, 9 Aug 2022 17:57:51 -0600 Subject: [PATCH 24/28] update remaining dependencies --- Emby.Photos/Emby.Photos.csproj | 2 +- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 549a4e32d7..a2e60f02d8 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -15,7 +15,7 @@ - + diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 7eda208369..6ba914e1ad 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,7 @@ - + From 3bf1a7e4459f464fe90600601540bd3e8cf57edf Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 13 Aug 2022 21:18:28 -0400 Subject: [PATCH 25/28] Use separate args for dotnet publish commands Fixes #8245 --- deployment/Dockerfile.docker.amd64 | 2 +- deployment/Dockerfile.docker.arm64 | 2 +- deployment/Dockerfile.docker.armhf | 2 +- deployment/build.linux.amd64 | 2 +- deployment/build.linux.amd64-musl | 2 +- deployment/build.linux.arm64 | 2 +- deployment/build.linux.armhf | 2 +- deployment/build.macos | 2 +- deployment/build.portable | 2 +- deployment/build.windows.amd64 | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/deployment/Dockerfile.docker.amd64 b/deployment/Dockerfile.docker.amd64 index b2bd40713d..3fd3fa33ce 100644 --- a/deployment/Dockerfile.docker.amd64 +++ b/deployment/Dockerfile.docker.amd64 @@ -10,4 +10,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # because of changes in docker and systemd we need to not build in parallel at the moment # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/Dockerfile.docker.arm64 b/deployment/Dockerfile.docker.arm64 index fc60f16246..e3cc92bcb3 100644 --- a/deployment/Dockerfile.docker.arm64 +++ b/deployment/Dockerfile.docker.arm64 @@ -10,4 +10,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # because of changes in docker and systemd we need to not build in parallel at the moment # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/Dockerfile.docker.armhf b/deployment/Dockerfile.docker.armhf index f5cc47d83e..3a5df2e246 100644 --- a/deployment/Dockerfile.docker.armhf +++ b/deployment/Dockerfile.docker.armhf @@ -10,4 +10,4 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # because of changes in docker and systemd we need to not build in parallel at the moment # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64 index a1e7f661a5..05059e4edf 100755 --- a/deployment/build.linux.amd64 +++ b/deployment/build.linux.amd64 @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.linux.amd64-musl b/deployment/build.linux.amd64-musl index 72444c05e7..0ee4b05fb3 100755 --- a/deployment/build.linux.amd64-musl +++ b/deployment/build.linux.amd64-musl @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true tar -czf jellyfin-server_${version}_linux-amd64-musl.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.linux.arm64 b/deployment/build.linux.arm64 index e362607a76..6e36db0eb8 100755 --- a/deployment/build.linux.arm64 +++ b/deployment/build.linux.arm64 @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true tar -czf jellyfin-server_${version}_linux-arm64.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.linux.armhf b/deployment/build.linux.armhf index c0d0607fc7..f83eeebf1c 100755 --- a/deployment/build.linux.armhf +++ b/deployment/build.linux.armhf @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true tar -czf jellyfin-server_${version}_linux-armhf.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.macos b/deployment/build.macos index 6255c80cb2..01c640c8bc 100755 --- a/deployment/build.macos +++ b/deployment/build.macos @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.portable b/deployment/build.portable index a6c7418810..27e5e987fe 100755 --- a/deployment/build.portable +++ b/deployment/build.portable @@ -16,7 +16,7 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=false" +dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=false tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version} rm -rf dist/jellyfin-server_${version} diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64 index a9daa6a239..30b252beb5 100755 --- a/deployment/build.windows.amd64 +++ b/deployment/build.windows.amd64 @@ -23,7 +23,7 @@ fi output_dir="dist/jellyfin-server_${version}" # Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true # Prepare addins addin_build_dir="$( mktemp -d )" From e14194bfe258c128ff98c9ee58a89b277ee0894d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sat, 13 Aug 2022 21:23:01 -0400 Subject: [PATCH 26/28] Fix remaining instances in root package configs --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- debian/rules | 2 +- fedora/jellyfin.spec | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index c3038b1d26..5ca233b8ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,7 +72,7 @@ COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # because of changes in docker and systemd we need to not build in parallel at the moment # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none FROM app diff --git a/Dockerfile.arm b/Dockerfile.arm index b25e6039f4..8da17ee5fc 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -64,7 +64,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # Discard objs - may cause failures if exists RUN find . -type d -name obj | xargs -r rm -r # Build -RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none FROM app diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index d0be834dd1..790be1c39d 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -55,7 +55,7 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # Discard objs - may cause failures if exists RUN find . -type d -name obj | xargs -r rm -r # Build -RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none" +RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none FROM app diff --git a/debian/rules b/debian/rules index 64e2b48ea8..f55b1807ea 100755 --- a/debian/rules +++ b/debian/rules @@ -40,7 +40,7 @@ override_dh_clistrip: override_dh_auto_build: dotnet publish -maxcpucount:1 --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ - "-p:DebugSymbols=false;DebugType=none" Jellyfin.Server + -p:DebugSymbols=false -p:DebugType=none Jellyfin.Server override_dh_auto_clean: dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index b5dde4faf9..3f2ffe1ca2 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -68,7 +68,7 @@ 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 + -p:DebugSymbols=false -p:DebugType=none Jellyfin.Server %install From 3ff78b687dfa0f598ce70adb8603899f31b8a201 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Sat, 13 Aug 2022 21:51:23 -0400 Subject: [PATCH 27/28] Revert "Restore "Merge pull request #8087 from cvium/generic_subtitleparser"" This reverts commit 5bcab0f0f804ab29ba36f37c29f8d458b2571a34. --- .../ApplicationHost.cs | 4 +- .../Subtitles/AssParser.cs | 19 +++++ .../Subtitles/ISubtitleParser.cs | 12 +-- .../Subtitles/SrtParser.cs | 19 +++++ .../Subtitles/SsaParser.cs | 19 +++++ .../Subtitles/SubtitleEditParser.cs | 85 +++---------------- .../Subtitles/SubtitleEncoder.cs | 47 ++++++++-- .../Subtitles/AssParserTests.cs | 2 +- .../Subtitles/SrtParserTests.cs | 4 +- .../Subtitles/SsaParserTests.cs | 6 +- 10 files changed, 119 insertions(+), 98 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/Subtitles/AssParser.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199d..bc55dc6b48 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -83,7 +83,6 @@ using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; -using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; @@ -635,8 +634,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs new file mode 100644 index 0000000000..08ee5c72e5 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// Advanced SubStation Alpha subtitle parser. + /// + public class AssParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public AssParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index bd13437fb6..c0023ebf24 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System.IO; +using System.Threading; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles @@ -11,15 +12,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// Parses the specified stream. /// /// The stream. - /// The file extension. + /// The cancellation token. /// SubtitleTrackInfo. - SubtitleTrackInfo Parse(Stream stream, string fileExtension); - - /// - /// Determines whether the file extension is supported by the parser. - /// - /// The file extension. - /// A value indicating whether the file extension is supported. - bool SupportsFileExtension(string fileExtension); + SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs new file mode 100644 index 0000000000..78d54ca51f --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// SubRip subtitle parser. + /// + public class SrtParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public SrtParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs new file mode 100644 index 0000000000..17c2ae40e0 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.SubtitleFormats; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + /// + /// SubStation Alpha subtitle parser. + /// + public class SsaParser : SubtitleEditParser + { + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public SsaParser(ILogger logger) : base(logger) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index eb8ff96246..52c1b64677 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,14 +1,12 @@ -using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; +using System.Threading; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; -using Nikse.SubtitleEdit.Core.SubtitleFormats; +using ILogger = Microsoft.Extensions.Logging.ILogger; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles @@ -16,57 +14,31 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// /// SubStation Alpha subtitle parser. /// - public class SubtitleEditParser : ISubtitleParser + /// The . + public abstract class SubtitleEditParser : ISubtitleParser + where T : SubtitleFormat, new() { - private readonly ILogger _logger; - private readonly Dictionary _subtitleFormats; + private readonly ILogger _logger; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The logger. - public SubtitleEditParser(ILogger logger) + protected SubtitleEditParser(ILogger logger) { _logger = logger; - _subtitleFormats = GetSubtitleFormats() - .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension)) - .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase) - .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase); } /// - public SubtitleTrackInfo Parse(Stream stream, string fileExtension) + public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) { var subtitle = new Subtitle(); + var subRip = new T(); var lines = stream.ReadAllLines().ToList(); - - if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats)) + subRip.LoadSubtitle(subtitle, lines, "untitled"); + if (subRip.ErrorCount > 0) { - throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); - } - - foreach (var subtitleFormat in subtitleFormats) - { - _logger.LogDebug( - "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", - fileExtension, - subtitleFormat.Name); - subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension); - if (subtitleFormat.ErrorCount == 0) - { - break; - } - - _logger.LogError( - "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser", - subtitleFormat.ErrorCount, - fileExtension, - subtitleFormat.Name); - } - - if (subtitle.Paragraphs.Count == 0) - { - throw new ArgumentException("Unsupported format: " + fileExtension); + _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount); } var trackInfo = new SubtitleTrackInfo(); @@ -85,36 +57,5 @@ namespace MediaBrowser.MediaEncoding.Subtitles trackInfo.TrackEvents = trackEvents; return trackInfo; } - - /// - public bool SupportsFileExtension(string fileExtension) - => _subtitleFormats.ContainsKey(fileExtension); - - private IEnumerable GetSubtitleFormats() - { - var subtitleFormats = new List(); - var assembly = typeof(SubtitleFormat).Assembly; - - foreach (var type in assembly.GetTypes()) - { - if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract) - { - continue; - } - - try - { - // It shouldn't be null, but the exception is caught if it is - var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!; - subtitleFormats.Add(subtitleFormat); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name); - } - } - - return subtitleFormats; - } } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 6112fda680..1148b4042c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -35,7 +35,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IMediaEncoder _mediaEncoder; private readonly IHttpClientFactory _httpClientFactory; private readonly IMediaSourceManager _mediaSourceManager; - private readonly ISubtitleParser _subtitleParser; /// /// The _semaphoreLocks. @@ -49,8 +48,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles IFileSystem fileSystem, IMediaEncoder mediaEncoder, IHttpClientFactory httpClientFactory, - IMediaSourceManager mediaSourceManager, - ISubtitleParser subtitleParser) + IMediaSourceManager mediaSourceManager) { _logger = logger; _appPaths = appPaths; @@ -58,7 +56,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles _mediaEncoder = mediaEncoder; _httpClientFactory = httpClientFactory; _mediaSourceManager = mediaSourceManager; - _subtitleParser = subtitleParser; } private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles"); @@ -76,7 +73,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var trackInfo = _subtitleParser.Parse(stream, inputFormat); + var reader = GetReader(inputFormat); + var trackInfo = reader.Parse(stream, cancellationToken); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -235,8 +233,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); - // Fallback to ffmpeg conversion - if (!_subtitleParser.SupportsFileExtension(currentFormat)) + if (!TryGetReader(currentFormat, out _)) { // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); @@ -246,10 +243,44 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true); } - // It's possible that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) + // It's possbile that the subtitleStream and mediaSource don't share the same protocol (e.g. .STRM file with local subs) return new SubtitleInfo(subtitleStream.Path, _mediaSourceManager.GetPathProtocol(subtitleStream.Path), currentFormat, true); } + private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) + { + if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) + { + value = new SrtParser(_logger); + return true; + } + + if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) + { + value = new SsaParser(_logger); + return true; + } + + if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) + { + value = new AssParser(_logger); + return true; + } + + value = null; + return false; + } + + private ISubtitleParser GetReader(string format) + { + if (TryGetReader(format, out var reader)) + { + return reader; + } + + throw new ArgumentException("Unsupported format: " + format); + } + private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index e14850eed7..3775555dec 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ass")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "ass"); + var parsed = new AssParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index 0038b18736..c07c9ea7db 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -15,7 +15,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); + var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; @@ -37,7 +37,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example2.srt")) { - var parsed = new SubtitleEditParser(new NullLogger()).Parse(stream, "srt"); + var parsed = new SrtParser(new NullLogger()).Parse(stream, CancellationToken.None); Assert.Equal(2, parsed.TrackEvents.Count); var trackEvent1 = parsed.TrackEvents[0]; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 3b9a71690c..56649db8f8 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -13,7 +13,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { public class SsaParserTests { - private readonly SubtitleEditParser _parser = new SubtitleEditParser(new NullLogger()); + private readonly SsaParser _parser = new SsaParser(new NullLogger()); [Theory] [MemberData(nameof(Parse_MultipleDialogues_TestData))] @@ -21,7 +21,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(ssa))) { - SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, "ssa"); + SubtitleTrackInfo subtitleTrackInfo = _parser.Parse(stream, CancellationToken.None); Assert.Equal(expectedSubtitleTrackEvents.Count, subtitleTrackInfo.TrackEvents.Count); @@ -76,7 +76,7 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests { using (var stream = File.OpenRead("Test Data/example.ssa")) { - var parsed = _parser.Parse(stream, "ssa"); + var parsed = _parser.Parse(stream, CancellationToken.None); Assert.Single(parsed.TrackEvents); var trackEvent = parsed.TrackEvents[0]; From b344771f8ad3a34f7d54962a1efef559f4c1f642 Mon Sep 17 00:00:00 2001 From: Joshua Boniface Date: Sat, 13 Aug 2022 21:51:50 -0400 Subject: [PATCH 28/28] Bump version to 10.8.4 --- 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 9baf6a2be2..8a71435b99 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -36,7 +36,7 @@ Jellyfin Contributors Jellyfin.Naming - 10.8.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 381b1b7ce0..531dddf7cc 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -18,7 +18,7 @@ Jellyfin Contributors Jellyfin.Data - 10.8.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 8d16c8357c..cf080d7eb5 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Common - 10.8.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index abffa0ef60..17863ec01d 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Controller - 10.8.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 9f36fbb5e3..1b78174589 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -8,7 +8,7 @@ Jellyfin Contributors Jellyfin.Model - 10.8.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only diff --git a/SharedVersion.cs b/SharedVersion.cs index 6b4b8fea64..8e52b73786 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.8.3")] -[assembly: AssemblyFileVersion("10.8.3")] +[assembly: AssemblyVersion("10.8.4")] +[assembly: AssemblyFileVersion("10.8.4")] diff --git a/build.yaml b/build.yaml index 189a52a4bf..349e398e82 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.3" +version: "10.8.4" packages: - debian.amd64 - debian.arm64 diff --git a/debian/changelog b/debian/changelog index a6313a288d..0109a35285 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.8.4-1) unstable; urgency=medium + + * New upstream version 10.8.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.4 + + -- Jellyfin Packaging Team Sat, 13 Aug 2022 21:51:45 -0400 + jellyfin-server (10.8.3-1) unstable; urgency=medium * New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.3 diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin index 7279fe1d47..5b80ade893 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.3 +Version: 10.8.4 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 ab20241b58..ae2cd5b040 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -7,7 +7,7 @@ %endif Name: jellyfin -Version: 10.8.3 +Version: 10.8.4 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv2 @@ -176,6 +176,8 @@ fi %systemd_postun_with_restart jellyfin.service %changelog +* Sat Aug 13 2022 Jellyfin Packaging Team +- New upstream version 10.8.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.4 * Mon Aug 01 2022 Jellyfin Packaging Team - New upstream version 10.8.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.8.3 * Mon Aug 01 2022 Jellyfin Packaging Team diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 12ce28ef28..da3b80d14d 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.3 + 10.8.4 https://github.com/jellyfin/jellyfin GPL-3.0-only