diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2022-01-27 14:21:53 +0100 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2022-02-18 22:19:24 +0100 |
| commit | ca5112f45a724fa24a420fa2b62c0849f99ff756 (patch) | |
| tree | f071bacdbe36bf55cc41de0cf0ae999e58d5967d /MediaBrowser.Providers | |
| parent | b92e1baa3c28870f49f82bfef9e48cd79b9b24c6 (diff) | |
feat(external-media): refactor external subtitle and audio provider
Diffstat (limited to 'MediaBrowser.Providers')
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 114 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs | 12 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 23 | ||||
| -rw-r--r-- | MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs | 268 |
4 files changed, 228 insertions, 189 deletions
diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 425913501a..745738f75b 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Emby.Naming.Audio; using Emby.Naming.Common; -using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; @@ -26,6 +27,9 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILocalizationManager _localizationManager; private readonly IMediaEncoder _mediaEncoder; private readonly NamingOptions _namingOptions; + private readonly ExternalAudioFilePathParser _externalAudioFilePathParser; + private readonly CompareInfo _compareInfo = CultureInfo.InvariantCulture.CompareInfo; + private const CompareOptions CompareOptions = System.Globalization.CompareOptions.IgnoreCase | System.Globalization.CompareOptions.IgnoreNonSpace | System.Globalization.CompareOptions.IgnoreSymbols; /// <summary> /// Initializes a new instance of the <see cref="AudioResolver"/> class. @@ -41,6 +45,7 @@ namespace MediaBrowser.Providers.MediaInfo _localizationManager = localizationManager; _mediaEncoder = mediaEncoder; _namingOptions = namingOptions; + _externalAudioFilePathParser = new ExternalAudioFilePathParser(_namingOptions); } /// <summary> @@ -66,37 +71,38 @@ namespace MediaBrowser.Providers.MediaInfo yield break; } - IEnumerable<string> paths = GetExternalAudioFiles(video, directoryService, clearCache); - foreach (string path in paths) + string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + + var externalAudioFileInfos = GetExternalAudioFiles(video, directoryService, clearCache); + foreach (var externalAudioFileInfo in externalAudioFileInfos) { - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); - Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken).ConfigureAwait(false); + string fileName = Path.GetFileName(externalAudioFileInfo.Path); + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(externalAudioFileInfo.Path); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(externalAudioFileInfo.Path, cancellationToken).ConfigureAwait(false); - foreach (MediaStream mediaStream in mediaInfo.MediaStreams) + if (mediaInfo.MediaStreams.Count == 1) { + MediaStream mediaStream = mediaInfo.MediaStreams.First(); mediaStream.Index = startIndex++; mediaStream.Type = MediaStreamType.Audio; mediaStream.IsExternal = true; - mediaStream.Path = path; - mediaStream.IsDefault = false; - mediaStream.Title = null; + mediaStream.Path = externalAudioFileInfo.Path; + mediaStream.IsDefault = externalAudioFileInfo.IsDefault || mediaStream.IsDefault; + mediaStream.IsForced = externalAudioFileInfo.IsForced || mediaStream.IsForced; - if (string.IsNullOrEmpty(mediaStream.Language)) + yield return DetectLanguage(mediaStream, fileNameWithoutExtension, videoFileNameWithoutExtension); + } + else + { + foreach (MediaStream mediaStream in mediaInfo.MediaStreams) { - // Try to translate to three character code - // Be flexible and check against both the full and three character versions - var language = StringExtensions.RightPart(fileNameWithoutExtension, '.').ToString(); + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Audio; + mediaStream.IsExternal = true; + mediaStream.Path = externalAudioFileInfo.Path; - if (language != fileNameWithoutExtension) - { - var culture = _localizationManager.FindLanguageInfo(language); - - language = culture == null ? language : culture.ThreeLetterISOLanguageName; - mediaStream.Language = language; - } + yield return DetectLanguage(mediaStream, fileNameWithoutExtension, videoFileNameWithoutExtension); } - - yield return mediaStream; } } } @@ -108,7 +114,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="directoryService">The directory service to search for files.</param> /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param> /// <returns>A list of external audio file paths.</returns> - public IEnumerable<string> GetExternalAudioFiles( + public IEnumerable<ExternalAudioFileInfo> GetExternalAudioFiles( Video video, IDirectoryService directoryService, bool clearCache) @@ -125,28 +131,19 @@ namespace MediaBrowser.Providers.MediaInfo yield break; } - string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); var files = directoryService.GetFilePaths(folder, clearCache, true); for (int i = 0; i < files.Count; i++) { - string file = files[i]; - if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase) - || !AudioFileParser.IsAudioFile(file, _namingOptions) - || Path.GetExtension(file.AsSpan()).Equals(".strm", StringComparison.OrdinalIgnoreCase)) + var subtitleFileInfo = _externalAudioFilePathParser.ParseFile(files[i]); + + if (subtitleFileInfo == null) { continue; } - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - // The audio filename must either be equal to the video filename or start with the video filename followed by a dot - if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) - || (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length - && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.' - && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))) - { - yield return file; - } + yield return subtitleFileInfo; } } @@ -172,5 +169,48 @@ namespace MediaBrowser.Providers.MediaInfo }, cancellationToken); } + + private MediaStream DetectLanguage(MediaStream mediaStream, string fileNameWithoutExtension, string videoFileNameWithoutExtension) + { + // Support xbmc naming conventions - 300.spanish.srt + var languageString = fileNameWithoutExtension; + while (languageString.Length > 0) + { + var lastDot = languageString.LastIndexOf('.'); + if (lastDot < videoFileNameWithoutExtension.Length) + { + break; + } + + var currentSlice = languageString[lastDot..]; + languageString = languageString[..lastDot]; + + if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase) + || currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase) + || currentSlice.Equals(".foreign", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var currentSliceString = currentSlice[1..]; + + // Try to translate to three character code + var culture = _localizationManager.FindLanguageInfo(currentSliceString); + + if (culture == null || mediaStream.Language != null) + { + if (mediaStream.Title == null) + { + mediaStream.Title = currentSliceString; + } + } + else + { + mediaStream.Language = culture.ThreeLetterISOLanguageName; + } + } + + return mediaStream; + } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 19a435196a..3a819ff7c2 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -43,7 +43,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly AudioResolver _audioResolver; private readonly FFProbeVideoInfo _videoProber; private readonly FFProbeAudioInfo _audioProber; - private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); public FFProbeProvider( @@ -62,7 +61,7 @@ namespace MediaBrowser.Providers.MediaInfo { _logger = logger; _audioResolver = new AudioResolver(localization, mediaEncoder, namingOptions); - _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); + _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, mediaEncoder, namingOptions); _videoProber = new FFProbeVideoInfo( _logger, mediaSourceManager, @@ -75,6 +74,7 @@ namespace MediaBrowser.Providers.MediaInfo subtitleManager, chapterManager, libraryManager, + _subtitleResolver, _audioResolver); _audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager); } @@ -104,7 +104,9 @@ namespace MediaBrowser.Providers.MediaInfo if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder && !video.SubtitleFiles.SequenceEqual( - _subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal)) + _subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false) + .Select(info => info.Path).ToList(), + StringComparer.Ordinal)) { _logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path); return true; @@ -112,7 +114,9 @@ namespace MediaBrowser.Providers.MediaInfo if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder && !video.AudioFiles.SequenceEqual( - _audioResolver.GetExternalAudioFiles(video, directoryService, false), StringComparer.Ordinal)) + _audioResolver.GetExternalAudioFiles(video, directoryService, false) + .Select(info => info.Path).ToList(), + StringComparer.Ordinal)) { _logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path); return true; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 77a849d00b..fa02874f20 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -45,6 +45,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; private readonly AudioResolver _audioResolver; + private readonly SubtitleResolver _subtitleResolver; private readonly IMediaSourceManager _mediaSourceManager; private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks; @@ -61,6 +62,7 @@ namespace MediaBrowser.Providers.MediaInfo ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager, + SubtitleResolver subtitleResolver, AudioResolver audioResolver) { _logger = logger; @@ -74,6 +76,7 @@ namespace MediaBrowser.Providers.MediaInfo _chapterManager = chapterManager; _libraryManager = libraryManager; _audioResolver = audioResolver; + _subtitleResolver = subtitleResolver; _mediaSourceManager = mediaSourceManager; } @@ -215,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo chapters = Array.Empty<ChapterInfo>(); } - await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + await AddExternalSubtitlesAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); @@ -526,16 +529,21 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="options">The refreshOptions.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task AddExternalSubtitles( + private async Task AddExternalSubtitlesAsync( Video video, List<MediaStream> currentStreams, MetadataRefreshOptions options, CancellationToken cancellationToken) { - var subtitleResolver = new SubtitleResolver(_localization); - var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1); - var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false); + var externalSubtitleStreamsAsync = _subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false, cancellationToken); + + List<MediaStream> externalSubtitleStreams = new List<MediaStream>(); + + await foreach (MediaStream externalSubtitleStream in externalSubtitleStreamsAsync) + { + externalSubtitleStreams.Add(externalSubtitleStream); + } var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; @@ -589,7 +597,10 @@ namespace MediaBrowser.Providers.MediaInfo // Rescan if (downloadedLanguages.Count > 0) { - externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true); + await foreach (MediaStream externalSubtitleStream in _subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true, cancellationToken)) + { + externalSubtitleStreams.Add(externalSubtitleStream); + } } } diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index ba284187ed..15beea39a1 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -1,10 +1,22 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Emby.Naming.Common; +using Emby.Naming.Subtitles; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.MediaInfo; + namespace MediaBrowser.Providers.MediaInfo { @@ -13,15 +25,28 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> public class SubtitleResolver { - private readonly ILocalizationManager _localization; + private readonly ILocalizationManager _localizationManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly NamingOptions _namingOptions; + private readonly SubtitleFilePathParser _subtitleFilePathParser; + private readonly CompareInfo _compareInfo = CultureInfo.InvariantCulture.CompareInfo; + private const CompareOptions CompareOptions = System.Globalization.CompareOptions.IgnoreCase | System.Globalization.CompareOptions.IgnoreNonSpace | System.Globalization.CompareOptions.IgnoreSymbols; /// <summary> /// Initializes a new instance of the <see cref="SubtitleResolver"/> class. /// </summary> /// <param name="localization">The localization manager.</param> - public SubtitleResolver(ILocalizationManager localization) + /// <param name="mediaEncoder">The media encoder.</param> + /// <param name="namingOptions">The naming Options.</param> + public SubtitleResolver( + ILocalizationManager localization, + IMediaEncoder mediaEncoder, + NamingOptions namingOptions) { - _localization = localization; + _localizationManager = localization; + _mediaEncoder = mediaEncoder; + _namingOptions = namingOptions; + _subtitleFilePathParser = new SubtitleFilePathParser(_namingOptions); } /// <summary> @@ -31,40 +56,58 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="startIndex">The stream index to start adding subtitle streams at.</param> /// <param name="directoryService">The directory service to search for files.</param> /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param> + /// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <returns>The external subtitle streams located.</returns> - public List<MediaStream> GetExternalSubtitleStreams( + public async IAsyncEnumerable<MediaStream> GetExternalSubtitleStreams( Video video, int startIndex, IDirectoryService directoryService, - bool clearCache) + bool clearCache, + [EnumeratorCancellation] CancellationToken cancellationToken) { - var streams = new List<MediaStream>(); + + cancellationToken.ThrowIfCancellationRequested(); if (!video.IsFileProtocol) { - return streams; + yield break; } - AddExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache); - - startIndex += streams.Count; + var subtitleFileInfos = GetExternalSubtitleFiles(video, directoryService, clearCache); - string folder = video.GetInternalMetadataPath(); + var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); - if (!Directory.Exists(folder)) + foreach (var subtitleFileInfo in subtitleFileInfos) { - return streams; - } + string fileName = Path.GetFileName(subtitleFileInfo.Path); + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(subtitleFileInfo.Path); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(subtitleFileInfo.Path, cancellationToken).ConfigureAwait(false); - try - { - AddExternalSubtitleStreams(streams, folder, video.Path, startIndex, directoryService, clearCache); - } - catch (IOException) - { - } + if (mediaInfo.MediaStreams.Count == 1) + { + MediaStream mediaStream = mediaInfo.MediaStreams.First(); + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Subtitle; + mediaStream.IsExternal = true; + mediaStream.Path = subtitleFileInfo.Path; + mediaStream.IsDefault = subtitleFileInfo.IsDefault || mediaStream.IsDefault; + mediaStream.IsForced = subtitleFileInfo.IsForced || mediaStream.IsForced; + + yield return DetectLanguage(mediaStream, fileNameWithoutExtension, videoFileNameWithoutExtension); + } + else + { + foreach (MediaStream mediaStream in mediaInfo.MediaStreams) + { + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Subtitle; + mediaStream.IsExternal = true; + mediaStream.Path = subtitleFileInfo.Path; - return streams; + yield return DetectLanguage(mediaStream, fileNameWithoutExtension, videoFileNameWithoutExtension); + } + } + } } /// <summary> @@ -74,7 +117,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="directoryService">The directory service to search for files.</param> /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param> /// <returns>The external subtitle file paths located.</returns> - public IEnumerable<string> GetExternalSubtitleFiles( + public IEnumerable<SubtitleFileInfo> GetExternalSubtitleFiles( Video video, IDirectoryService directoryService, bool clearCache) @@ -84,152 +127,93 @@ namespace MediaBrowser.Providers.MediaInfo yield break; } - var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache); + // Check if video folder exists + string folder = video.ContainingFolderPath; + if (!Directory.Exists(folder)) + { + yield break; + } + + var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); - foreach (var stream in streams) + var files = directoryService.GetFilePaths(folder, clearCache, true); + for (int i = 0; i < files.Count; i++) { - yield return stream.Path; + var subtitleFileInfo = _subtitleFilePathParser.ParseFile(files[i]); + + if (subtitleFileInfo == null) + { + continue; + } + + yield return subtitleFileInfo; } } /// <summary> - /// Extracts the subtitle files from the provided list and adds them to the list of streams. + /// Returns the media info of the given subtitle file. /// </summary> - /// <param name="streams">The list of streams to add external subtitles to.</param> - /// <param name="videoPath">The path to the video file.</param> - /// <param name="startIndex">The stream index to start adding subtitle streams at.</param> - /// <param name="files">The files to add if they are subtitles.</param> - public void AddExternalSubtitleStreams( - List<MediaStream> streams, - string videoPath, - int startIndex, - IReadOnlyList<string> files) + /// <param name="path">The path to the subtitle file.</param> + /// <param name="cancellationToken">The cancellation token to cancel operation.</param> + /// <returns>The media info for the given subtitle file.</returns> + private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(string path, CancellationToken cancellationToken) { - var videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoPath); + cancellationToken.ThrowIfCancellationRequested(); - for (var i = 0; i < files.Count; i++) + return _mediaEncoder.GetMediaInfo( + new MediaInfoRequest + { + MediaType = DlnaProfileType.Subtitle, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = MediaProtocol.File + } + }, + cancellationToken); + } + + private MediaStream DetectLanguage(MediaStream mediaStream, string fileNameWithoutExtension, string videoFileNameWithoutExtension) + { + // Support xbmc naming conventions - 300.spanish.srt + var languageString = fileNameWithoutExtension; + while (languageString.Length > 0) { - var fullName = files[i]; - var extension = Path.GetExtension(fullName.AsSpan()); - if (!IsSubtitleExtension(extension)) + var lastDot = languageString.LastIndexOf('.'); + if (lastDot < videoFileNameWithoutExtension.Length) { - continue; + break; } - var fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fullName); + var currentSlice = languageString[lastDot..]; + languageString = languageString[..lastDot]; - MediaStream mediaStream; - - // The subtitle filename must either be equal to the video filename or start with the video filename followed by a dot - if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) + if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase) + || currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase) + || currentSlice.Equals(".foreign", StringComparison.OrdinalIgnoreCase)) { - mediaStream = new MediaStream - { - Index = startIndex++, - Type = MediaStreamType.Subtitle, - IsExternal = true, - Path = fullName - }; + continue; } - else if (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length - && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.' - && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) - { - var isForced = fullName.Contains(".forced.", StringComparison.OrdinalIgnoreCase) - || fullName.Contains(".foreign.", StringComparison.OrdinalIgnoreCase); - var isDefault = fullName.Contains(".default.", StringComparison.OrdinalIgnoreCase); + var currentSliceString = currentSlice[1..]; - // Support xbmc naming conventions - 300.spanish.srt - var languageSpan = fileNameWithoutExtension; - while (languageSpan.Length > 0) - { - var lastDot = languageSpan.LastIndexOf('.'); - if (lastDot < videoFileNameWithoutExtension.Length) - { - languageSpan = ReadOnlySpan<char>.Empty; - break; - } - - var currentSlice = languageSpan[lastDot..]; - if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase) - || currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase) - || currentSlice.Equals(".foreign", StringComparison.OrdinalIgnoreCase)) - { - languageSpan = languageSpan[..lastDot]; - continue; - } - - languageSpan = languageSpan[(lastDot + 1)..]; - break; - } + // Try to translate to three character code + var culture = _localizationManager.FindLanguageInfo(currentSliceString); - var language = languageSpan.ToString(); - if (string.IsNullOrWhiteSpace(language)) - { - language = null; - } - else + if (culture == null || mediaStream.Language != null) + { + if (mediaStream.Title == null) { - // Try to translate to three character code - // Be flexible and check against both the full and three character versions - var culture = _localization.FindLanguageInfo(language); - - language = culture == null ? language : culture.ThreeLetterISOLanguageName; + mediaStream.Title = currentSliceString; } - - mediaStream = new MediaStream - { - Index = startIndex++, - Type = MediaStreamType.Subtitle, - IsExternal = true, - Path = fullName, - Language = language, - IsForced = isForced, - IsDefault = isDefault - }; } else { - continue; + mediaStream.Language = culture.ThreeLetterISOLanguageName; } - - mediaStream.Codec = extension.TrimStart('.').ToString().ToLowerInvariant(); - - streams.Add(mediaStream); } - } - - private static bool IsSubtitleExtension(ReadOnlySpan<char> extension) - { - return extension.Equals(".srt", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".ssa", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".ass", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".sub", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".vtt", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".smi", StringComparison.OrdinalIgnoreCase) - || extension.Equals(".sami", StringComparison.OrdinalIgnoreCase); - } - - private static ReadOnlySpan<char> NormalizeFilenameForSubtitleComparison(string filename) - { - // Try to account for sloppy file naming - filename = filename.Replace("_", string.Empty, StringComparison.Ordinal); - filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal); - return Path.GetFileNameWithoutExtension(filename.AsSpan()); - } - - private void AddExternalSubtitleStreams( - List<MediaStream> streams, - string folder, - string videoPath, - int startIndex, - IDirectoryService directoryService, - bool clearCache) - { - var files = directoryService.GetFilePaths(folder, clearCache, true); - AddExternalSubtitleStreams(streams, videoPath, startIndex, files); + return mediaStream; } } } |
