using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles { /// /// SubStation Alpha subtitle parser. /// public class SubtitleEditParser : ISubtitleParser { private readonly ILogger _logger; private readonly Dictionary _subtitleFormats; /// /// Initializes a new instance of the class. /// /// The 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, string fileExtension) { var subtitle = new Subtitle(); var lines = stream.ReadAllLines().ToList(); 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 '{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(); int len = subtitle.Paragraphs.Count; var trackEvents = new SubtitleTrackEvent[len]; for (int i = 0; i < len; i++) { var p = subtitle.Paragraphs[i]; trackEvents[i] = new SubtitleTrackEvent(p.Number.ToString(CultureInfo.InvariantCulture), p.Text) { StartPositionTicks = p.StartTime.TimeSpan.Ticks, EndPositionTicks = p.EndTime.TimeSpan.Ticks }; } trackInfo.TrackEvents = trackEvents; return trackInfo; } /// public bool SupportsFileExtension(string fileExtension) => _subtitleFormats.ContainsKey(fileExtension); private List 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; } } }