diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding/Subtitles')
6 files changed, 89 insertions, 112 deletions
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs deleted file mode 100644 index 08ee5c72e..000000000 --- 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 -{ - /// <summary> - /// Advanced SubStation Alpha subtitle parser. - /// </summary> - public class AssParser : SubtitleEditParser<AdvancedSubStationAlpha> - { - /// <summary> - /// Initializes a new instance of the <see cref="AssParser"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - public AssParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index c0023ebf2..bd13437fb 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. /// </summary> /// <param name="stream">The stream.</param> - /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="fileExtension">The file extension.</param> /// <returns>SubtitleTrackInfo.</returns> - SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken); + SubtitleTrackInfo Parse(Stream stream, string fileExtension); + + /// <summary> + /// Determines whether the file extension is supported by the parser. + /// </summary> + /// <param name="fileExtension">The file extension.</param> + /// <returns>A value indicating whether the file extension is supported.</returns> + bool SupportsFileExtension(string fileExtension); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs deleted file mode 100644 index 78d54ca51..000000000 --- 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 -{ - /// <summary> - /// SubRip subtitle parser. - /// </summary> - public class SrtParser : SubtitleEditParser<SubRip> - { - /// <summary> - /// Initializes a new instance of the <see cref="SrtParser"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - 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 17c2ae40e..000000000 --- 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 -{ - /// <summary> - /// SubStation Alpha subtitle parser. - /// </summary> - public class SsaParser : SubtitleEditParser<SubStationAlpha> - { - /// <summary> - /// Initializes a new instance of the <see cref="SsaParser"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - public SsaParser(ILogger logger) : base(logger) - { - } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 52c1b6467..eb8ff9624 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 /// <summary> /// SubStation Alpha subtitle parser. /// </summary> - /// <typeparam name="T">The <see cref="SubtitleFormat" />.</typeparam> - public abstract class SubtitleEditParser<T> : ISubtitleParser - where T : SubtitleFormat, new() + public class SubtitleEditParser : ISubtitleParser { - private readonly ILogger _logger; + private readonly ILogger<SubtitleEditParser> _logger; + private readonly Dictionary<string, SubtitleFormat[]> _subtitleFormats; /// <summary> - /// Initializes a new instance of the <see cref="SubtitleEditParser{T}"/> class. + /// Initializes a new instance of the <see cref="SubtitleEditParser"/> class. /// </summary> /// <param name="logger">The logger.</param> - protected SubtitleEditParser(ILogger logger) + public SubtitleEditParser(ILogger<SubtitleEditParser> 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); } /// <inheritdoc /> - 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)) + { + throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension)); + } + + foreach (var subtitleFormat in subtitleFormats) { - _logger.LogError("{ErrorCount} errors encountered while parsing subtitle", subRip.ErrorCount); + _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; } + + /// <inheritdoc /> + public bool SupportsFileExtension(string fileExtension) + => _subtitleFormats.ContainsKey(fileExtension); + + private IEnumerable<SubtitleFormat> GetSubtitleFormats() + { + var subtitleFormats = new List<SubtitleFormat>(); + 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 7091af734..50c4d9210 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; /// <summary> /// 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)) |
