SubtitleResolver.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #pragma warning disable CS1591
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using MediaBrowser.Controller.Entities;
  7. using MediaBrowser.Controller.Providers;
  8. using MediaBrowser.Model.Entities;
  9. using MediaBrowser.Model.Globalization;
  10. namespace MediaBrowser.Providers.MediaInfo
  11. {
  12. public class SubtitleResolver
  13. {
  14. private readonly ILocalizationManager _localization;
  15. public SubtitleResolver(ILocalizationManager localization)
  16. {
  17. _localization = localization;
  18. }
  19. public List<MediaStream> GetExternalSubtitleStreams(
  20. Video video,
  21. int startIndex,
  22. IDirectoryService directoryService,
  23. bool clearCache)
  24. {
  25. var streams = new List<MediaStream>();
  26. if (!video.IsFileProtocol)
  27. {
  28. return streams;
  29. }
  30. AddExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache);
  31. startIndex += streams.Count;
  32. string folder = video.GetInternalMetadataPath();
  33. if (!Directory.Exists(folder))
  34. {
  35. return streams;
  36. }
  37. try
  38. {
  39. AddExternalSubtitleStreams(streams, folder, video.Path, startIndex, directoryService, clearCache);
  40. }
  41. catch (IOException)
  42. {
  43. }
  44. return streams;
  45. }
  46. public List<string> GetExternalSubtitleFiles(
  47. Video video,
  48. IDirectoryService directoryService,
  49. bool clearCache)
  50. {
  51. var list = new List<string>();
  52. if (!video.IsFileProtocol)
  53. {
  54. return list;
  55. }
  56. var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache);
  57. foreach (var stream in streams)
  58. {
  59. list.Add(stream.Path);
  60. }
  61. return list;
  62. }
  63. public void AddExternalSubtitleStreams(
  64. List<MediaStream> streams,
  65. string videoPath,
  66. int startIndex,
  67. string[] files)
  68. {
  69. var videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoPath);
  70. foreach (var fullName in files)
  71. {
  72. var extension = Path.GetExtension(fullName.AsSpan());
  73. if (!IsSubtitleExtension(extension))
  74. {
  75. continue;
  76. }
  77. var fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fullName);
  78. MediaStream mediaStream;
  79. // The subtitle filename must either be equal to the video filename or start with the video filename followed by a dot
  80. if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
  81. {
  82. mediaStream = new MediaStream
  83. {
  84. Index = startIndex++,
  85. Type = MediaStreamType.Subtitle,
  86. IsExternal = true,
  87. Path = fullName
  88. };
  89. }
  90. else if (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length
  91. && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.'
  92. && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
  93. {
  94. var isForced = fullName.Contains(".forced.", StringComparison.OrdinalIgnoreCase)
  95. || fullName.Contains(".foreign.", StringComparison.OrdinalIgnoreCase);
  96. var isDefault = fullName.Contains(".default.", StringComparison.OrdinalIgnoreCase);
  97. // Support xbmc naming conventions - 300.spanish.srt
  98. var languageSpan = fileNameWithoutExtension;
  99. while (languageSpan.Length > 0)
  100. {
  101. var lastDot = languageSpan.LastIndexOf('.');
  102. var currentSlice = languageSpan[lastDot..];
  103. if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase)
  104. || currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase)
  105. || currentSlice.Equals(".foreign", StringComparison.OrdinalIgnoreCase))
  106. {
  107. languageSpan = languageSpan[..lastDot];
  108. continue;
  109. }
  110. languageSpan = languageSpan[(lastDot + 1)..];
  111. break;
  112. }
  113. var language = languageSpan.ToString();
  114. // Try to translate to three character code
  115. // Be flexible and check against both the full and three character versions
  116. var culture = _localization.FindLanguageInfo(language);
  117. if (culture != null)
  118. {
  119. language = culture.ThreeLetterISOLanguageName;
  120. }
  121. mediaStream = new MediaStream
  122. {
  123. Index = startIndex++,
  124. Type = MediaStreamType.Subtitle,
  125. IsExternal = true,
  126. Path = fullName,
  127. Language = language,
  128. IsForced = isForced,
  129. IsDefault = isDefault
  130. };
  131. }
  132. else
  133. {
  134. continue;
  135. }
  136. mediaStream.Codec = extension.TrimStart('.').ToString().ToLowerInvariant();
  137. streams.Add(mediaStream);
  138. }
  139. }
  140. private static bool IsSubtitleExtension(ReadOnlySpan<char> extension)
  141. {
  142. return extension.Equals(".srt", StringComparison.OrdinalIgnoreCase)
  143. || extension.Equals(".ssa", StringComparison.OrdinalIgnoreCase)
  144. || extension.Equals(".ass", StringComparison.OrdinalIgnoreCase)
  145. || extension.Equals(".sub", StringComparison.OrdinalIgnoreCase)
  146. || extension.Equals(".vtt", StringComparison.OrdinalIgnoreCase)
  147. || extension.Equals(".smi", StringComparison.OrdinalIgnoreCase)
  148. || extension.Equals(".sami", StringComparison.OrdinalIgnoreCase);
  149. }
  150. private static ReadOnlySpan<char> NormalizeFilenameForSubtitleComparison(string filename)
  151. {
  152. // Try to account for sloppy file naming
  153. filename = filename.Replace("_", string.Empty, StringComparison.Ordinal);
  154. filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal);
  155. return Path.GetFileNameWithoutExtension(filename.AsSpan());
  156. }
  157. private void AddExternalSubtitleStreams(
  158. List<MediaStream> streams,
  159. string folder,
  160. string videoPath,
  161. int startIndex,
  162. IDirectoryService directoryService,
  163. bool clearCache)
  164. {
  165. var files = directoryService.GetFilePaths(folder, clearCache).OrderBy(i => i).ToArray();
  166. AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
  167. }
  168. }
  169. }