SubtitleEditParser.cs 4.9 KB

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