From 997816443862a8db68f314a6d23ef076c9661c01 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Mon, 22 Nov 2021 21:47:52 +0100 Subject: [PATCH 01/29] Add support for external audio files --- MediaBrowser.Controller/Entities/Video.cs | 6 + .../MediaEncoding/EncodingHelper.cs | 21 +- .../MediaInfo/AudioResolver.cs | 275 ++++++++++++++++++ .../MediaInfo/FFProbeProvider.cs | 11 + .../MediaInfo/FFProbeVideoInfo.cs | 25 ++ 5 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 MediaBrowser.Providers/MediaInfo/AudioResolver.cs diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index de42c67d38..56e955adad 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -97,6 +97,12 @@ namespace MediaBrowser.Controller.Entities /// The subtitle paths. public string[] SubtitleFiles { get; set; } + /// + /// Gets or sets the audio paths. + /// + /// The audio paths. + public string[] AudioFiles { get; set; } + /// /// Gets or sets a value indicating whether this instance has subtitles. /// diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5715194b85..5326ecd20c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -678,6 +678,12 @@ namespace MediaBrowser.Controller.MediaEncoding arg.Append("-i ") .Append(GetInputPathArgument(state)); + if (state.AudioStream.IsExternal) + { + arg.Append(" -i ") + .Append(string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", state.AudioStream.Path)); + } + if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) @@ -1999,10 +2005,17 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream != null) { - args += string.Format( - CultureInfo.InvariantCulture, - " -map 0:{0}", - state.AudioStream.Index); + if (state.AudioStream.IsExternal) + { + args += " -map 1:a"; + } + else + { + args += string.Format( + CultureInfo.InvariantCulture, + " -map 0:{0}", + state.AudioStream.Index); + } } else { diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs new file mode 100644 index 0000000000..fce2fa5512 --- /dev/null +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -0,0 +1,275 @@ +#nullable disable + +#pragma warning disable CA1002, CS1591 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +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 +{ + public class AudioResolver + { + private readonly ILocalizationManager _localization; + + private readonly IMediaEncoder _mediaEncoder; + + private readonly CancellationToken _cancellationToken; + + public AudioResolver(ILocalizationManager localization, IMediaEncoder mediaEncoder, CancellationToken cancellationToken = default) + { + _localization = localization; + _mediaEncoder = mediaEncoder; + _cancellationToken = cancellationToken; + } + + public List GetExternalAudioStreams( + Video video, + int startIndex, + IDirectoryService directoryService, + bool clearCache) + { + var streams = new List(); + + if (!video.IsFileProtocol) + { + return streams; + } + + AddExternalAudioStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache); + + startIndex += streams.Count; + + string folder = video.GetInternalMetadataPath(); + + if (!Directory.Exists(folder)) + { + return streams; + } + + try + { + AddExternalAudioStreams(streams, folder, video.Path, startIndex, directoryService, clearCache); + } + catch (IOException) + { + } + + return streams; + } + + public IEnumerable GetExternalAudioFiles( + Video video, + IDirectoryService directoryService, + bool clearCache) + { + if (!video.IsFileProtocol) + { + yield break; + } + + var streams = GetExternalAudioStreams(video, 0, directoryService, clearCache); + + foreach (var stream in streams) + { + yield return stream.Path; + } + } + + public void AddExternalAudioStreams( + List streams, + string videoPath, + int startIndex, + IReadOnlyList files) + { + var videoFileNameWithoutExtension = NormalizeFilenameForAudioComparison(videoPath); + + for (var i = 0; i < files.Count; i++) + { + + var fullName = files[i]; + var extension = Path.GetExtension(fullName.AsSpan()); + if (!IsAudioExtension(extension)) + { + continue; + } + + Model.MediaInfo.MediaInfo mediaInfo = GetMediaInfo(fullName).Result; + MediaStream mediaStream = mediaInfo.MediaStreams.First(); + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Audio; + mediaStream.IsExternal = true; + mediaStream.Path = fullName; + mediaStream.IsDefault = false; + mediaStream.Title = null; + + var fileNameWithoutExtension = NormalizeFilenameForAudioComparison(fullName); + + // 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)) + { + mediaStream.Path = fullName; + } + else if (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length + && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.' + && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) + { + + // Support xbmc naming conventions - 300.spanish.m4a + var languageSpan = fileNameWithoutExtension; + while (languageSpan.Length > 0) + { + var lastDot = languageSpan.LastIndexOf('.'); + var currentSlice = languageSpan[lastDot..]; + languageSpan = languageSpan[(lastDot + 1)..]; + break; + } + + // Try to translate to three character code + // Be flexible and check against both the full and three character versions + var language = languageSpan.ToString(); + var culture = _localization.FindLanguageInfo(language); + + language = culture == null ? language : culture.ThreeLetterISOLanguageName; + mediaStream.Language = language; + } + else + { + continue; + } + + mediaStream.Codec = extension.TrimStart('.').ToString().ToLowerInvariant(); + + streams.Add(mediaStream); + } + } + + private static bool IsAudioExtension(ReadOnlySpan extension) + { + String[] audioExtensions = new[] + { + ".nsv", + ".m4a", + ".flac", + ".aac", + ".strm", + ".pls", + ".rm", + ".mpa", + ".wav", + ".wma", + ".ogg", + ".opus", + ".mp3", + ".mp2", + ".mod", + ".amf", + ".669", + ".dmf", + ".dsm", + ".far", + ".gdm", + ".imf", + ".it", + ".m15", + ".med", + ".okt", + ".s3m", + ".stm", + ".sfx", + ".ult", + ".uni", + ".xm", + ".sid", + ".ac3", + ".dts", + ".cue", + ".aif", + ".aiff", + ".ape", + ".mac", + ".mpc", + ".mp+", + ".mpp", + ".shn", + ".wv", + ".nsf", + ".spc", + ".gym", + ".adplug", + ".adx", + ".dsp", + ".adp", + ".ymf", + ".ast", + ".afc", + ".hps", + ".xsp", + ".acc", + ".m4b", + ".oga", + ".dsf", + ".mka" + }; + + foreach (String audioExtension in audioExtensions) + { + if (extension.Equals(audioExtension, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private Task GetMediaInfo(string path) + { + _cancellationToken.ThrowIfCancellationRequested(); + + return _mediaEncoder.GetMediaInfo( + new MediaInfoRequest + { + MediaType = DlnaProfileType.Audio, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = MediaProtocol.File + } + }, + _cancellationToken); + } + + private static ReadOnlySpan NormalizeFilenameForAudioComparison(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 AddExternalAudioStreams( + List streams, + string folder, + string videoPath, + int startIndex, + IDirectoryService directoryService, + bool clearCache) + { + var files = directoryService.GetFilePaths(folder, clearCache, true); + + AddExternalAudioStreams(streams, videoPath, startIndex, files); + } + } +} diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index d4b5d8655c..1e7fcf2d20 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -50,6 +50,8 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IMediaSourceManager _mediaSourceManager; private readonly SubtitleResolver _subtitleResolver; + private readonly AudioResolver _audioResolver; + private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); public FFProbeProvider( @@ -78,6 +80,7 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); + _audioResolver = new AudioResolver(BaseItem.LocalizationManager, mediaEncoder); } public string Name => "ffprobe"; @@ -111,6 +114,14 @@ namespace MediaBrowser.Providers.MediaInfo return true; } + if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder + && !video.AudioFiles.SequenceEqual( + _audioResolver.GetExternalAudioFiles(video, directoryService, false), StringComparer.Ordinal)) + { + _logger.LogDebug("Refreshing {0} due to external audio change.", item.Path); + return true; + } + return false; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 4ab15f60e2..8db095416c 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -214,6 +214,8 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + AddExternalAudio(video, mediaStreams, options, cancellationToken); + var libraryOptions = _libraryManager.GetLibraryOptions(video); if (mediaInfo != null) @@ -574,6 +576,29 @@ namespace MediaBrowser.Providers.MediaInfo currentStreams.AddRange(externalSubtitleStreams); } + /// + /// Adds the external audio. + /// + /// The video. + /// The current streams. + /// The refreshOptions. + /// The cancellation token. + private void AddExternalAudio( + Video video, + List currentStreams, + MetadataRefreshOptions options, + CancellationToken cancellationToken) + { + var audioResolver = new AudioResolver(_localization, _mediaEncoder, cancellationToken); + + var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1); + var externalAudioStreams = audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, false); + + video.AudioFiles = externalAudioStreams.Select(i => i.Path).ToArray(); + + currentStreams.AddRange(externalAudioStreams); + } + /// /// Creates dummy chapters. /// From c1a8385c9ceb70d9ad4301e7032e1719fe1bdc23 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:53:36 +0100 Subject: [PATCH 02/29] Shorten calculation of audio startIndex in MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 8db095416c..2d49e43cad 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -591,7 +591,7 @@ namespace MediaBrowser.Providers.MediaInfo { var audioResolver = new AudioResolver(_localization, _mediaEncoder, cancellationToken); - var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1); + var startIndex = currentStreams.Count == 0 ? 0 : currentStreams.Max(i => i.Index) + 1; var externalAudioStreams = audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, false); video.AudioFiles = externalAudioStreams.Select(i => i.Path).ToArray(); From 5e91f50c437adcf28cc80eba970c87fddf4f0c9f Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Sat, 27 Nov 2021 12:10:49 +0100 Subject: [PATCH 03/29] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5c4a031bc1..d52e133249 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -150,6 +150,7 @@ - [ianjazz246](https://github.com/ianjazz246) - [peterspenler](https://github.com/peterspenler) - [MBR-0001](https://github.com/MBR-0001) + - [jonas-resch](https://github.com/jonas-resch) # Emby Contributors From a68e58556c49ee9bc1c27fac696ffc9170c95e84 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Sat, 27 Nov 2021 12:10:57 +0100 Subject: [PATCH 04/29] Implement code feedback - Rewrite AudioResolver - Use async & await instead of .Result - Add support for audio containers with multiple audio streams (e.g. mka) - Fix bug when using external subtitle and external audio streams at the same time --- .../MediaEncoding/EncodingHelper.cs | 21 +- .../MediaInfo/AudioResolver.cs | 271 ++++++------------ .../MediaInfo/FFProbeProvider.cs | 16 +- .../MediaInfo/FFProbeVideoInfo.cs | 16 +- 4 files changed, 114 insertions(+), 210 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5326ecd20c..5695ee2dbb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -678,12 +678,6 @@ namespace MediaBrowser.Controller.MediaEncoding arg.Append("-i ") .Append(GetInputPathArgument(state)); - if (state.AudioStream.IsExternal) - { - arg.Append(" -i ") - .Append(string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", state.AudioStream.Path)); - } - if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && state.SubtitleStream.IsExternal && !state.SubtitleStream.IsTextSubtitleStream) @@ -702,6 +696,12 @@ namespace MediaBrowser.Controller.MediaEncoding arg.Append(" -i \"").Append(subtitlePath).Append('\"'); } + if (state.AudioStream != null && state.AudioStream.IsExternal) + { + arg.Append(" -i ") + .Append(string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", state.AudioStream.Path)); + } + return arg.ToString(); } @@ -2007,7 +2007,14 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.AudioStream.IsExternal) { - args += " -map 1:a"; + int externalAudioMapIndex = state.SubtitleStream != null && state.SubtitleStream.IsExternal ? 2 : 1; + int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); + + args += string.Format( + CultureInfo.InvariantCulture, + " -map {0}:{1}", + externalAudioMapIndex, + externalAudioStream); } else { diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index fce2fa5512..8d5f8d86ec 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -1,13 +1,13 @@ -#nullable disable - #pragma warning disable CA1002, CS1591 using System; using System.Collections.Generic; using System.IO; -using System.Linq; 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; @@ -21,24 +21,15 @@ namespace MediaBrowser.Providers.MediaInfo { public class AudioResolver { - private readonly ILocalizationManager _localization; - - private readonly IMediaEncoder _mediaEncoder; - - private readonly CancellationToken _cancellationToken; - - public AudioResolver(ILocalizationManager localization, IMediaEncoder mediaEncoder, CancellationToken cancellationToken = default) - { - _localization = localization; - _mediaEncoder = mediaEncoder; - _cancellationToken = cancellationToken; - } - - public List GetExternalAudioStreams( + public async Task> GetExternalAudioStreams( Video video, int startIndex, IDirectoryService directoryService, - bool clearCache) + NamingOptions namingOptions, + bool clearCache, + ILocalizationManager localizationManager, + IMediaEncoder mediaEncoder, + CancellationToken cancellationToken) { var streams = new List(); @@ -47,198 +38,117 @@ namespace MediaBrowser.Providers.MediaInfo return streams; } - AddExternalAudioStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache); - - startIndex += streams.Count; + List paths = GetExternalAudioFiles(video, directoryService, namingOptions, clearCache); - string folder = video.GetInternalMetadataPath(); - - if (!Directory.Exists(folder)) - { - return streams; - } - - try - { - AddExternalAudioStreams(streams, folder, video.Path, startIndex, directoryService, clearCache); - } - catch (IOException) - { - } + await AddExternalAudioStreams(streams, paths, startIndex, localizationManager, mediaEncoder, cancellationToken); return streams; } - public IEnumerable GetExternalAudioFiles( + public List GetExternalAudioFiles( Video video, IDirectoryService directoryService, + NamingOptions namingOptions, bool clearCache) { + List paths = new List(); + if (!video.IsFileProtocol) { - yield break; + return paths; } - var streams = GetExternalAudioStreams(video, 0, directoryService, clearCache); + paths.AddRange(GetAudioFilesFromFolder(video.ContainingFolderPath, video.Path, directoryService, namingOptions, clearCache)); + paths.AddRange(GetAudioFilesFromFolder(video.GetInternalMetadataPath(), video.Path, directoryService, namingOptions, clearCache)); - foreach (var stream in streams) - { - yield return stream.Path; - } + return paths; } - public void AddExternalAudioStreams( - List streams, - string videoPath, - int startIndex, - IReadOnlyList files) + private List GetAudioFilesFromFolder( + string folder, + string videoFileName, + IDirectoryService directoryService, + NamingOptions namingOptions, + bool clearCache) { - var videoFileNameWithoutExtension = NormalizeFilenameForAudioComparison(videoPath); + List paths = new List(); + string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(videoFileName); - for (var i = 0; i < files.Count; i++) + 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]); - var fullName = files[i]; - var extension = Path.GetExtension(fullName.AsSpan()); - if (!IsAudioExtension(extension)) + if (!AudioFileParser.IsAudioFile(files[i], namingOptions)) { continue; } - Model.MediaInfo.MediaInfo mediaInfo = GetMediaInfo(fullName).Result; - MediaStream mediaStream = mediaInfo.MediaStreams.First(); - mediaStream.Index = startIndex++; - mediaStream.Type = MediaStreamType.Audio; - mediaStream.IsExternal = true; - mediaStream.Path = fullName; - mediaStream.IsDefault = false; - mediaStream.Title = null; - - var fileNameWithoutExtension = NormalizeFilenameForAudioComparison(fullName); - // 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)) - { - mediaStream.Path = fullName; - } - else if (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length + if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) || + (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.' - && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) + && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))) { - - // Support xbmc naming conventions - 300.spanish.m4a - var languageSpan = fileNameWithoutExtension; - while (languageSpan.Length > 0) - { - var lastDot = languageSpan.LastIndexOf('.'); - var currentSlice = languageSpan[lastDot..]; - languageSpan = languageSpan[(lastDot + 1)..]; - break; - } - - // Try to translate to three character code - // Be flexible and check against both the full and three character versions - var language = languageSpan.ToString(); - var culture = _localization.FindLanguageInfo(language); - - language = culture == null ? language : culture.ThreeLetterISOLanguageName; - mediaStream.Language = language; - } - else - { - continue; + paths.Add(files[i]); } - - mediaStream.Codec = extension.TrimStart('.').ToString().ToLowerInvariant(); - - streams.Add(mediaStream); } + + return paths; } - private static bool IsAudioExtension(ReadOnlySpan extension) + public async Task AddExternalAudioStreams( + List streams, + List paths, + int startIndex, + ILocalizationManager localizationManager, + IMediaEncoder mediaEncoder, + CancellationToken cancellationToken) { - String[] audioExtensions = new[] + foreach (string path in paths) { - ".nsv", - ".m4a", - ".flac", - ".aac", - ".strm", - ".pls", - ".rm", - ".mpa", - ".wav", - ".wma", - ".ogg", - ".opus", - ".mp3", - ".mp2", - ".mod", - ".amf", - ".669", - ".dmf", - ".dsm", - ".far", - ".gdm", - ".imf", - ".it", - ".m15", - ".med", - ".okt", - ".s3m", - ".stm", - ".sfx", - ".ult", - ".uni", - ".xm", - ".sid", - ".ac3", - ".dts", - ".cue", - ".aif", - ".aiff", - ".ape", - ".mac", - ".mpc", - ".mp+", - ".mpp", - ".shn", - ".wv", - ".nsf", - ".spc", - ".gym", - ".adplug", - ".adx", - ".dsp", - ".adp", - ".ymf", - ".ast", - ".afc", - ".hps", - ".xsp", - ".acc", - ".m4b", - ".oga", - ".dsf", - ".mka" - }; + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, mediaEncoder, cancellationToken); - foreach (String audioExtension in audioExtensions) - { - if (extension.Equals(audioExtension, StringComparison.OrdinalIgnoreCase)) + foreach (MediaStream mediaStream in mediaInfo.MediaStreams) { - return true; + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Audio; + mediaStream.IsExternal = true; + mediaStream.Path = path; + mediaStream.IsDefault = false; + mediaStream.Title = null; + + if (mediaStream.Language == null) + { + // 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(); + + if (language != fileNameWithoutExtension) + { + var culture = localizationManager.FindLanguageInfo(language); + + language = culture == null ? language : culture.ThreeLetterISOLanguageName; + mediaStream.Language = language; + } + } + + streams.Add(mediaStream); } } - - return false; } - private Task GetMediaInfo(string path) + private Task GetMediaInfo(string path, IMediaEncoder mediaEncoder, CancellationToken cancellationToken) { - _cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - return _mediaEncoder.GetMediaInfo( + return mediaEncoder.GetMediaInfo( new MediaInfoRequest { MediaType = DlnaProfileType.Audio, @@ -248,28 +158,7 @@ namespace MediaBrowser.Providers.MediaInfo Protocol = MediaProtocol.File } }, - _cancellationToken); - } - - private static ReadOnlySpan NormalizeFilenameForAudioComparison(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 AddExternalAudioStreams( - List streams, - string folder, - string videoPath, - int startIndex, - IDirectoryService directoryService, - bool clearCache) - { - var files = directoryService.GetFilePaths(folder, clearCache, true); - - AddExternalAudioStreams(streams, videoPath, startIndex, files); + cancellationToken); } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 1e7fcf2d20..98909c94ec 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Emby.Naming.Common; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -50,10 +51,10 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IMediaSourceManager _mediaSourceManager; private readonly SubtitleResolver _subtitleResolver; - private readonly AudioResolver _audioResolver; - private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); + private readonly NamingOptions _namingOptions; + public FFProbeProvider( ILogger logger, IMediaSourceManager mediaSourceManager, @@ -65,7 +66,8 @@ namespace MediaBrowser.Providers.MediaInfo IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, - ILibraryManager libraryManager) + ILibraryManager libraryManager, + NamingOptions namingOptions) { _logger = logger; _mediaEncoder = mediaEncoder; @@ -78,9 +80,9 @@ namespace MediaBrowser.Providers.MediaInfo _chapterManager = chapterManager; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; + _namingOptions = namingOptions; _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); - _audioResolver = new AudioResolver(BaseItem.LocalizationManager, mediaEncoder); } public string Name => "ffprobe"; @@ -114,9 +116,10 @@ namespace MediaBrowser.Providers.MediaInfo return true; } + AudioResolver audioResolver = new AudioResolver(); if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder && !video.AudioFiles.SequenceEqual( - _audioResolver.GetExternalAudioFiles(video, directoryService, false), StringComparer.Ordinal)) + audioResolver.GetExternalAudioFiles(video, directoryService, _namingOptions, false), StringComparer.Ordinal)) { _logger.LogDebug("Refreshing {0} due to external audio change.", item.Path); return true; @@ -199,7 +202,8 @@ namespace MediaBrowser.Providers.MediaInfo _config, _subtitleManager, _chapterManager, - _libraryManager); + _libraryManager, + _namingOptions); return prober.ProbeVideo(item, options, cancellationToken); } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 2d49e43cad..39950db70f 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -10,6 +10,7 @@ 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; @@ -44,6 +45,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; + private readonly NamingOptions _namingOptions; private readonly IMediaSourceManager _mediaSourceManager; private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks; @@ -59,7 +61,8 @@ namespace MediaBrowser.Providers.MediaInfo IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, - ILibraryManager libraryManager) + ILibraryManager libraryManager, + NamingOptions namingOptions) { _logger = logger; _mediaEncoder = mediaEncoder; @@ -71,6 +74,7 @@ namespace MediaBrowser.Providers.MediaInfo _subtitleManager = subtitleManager; _chapterManager = chapterManager; _libraryManager = libraryManager; + _namingOptions = namingOptions; _mediaSourceManager = mediaSourceManager; } @@ -214,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - AddExternalAudio(video, mediaStreams, options, cancellationToken); + await AddExternalAudio(video, mediaStreams, options, cancellationToken); var libraryOptions = _libraryManager.GetLibraryOptions(video); @@ -583,18 +587,18 @@ namespace MediaBrowser.Providers.MediaInfo /// The current streams. /// The refreshOptions. /// The cancellation token. - private void AddExternalAudio( + private async Task AddExternalAudio( Video video, List currentStreams, MetadataRefreshOptions options, CancellationToken cancellationToken) { - var audioResolver = new AudioResolver(_localization, _mediaEncoder, cancellationToken); + var audioResolver = new AudioResolver(); var startIndex = currentStreams.Count == 0 ? 0 : currentStreams.Max(i => i.Index) + 1; - var externalAudioStreams = audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, false); + var externalAudioStreams = await audioResolver.GetExternalAudioStreams(video, startIndex, options.DirectoryService, _namingOptions, false, _localization, _mediaEncoder, cancellationToken); - video.AudioFiles = externalAudioStreams.Select(i => i.Path).ToArray(); + video.AudioFiles = externalAudioStreams.Select(i => i.Path).Distinct().ToArray(); currentStreams.AddRange(externalAudioStreams); } From f1862f9b1a9d6daa0315308671cfb6d0fdd6a989 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:50:24 +0100 Subject: [PATCH 05/29] Add ConfigureAwait false to MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 8d5f8d86ec..2a50dc5d11 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Providers.MediaInfo List paths = GetExternalAudioFiles(video, directoryService, namingOptions, clearCache); - await AddExternalAudioStreams(streams, paths, startIndex, localizationManager, mediaEncoder, cancellationToken); + await AddExternalAudioStreams(streams, paths, startIndex, localizationManager, mediaEncoder, cancellationToken).ConfigureAwait(false); return streams; } From a3c5afa443dca5cfd90ff1a33b7af9dbfe751ff4 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:50:39 +0100 Subject: [PATCH 06/29] Add ConfigureAwait false MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs Co-authored-by: Cody Robibero --- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 39950db70f..7450205993 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -218,7 +218,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - await AddExternalAudio(video, mediaStreams, options, cancellationToken); + await AddExternalAudio(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); var libraryOptions = _libraryManager.GetLibraryOptions(video); From 9433072f90593a43d2faa4eb645eb521a257205c Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:52:03 +0100 Subject: [PATCH 07/29] Only search in video folder for external audio files Don't search in video metadata folder since audio files won't be stored there Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 2a50dc5d11..2f73819834 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -59,7 +59,6 @@ namespace MediaBrowser.Providers.MediaInfo } paths.AddRange(GetAudioFilesFromFolder(video.ContainingFolderPath, video.Path, directoryService, namingOptions, clearCache)); - paths.AddRange(GetAudioFilesFromFolder(video.GetInternalMetadataPath(), video.Path, directoryService, namingOptions, clearCache)); return paths; } From 61b191d34556a0dad260344e83b9f40ac12f28ce Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:55:19 +0100 Subject: [PATCH 08/29] Fix indentation in MediaBrowser.Providers/MediaInfo/AudioResolver.cs If statement which checks if filename of audio and video file match or if audio file starts with video filename Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 2f73819834..481a82df49 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -89,10 +89,10 @@ namespace MediaBrowser.Providers.MediaInfo } // 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))) + if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) + || (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length + && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.' + && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))) { paths.Add(files[i]); } From d016d483ae5ab79f6f1171ce7f6ca99b8cd91cf0 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:56:17 +0100 Subject: [PATCH 09/29] Change return type from Task> to Task> in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 481a82df49..bd778684ed 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Providers.MediaInfo { public class AudioResolver { - public async Task> GetExternalAudioStreams( + public async Task> GetExternalAudioStreams( Video video, int startIndex, IDirectoryService directoryService, From bbf1399826f92241a653adbb808a4cc7ea6a5542 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:57:53 +0100 Subject: [PATCH 10/29] Check language for null or empty instead of only null in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index bd778684ed..54d8525778 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -123,7 +123,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaStream.IsDefault = false; mediaStream.Title = null; - if (mediaStream.Language == null) + if (string.IsNullOrEmpty(mediaStream.Language)) { // Try to translate to three character code // Be flexible and check against both the full and three character versions From 9d34d6339a2d677139573ada98b92318de298653 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Sat, 27 Nov 2021 16:58:37 +0100 Subject: [PATCH 11/29] Change return type from List to IEnumerable in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 54d8525778..ccac450f6c 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Providers.MediaInfo return streams; } - public List GetExternalAudioFiles( + public IEnumerable GetExternalAudioFiles( Video video, IDirectoryService directoryService, NamingOptions namingOptions, From 0894a6193f025a9cec5c735226a8487caa2bc66b Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Sun, 28 Nov 2021 14:03:52 +0100 Subject: [PATCH 12/29] Implement coding standards from 2nd code feedback --- .../MediaEncoding/EncodingHelper.cs | 3 +- .../MediaInfo/AudioResolver.cs | 138 ++++++++---------- .../MediaInfo/FFProbeProvider.cs | 15 +- .../MediaInfo/FFProbeVideoInfo.cs | 21 +-- 4 files changed, 78 insertions(+), 99 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5695ee2dbb..5712303123 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 ccac450f6c..d23afdc3b9 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -21,69 +21,93 @@ 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); + IEnumerable paths = GetExternalAudioFiles(video, directoryService, clearCache); + foreach (string path in paths) + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken); - await AddExternalAudioStreams(streams, paths, startIndex, localizationManager, mediaEncoder, cancellationToken).ConfigureAwait(false); + foreach (MediaStream mediaStream in mediaInfo.MediaStreams) + { + mediaStream.Index = startIndex++; + mediaStream.Type = MediaStreamType.Audio; + mediaStream.IsExternal = true; + mediaStream.Path = path; + mediaStream.IsDefault = false; + mediaStream.Title = null; - return streams; + if (string.IsNullOrEmpty(mediaStream.Language)) + { + // 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(); + + if (language != fileNameWithoutExtension) + { + var culture = _localizationManager.FindLanguageInfo(language); + + language = culture == null ? language : culture.ThreeLetterISOLanguageName; + mediaStream.Language = language; + } + } + + yield return mediaStream; + } + } } public IEnumerable GetExternalAudioFiles( Video video, IDirectoryService directoryService, - NamingOptions namingOptions, bool clearCache) { - List paths = new List(); - if (!video.IsFileProtocol) { - return paths; + yield break; } - 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); - + // Check if video folder exists + string folder = video.ContainingFolderPath; if (!Directory.Exists(folder)) { - return paths; + yield break; } + string videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); + var files = directoryService.GetFilePaths(folder, clearCache, true); for (int i = 0; i < files.Count; i++) { - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); + string file = files[i]; + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (!AudioFileParser.IsAudioFile(files[i], namingOptions)) + if (!AudioFileParser.IsAudioFile(file, _namingOptions)) { continue; } @@ -94,60 +118,16 @@ namespace MediaBrowser.Providers.MediaInfo && 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) - { - foreach (string path in paths) - { - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); - Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, mediaEncoder, cancellationToken); - - foreach (MediaStream mediaStream in mediaInfo.MediaStreams) - { - mediaStream.Index = startIndex++; - mediaStream.Type = MediaStreamType.Audio; - mediaStream.IsExternal = true; - mediaStream.Path = path; - mediaStream.IsDefault = false; - mediaStream.Title = null; - - if (string.IsNullOrEmpty(mediaStream.Language)) - { - // 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(); - - if (language != fileNameWithoutExtension) - { - var culture = localizationManager.FindLanguageInfo(language); - - language = culture == null ? language : culture.ThreeLetterISOLanguageName; - mediaStream.Language = language; - } - } - - streams.Add(mediaStream); + yield return file; } } } - private Task GetMediaInfo(string path, IMediaEncoder mediaEncoder, CancellationToken cancellationToken) + 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 98909c94ec..392641468e 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 7450205993..b31f0ed23d 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(); } /// From b5b994b22f8ec8cf0fd10f67d78d66dd12b3e21c Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Sun, 28 Nov 2021 14:20:12 +0100 Subject: [PATCH 13/29] Fix compiler warning due to missing EnumeratorCancellation attribute --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index d23afdc3b9..869512f76c 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Emby.Naming.Audio; @@ -40,8 +41,11 @@ namespace MediaBrowser.Providers.MediaInfo int startIndex, IDirectoryService directoryService, bool clearCache, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { + + cancellationToken.ThrowIfCancellationRequested(); + if (!video.IsFileProtocol) { yield break; From c61b9ef05a4a49a90378e9f81275eb84c2b55eed Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Tue, 30 Nov 2021 19:52:44 +0100 Subject: [PATCH 14/29] Fix warning due to new line after opening bracket --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 869512f76c..35351665a3 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -43,7 +43,6 @@ namespace MediaBrowser.Providers.MediaInfo bool clearCache, [EnumeratorCancellation] CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); if (!video.IsFileProtocol) From 1a356908346fbfe1ba0cf438b2a5c8248fd3f371 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Tue, 30 Nov 2021 20:44:16 +0100 Subject: [PATCH 15/29] Don't disable warnings in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 35351665a3..795c4d6ce5 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA1002, CS1591 - using System; using System.Collections.Generic; using System.IO; From 0d8170cedb1fd711e5c2d09d458467cccdd7874a Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Tue, 30 Nov 2021 20:44:57 +0100 Subject: [PATCH 16/29] Move variable in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 795c4d6ce5..6d1486698f 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -106,13 +106,12 @@ namespace MediaBrowser.Providers.MediaInfo for (int i = 0; i < files.Count; i++) { string file = files[i]; - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (!AudioFileParser.IsAudioFile(file, _namingOptions)) { 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 From a9a53dc6579184edfb70ce289c06176e769d5280 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Tue, 30 Nov 2021 20:45:21 +0100 Subject: [PATCH 17/29] Add ConfigureAwait true in MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index b31f0ed23d..542e56256e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -217,7 +217,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - await AddExternalAudio(video, mediaStreams, options, cancellationToken); + await AddExternalAudio(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); var libraryOptions = _libraryManager.GetLibraryOptions(video); From 7b500480201cef84f5d794c9f6319cccf8b7b03b Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Tue, 30 Nov 2021 20:45:47 +0100 Subject: [PATCH 18/29] Add ConfigureAwait true in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 6d1486698f..b6f3ac1cc1 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.MediaInfo foreach (string path in paths) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); - Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken); + Model.MediaInfo.MediaInfo mediaInfo = await GetMediaInfo(path, cancellationToken).ConfigureAwait(false); foreach (MediaStream mediaStream in mediaInfo.MediaStreams) { From 6bbfcf1906b7a35da3db4954f9a3762bca9f3a93 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Tue, 30 Nov 2021 21:05:43 +0100 Subject: [PATCH 19/29] Add documentation to AudioResolver class --- .../MediaInfo/AudioResolver.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index b6f3ac1cc1..20dee834ff 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -18,12 +18,21 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Providers.MediaInfo { + /// + /// Resolves external audios for videos. + /// public class AudioResolver { private readonly ILocalizationManager _localizationManager; private readonly IMediaEncoder _mediaEncoder; private readonly NamingOptions _namingOptions; + /// + /// Initializes a new instance of the class. + /// + /// The localization manager. + /// The media encoder. + /// The naming options. public AudioResolver( ILocalizationManager localizationManager, IMediaEncoder mediaEncoder, @@ -34,6 +43,15 @@ namespace MediaBrowser.Providers.MediaInfo _namingOptions = namingOptions; } + /// + /// Returns the audio streams found in the external audio files for the given video. + /// + /// The video to get the external audio streams from. + /// The stream index to start adding audio streams at. + /// The directory service to search for files. + /// True if the directory service cache should be cleared before searching. + /// The cancellation token to cancel operation. + /// A list of external audio streams. public async IAsyncEnumerable GetExternalAudioStreams( Video video, int startIndex, @@ -83,6 +101,13 @@ namespace MediaBrowser.Providers.MediaInfo } } + /// + /// Returns the external audio file paths for the given video. + /// + /// The video to get the external audio file paths from. + /// The directory service to search for files. + /// True if the directory service cache should be cleared before searching. + /// A list of external audio file paths. public IEnumerable GetExternalAudioFiles( Video video, IDirectoryService directoryService, @@ -123,6 +148,12 @@ namespace MediaBrowser.Providers.MediaInfo } } + /// + /// Returns the media info of the given audio file. + /// + /// The path to the audio file. + /// The cancellation token to cancel operation. + /// The media info for the given audio file. private Task GetMediaInfo(string path, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); From 180e2dc329434a68a5fe3a8ac05591d0a7c898f8 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Wed, 1 Dec 2021 21:05:43 +0100 Subject: [PATCH 20/29] Prevent crashes in specific scenarios --- MediaBrowser.Controller/Entities/Video.cs | 1 + MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 56e955adad..8e0593507a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.Entities AdditionalParts = Array.Empty(); LocalAlternateVersions = Array.Empty(); SubtitleFiles = Array.Empty(); + AudioFiles = Array.Empty(); LinkedAlternateVersions = Array.Empty(); } diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index 20dee834ff..bec8ee34a6 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.MediaInfo for (int i = 0; i < files.Count; i++) { string file = files[i]; - if (!AudioFileParser.IsAudioFile(file, _namingOptions)) + if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase) || !AudioFileParser.IsAudioFile(file, _namingOptions)) { continue; } From 120828d8d03da08a55edfbce5bfe1463bf06eae3 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Fri, 3 Dec 2021 19:18:43 +0100 Subject: [PATCH 21/29] Replace escaped quote string with quote character in MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs Co-authored-by: Cody Robibero --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5712303123..92b345f126 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -698,7 +698,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream != null && state.AudioStream.IsExternal) { - arg.Append(" -i \"").Append(state.AudioStream.Path).Append("\""); + arg.Append(" -i \"").Append(state.AudioStream.Path).Append('"'); } return arg.ToString(); From 99a48554a618302b4fe70ef1fd3d7fd06096c70e Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Fri, 3 Dec 2021 19:19:22 +0100 Subject: [PATCH 22/29] Optimize calculation of external audio stream index in MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs Co-authored-by: Cody Robibero --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 92b345f126..91f6654bb7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2007,7 +2007,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream.IsExternal) { int externalAudioMapIndex = state.SubtitleStream != null && state.SubtitleStream.IsExternal ? 2 : 1; - int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); + int externalAudioStream = state.MediaSource.MediaStreams.FindIndex(i => i.Path == state.AudioStream.Path); args += string.Format( CultureInfo.InvariantCulture, From a327b43ab7faceadb555890c41f103008fc00737 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 7 Dec 2021 20:28:51 +0100 Subject: [PATCH 23/29] Update MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs --- MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 8445a12aac..fc59e410fd 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -45,7 +45,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly FFProbeAudioInfo _audioProber; private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); - private readonly NamingOptions _namingOptions; public FFProbeProvider( ILogger logger, From 01a0a4a87ca6b89d5b235e5b240afb0abf44809d Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Wed, 8 Dec 2021 10:16:48 +0100 Subject: [PATCH 24/29] Add audioResolver argument to FFProbeVideoInfo initialization --- MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index fc59e410fd..19a435196a 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -74,7 +74,8 @@ namespace MediaBrowser.Providers.MediaInfo config, subtitleManager, chapterManager, - libraryManager); + libraryManager, + _audioResolver); _audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager); } From d47811bdaf8bbfc74c8185ef0bff7490726828d9 Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Wed, 8 Dec 2021 10:17:25 +0100 Subject: [PATCH 25/29] Fix wrong ffmpeg map argument due to wrong calculation --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 91f6654bb7..92b345f126 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2007,7 +2007,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.AudioStream.IsExternal) { int externalAudioMapIndex = state.SubtitleStream != null && state.SubtitleStream.IsExternal ? 2 : 1; - int externalAudioStream = state.MediaSource.MediaStreams.FindIndex(i => i.Path == state.AudioStream.Path); + int externalAudioStream = state.MediaSource.MediaStreams.Where(i => i.Path == state.AudioStream.Path).ToList().IndexOf(state.AudioStream); args += string.Format( CultureInfo.InvariantCulture, From 4cdb590291086ee623ab8ed945f9707ade94777a Mon Sep 17 00:00:00 2001 From: Jonas Resch Date: Wed, 8 Dec 2021 10:18:09 +0100 Subject: [PATCH 26/29] Exclude .strm files when searching for external audio files --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index bec8ee34a6..b68b4c248c 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.MediaInfo for (int i = 0; i < files.Count; i++) { string file = files[i]; - if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase) || !AudioFileParser.IsAudioFile(file, _namingOptions)) + if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase) || !AudioFileParser.IsAudioFile(file, _namingOptions) || Path.GetExtension(file).Equals(".strm", StringComparison.OrdinalIgnoreCase)) { continue; } From e18d966874cbdcab16ee99571d88147a9ac198fb Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Wed, 8 Dec 2021 16:49:20 +0100 Subject: [PATCH 27/29] Add "Async" suffix to AddExternalAudio method Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 542e56256e..07d87e6076 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -586,7 +586,7 @@ namespace MediaBrowser.Providers.MediaInfo /// The current streams. /// The refreshOptions. /// The cancellation token. - private async Task AddExternalAudio( + private async Task AddExternalAudioAsync( Video video, List currentStreams, MetadataRefreshOptions options, From 65833076dbc574cc830fdd370a3a6127cfdc3bcf Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Wed, 8 Dec 2021 16:49:27 +0100 Subject: [PATCH 28/29] Add "Async" suffix to AddExternalAudio method Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 07d87e6076..77372e0635 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -217,7 +217,7 @@ namespace MediaBrowser.Providers.MediaInfo await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); - await AddExternalAudio(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + await AddExternalAudioAsync(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); var libraryOptions = _libraryManager.GetLibraryOptions(video); From 03b3f08354f496d18444779977bfc1602bbb2c73 Mon Sep 17 00:00:00 2001 From: Jonas Resch <32968142+jonas-resch@users.noreply.github.com> Date: Wed, 8 Dec 2021 18:55:28 +0100 Subject: [PATCH 29/29] Format code in MediaBrowser.Providers/MediaInfo/AudioResolver.cs Co-authored-by: Claus Vium --- MediaBrowser.Providers/MediaInfo/AudioResolver.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs index b68b4c248c..425913501a 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioResolver.cs @@ -131,7 +131,9 @@ namespace MediaBrowser.Providers.MediaInfo 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).Equals(".strm", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(video.Path, file, StringComparison.OrdinalIgnoreCase) + || !AudioFileParser.IsAudioFile(file, _namingOptions) + || Path.GetExtension(file.AsSpan()).Equals(".strm", StringComparison.OrdinalIgnoreCase)) { continue; }