SubtitleEditParser.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. _logger.LogError(
  53. "{ErrorCount} errors encountered while parsing '{FileExtension}' subtitle using the {SubtitleFormatParser} format parser",
  54. subtitleFormat.ErrorCount,
  55. fileExtension,
  56. subtitleFormat.Name);
  57. }
  58. if (subtitle.Paragraphs.Count == 0)
  59. {
  60. throw new ArgumentException("Unsupported format: " + fileExtension);
  61. }
  62. var trackInfo = new SubtitleTrackInfo();
  63. int len = subtitle.Paragraphs.Count;
  64. var trackEvents = new SubtitleTrackEvent[len];
  65. for (int i = 0; i < len; i++)
  66. {
  67. var p = subtitle.Paragraphs[i];
  68. trackEvents[i] = new SubtitleTrackEvent(p.Number.ToString(CultureInfo.InvariantCulture), p.Text)
  69. {
  70. StartPositionTicks = p.StartTime.TimeSpan.Ticks,
  71. EndPositionTicks = p.EndTime.TimeSpan.Ticks
  72. };
  73. }
  74. trackInfo.TrackEvents = trackEvents;
  75. return trackInfo;
  76. }
  77. /// <inheritdoc />
  78. public bool SupportsFileExtension(string fileExtension)
  79. => _subtitleFormats.ContainsKey(fileExtension);
  80. private List<SubtitleFormat> GetSubtitleFormats()
  81. {
  82. var subtitleFormats = new List<SubtitleFormat>();
  83. var assembly = typeof(SubtitleFormat).Assembly;
  84. foreach (var type in assembly.GetTypes())
  85. {
  86. if (!type.IsSubclassOf(typeof(SubtitleFormat)) || type.IsAbstract)
  87. {
  88. continue;
  89. }
  90. try
  91. {
  92. // It shouldn't be null, but the exception is caught if it is
  93. var subtitleFormat = (SubtitleFormat)Activator.CreateInstance(type, true)!;
  94. subtitleFormats.Add(subtitleFormat);
  95. }
  96. catch (Exception ex)
  97. {
  98. _logger.LogWarning(ex, "Failed to create instance of the subtitle format {SubtitleFormatType}", type.Name);
  99. }
  100. }
  101. return subtitleFormats;
  102. }
  103. }
  104. }