SubtitleEditParser.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using Jellyfin.Extensions;
  8. using MediaBrowser.Model.MediaInfo;
  9. using Microsoft.Extensions.Logging;
  10. using Nikse.SubtitleEdit.Core.Common;
  11. using Nikse.SubtitleEdit.Core.SubtitleFormats;
  12. using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat;
  13. namespace MediaBrowser.MediaEncoding.Subtitles
  14. {
  15. /// <summary>
  16. /// SubStation Alpha subtitle parser.
  17. /// </summary>
  18. public class SubtitleEditParser : ISubtitleParser
  19. {
  20. private readonly ILogger<SubtitleEditParser> _logger;
  21. private readonly Dictionary<string, SubtitleFormat[]> _subtitleFormats;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="SubtitleEditParser"/> class.
  24. /// </summary>
  25. /// <param name="logger">The logger.</param>
  26. public SubtitleEditParser(ILogger<SubtitleEditParser> logger)
  27. {
  28. _logger = logger;
  29. _subtitleFormats = GetSubtitleFormats()
  30. .Where(subtitleFormat => !string.IsNullOrEmpty(subtitleFormat.Extension))
  31. .GroupBy(subtitleFormat => subtitleFormat.Extension.TrimStart('.'), StringComparer.OrdinalIgnoreCase)
  32. .ToDictionary(g => g.Key, g => g.ToArray(), StringComparer.OrdinalIgnoreCase);
  33. }
  34. /// <inheritdoc />
  35. public SubtitleTrackInfo Parse(Stream stream, string fileExtension)
  36. {
  37. var subtitle = new Subtitle();
  38. var lines = stream.ReadAllLines().ToList();
  39. if (!_subtitleFormats.TryGetValue(fileExtension, out var subtitleFormats))
  40. {
  41. throw new ArgumentException($"Unsupported file extension: {fileExtension}", nameof(fileExtension));
  42. }
  43. foreach (var subtitleFormat in subtitleFormats)
  44. {
  45. _logger.LogDebug(
  46. "Trying to parse '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser",
  47. fileExtension,
  48. subtitleFormat.Name);
  49. subtitleFormat.LoadSubtitle(subtitle, lines, fileExtension);
  50. if (subtitleFormat.ErrorCount == 0)
  51. {
  52. break;
  53. }
  54. _logger.LogError(
  55. "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser",
  56. subtitleFormat.ErrorCount,
  57. fileExtension,
  58. subtitleFormat.Name);
  59. }
  60. if (subtitle.Paragraphs.Count == 0)
  61. {
  62. throw new ArgumentException("Unsupported format: " + fileExtension);
  63. }
  64. var trackInfo = new SubtitleTrackInfo();
  65. int len = subtitle.Paragraphs.Count;
  66. var trackEvents = new SubtitleTrackEvent[len];
  67. for (int i = 0; i < len; i++)
  68. {
  69. var p = subtitle.Paragraphs[i];
  70. trackEvents[i] = new SubtitleTrackEvent(p.Number.ToString(CultureInfo.InvariantCulture), p.Text)
  71. {
  72. StartPositionTicks = p.StartTime.TimeSpan.Ticks,
  73. EndPositionTicks = p.EndTime.TimeSpan.Ticks
  74. };
  75. }
  76. trackInfo.TrackEvents = trackEvents;
  77. return trackInfo;
  78. }
  79. /// <inheritdoc />
  80. public bool SupportsFileExtension(string fileExtension)
  81. => _subtitleFormats.ContainsKey(fileExtension);
  82. private IEnumerable<SubtitleFormat> GetSubtitleFormats()
  83. {
  84. var subtitleFormats = new List<SubtitleFormat>();
  85. var assembly = typeof(SubtitleFormat).Assembly;
  86. foreach (var type in assembly.GetTypes())
  87. {
  88. if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract)
  89. {
  90. continue;
  91. }
  92. try
  93. {
  94. // It shouldn't be null, but the exception is caught if it is
  95. var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!;
  96. subtitleFormats.Add(subtitleFormat);
  97. }
  98. catch (Exception ex)
  99. {
  100. _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name);
  101. }
  102. }
  103. return subtitleFormats;
  104. }
  105. }
  106. }