use reflection to get all subtitle formats without causing libse configuration loading

pull/8087/head
cvium 2 years ago
parent dbfa0f3027
commit 126da94020

@ -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<IAuthService, AuthService>();
serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
serviceCollection.AddSingleton<ISubtitleParser, SubtitleEditParser>();
serviceCollection.AddSingleton<ISubtitleEncoder, SubtitleEncoder>();
serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();

@ -14,5 +14,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <param name="fileExtension">The file extension.</param>
/// <returns>SubtitleTrackInfo.</returns>
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);
}
}

@ -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
/// </summary>
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"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public 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 />
@ -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;
}
/// <inheritdoc />
public bool SupportsFileExtension(string fileExtension)
=> _subtitleFormats.ContainsKey(fileExtension);
private IEnumerable<SubtitleFormat> GetSubtitleFormats()
{
var subtitleFormats = new List<SubtitleFormat>();
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<SubtitleFormat> 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()
};
}
}

@ -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, $".{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))

Loading…
Cancel
Save