diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5695ee2db..571230312 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -698,8 +698,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream != null && state.AudioStream.IsExternal) { - arg.Append(" -i ") - .Append(string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", state.AudioStream.Path)); + arg.Append(" -i \"").Append(state.AudioStream.Path).Append("\""); } return arg.ToString(); diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index ccac450f6..d23afdc3b 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -21,98 +21,37 @@ namespace MediaBrowser.Providers.MediaInfo { public class AudioResolver { - public async Task> GetExternalAudioStreams( + private readonly ILocalizationManager _localizationManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly NamingOptions _namingOptions; + + public AudioResolver( + ILocalizationManager localizationManager, + IMediaEncoder mediaEncoder, + NamingOptions namingOptions) + { + _localizationManager = localizationManager; + _mediaEncoder = mediaEncoder; + _namingOptions = namingOptions; + } + + public async IAsyncEnumerable GetExternalAudioStreams( Video video, int startIndex, IDirectoryService directoryService, - NamingOptions namingOptions, bool clearCache, - ILocalizationManager localizationManager, - IMediaEncoder mediaEncoder, CancellationToken cancellationToken) { - var streams = new List(); - if (!video.IsFileProtocol) { - return streams; + yield break; } - List paths = GetExternalAudioFiles(video, directoryService, namingOptions, clearCache); - - await AddExternalAudioStreams(streams, paths, startIndex, localizationManager, mediaEncoder, cancellationToken).ConfigureAwait(false); - - return streams; - } - - public IEnumerable GetExternalAudioFiles( - Video video, - IDirectoryService directoryService, - NamingOptions namingOptions, - bool clearCache) - { - List paths = new List(); - - if (!video.IsFileProtocol) - { - return paths; - } - - paths.AddRange(GetAudioFilesFromFolder(video.ContainingFolderPath, video.Path, directoryService, namingOptions, clearCache)); - - return paths; - } - - private List GetAudioFilesFromFolder( - string folder, - string videoFileName, - IDirectoryService directoryService, - NamingOptions namingOptions, - bool clearCache) - { - List paths = new List(); - string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(videoFileName); - - if (!Directory.Exists(folder)) - { - return paths; - } - - var files = directoryService.GetFilePaths(folder, clearCache, true); - for (int i = 0; i < files.Count; i++) - { - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); - - if (!AudioFileParser.IsAudioFile(files[i], namingOptions)) - { - continue; - } - - // 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))) - { - paths.Add(files[i]); - } - } - - return paths; - } - - public async Task AddExternalAudioStreams( - List streams, - List paths, - int startIndex, - ILocalizationManager localizationManager, - IMediaEncoder mediaEncoder, - CancellationToken cancellationToken) - { + IEnumerable paths = GetExternalAudioFiles(video, directoryService, clearCache); foreach (string path in paths) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); - Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, mediaEncoder, cancellationToken); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken); foreach (MediaStream mediaStream in mediaInfo.MediaStreams) { @@ -131,23 +70,64 @@ namespace MediaBrowser.Providers.MediaInfo if (language != fileNameWithoutExtension) { - var culture = localizationManager.FindLanguageInfo(language); + var culture = _localizationManager.FindLanguageInfo(language); language = culture == null ? language : culture.ThreeLetterISOLanguageName; mediaStream.Language = language; } } - streams.Add(mediaStream); + yield return mediaStream; } } } - private Task GetMediaInfo(string path, IMediaEncoder mediaEncoder, CancellationToken cancellationToken) + public IEnumerable GetExternalAudioFiles( + Video video, + IDirectoryService directoryService, + bool clearCache) + { + if (!video.IsFileProtocol) + { + yield break; + } + + // Check if video folder exists + string folder = video.ContainingFolderPath; + if (!Directory.Exists(folder)) + { + yield break; + } + + string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + + var files = directoryService.GetFilePaths(folder, clearCache, true); + for (int i = 0; i < files.Count; i++) + { + string file = files[i]; + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); + + if (!AudioFileParser.IsAudioFile(file, _namingOptions)) + { + continue; + } + + // 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; + } + } + } + + private Task GetMediaInfo(string path, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - return mediaEncoder.GetMediaInfo( + return _mediaEncoder.GetMediaInfo( new MediaInfoRequest { MediaType = DlnaProfileType.Audio, diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 98909c94e..392641468 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -50,10 +50,9 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; private readonly SubtitleResolver _subtitleResolver; - private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); - private readonly NamingOptions _namingOptions; + private readonly AudioResolver _audioResolver; public FFProbeProvider( ILogger logger, @@ -83,6 +82,7 @@ namespace MediaBrowser.Providers.MediaInfo _namingOptions = namingOptions; _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); + _audioResolver = new AudioResolver(_localization, _mediaEncoder, namingOptions); } public string Name => "ffprobe"; @@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.MediaInfo var file = directoryService.GetFile(path); if (file != null && file.LastWriteTimeUtc != item.DateModified) { - _logger.LogDebug("Refreshing {0} due to date modified timestamp change.", path); + _logger.LogDebug("Refreshing {ItemPath} due to date modified timestamp change.", path); return true; } } @@ -112,16 +112,15 @@ namespace MediaBrowser.Providers.MediaInfo && !video.SubtitleFiles.SequenceEqual( _subtitleResolver.GetExternalSubtitleFiles(video, directoryService, false), StringComparer.Ordinal)) { - _logger.LogDebug("Refreshing {0} due to external subtitles change.", item.Path); + _logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path); return true; } - AudioResolver audioResolver = new AudioResolver(); if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder && !video.AudioFiles.SequenceEqual( - audioResolver.GetExternalAudioFiles(video, directoryService, _namingOptions, false), StringComparer.Ordinal)) + _audioResolver.GetExternalAudioFiles(video, directoryService, false), StringComparer.Ordinal)) { - _logger.LogDebug("Refreshing {0} due to external audio change.", item.Path); + _logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path); return true; } @@ -203,7 +202,7 @@ namespace MediaBrowser.Providers.MediaInfo _subtitleManager, _chapterManager, _libraryManager, - _namingOptions); + _audioResolver); return prober.ProbeVideo(item, options, cancellationToken); } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 745020599..b31f0ed23 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using DvdLib.Ifo; -using Emby.Naming.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; @@ -45,7 +44,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; - private readonly NamingOptions _namingOptions; + private readonly AudioResolver _audioResolver; private readonly IMediaSourceManager _mediaSourceManager; private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks; @@ -62,7 +61,7 @@ namespace MediaBrowser.Providers.MediaInfo ISubtitleManager subtitleManager, IChapterManager chapterManager, ILibraryManager libraryManager, - NamingOptions namingOptions) + AudioResolver audioResolver) { _logger = logger; _mediaEncoder = mediaEncoder; @@ -74,7 +73,7 @@ namespace MediaBrowser.Providers.MediaInfo _subtitleManager = subtitleManager; _chapterManager = chapterManager; _libraryManager = libraryManager; - _namingOptions = namingOptions; + _audioResolver = audioResolver; _mediaSourceManager = mediaSourceManager; } @@ -218,7 +217,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - await AddExternalAudio(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + await AddExternalAudio(video, mediaStreams, options, cancellationToken); var libraryOptions = _libraryManager.GetLibraryOptions(video); @@ -593,14 +592,16 @@ namespace MediaBrowser.Providers.MediaInfo MetadataRefreshOptions options, CancellationToken cancellationToken) { - var audioResolver = new AudioResolver(); - var startIndex = currentStreams.Count == 0 ? 0 : currentStreams.Max(i => i.Index) + 1; - var externalAudioStreams = await audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, _namingOptions, false, _localization, _mediaEncoder, cancellationToken); + var externalAudioStreams = _audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, false, cancellationToken); - video.AudioFiles = externalAudioStreams.Select(i => i.Path).Distinct().ToArray(); + await foreach (MediaStream externalAudioStream in externalAudioStreams) + { + currentStreams.Add(externalAudioStream); + } - currentStreams.AddRange(externalAudioStreams); + // Select all external audio file paths + video.AudioFiles = currentStreams.Where(i => i.Type == MediaStreamType.Audio && i.IsExternal).Select(i => i.Path).Distinct().ToArray(); } ///