diff options
| author | Joshua M. Boniface <joshua@boniface.me> | 2025-08-03 17:27:17 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-03 17:27:17 -0400 |
| commit | 4b6fb6c4bb2478badad068ce18aabe0c2955db48 (patch) | |
| tree | 15f986ee62327cceb8f5c8f009bcf08d10cfaa66 /Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs | |
| parent | e7bc86ebb8496615e0b3f73eb4f13ab4c0913dc8 (diff) | |
| parent | db7465e83d9cc07134a0bffad7ed17b1c7b873da (diff) | |
Merge branch 'master' into master
Diffstat (limited to 'Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs')
| -rw-r--r-- | Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs | 150 |
1 files changed, 89 insertions, 61 deletions
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs index 031d14776..e912e9f01 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs @@ -76,93 +76,111 @@ public partial class AudioNormalizationTask : IScheduledTask /// <inheritdoc /> public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) { - foreach (var library in _libraryManager.RootFolder.Children) + var numComplete = 0; + var libraries = _libraryManager.RootFolder.Children.Where(library => _libraryManager.GetLibraryOptions(library).EnableLUFSScan).ToArray(); + double percent = 0; + + foreach (var library in libraries) { - var libraryOptions = _libraryManager.GetLibraryOptions(library); - if (!libraryOptions.EnableLUFSScan) - { - continue; - } + var albums = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = [BaseItemKind.MusicAlbum], Parent = library, Recursive = true }); - // Album gain - var albums = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = [BaseItemKind.MusicAlbum], - Parent = library, - Recursive = true - }); + double nextPercent = numComplete + 1; + nextPercent /= libraries.Length; + nextPercent -= percent; + // Split the progress for this single library into two halves: album gain and track gain. + // The first half will be for album gain, the second half for track gain. + nextPercent /= 2; + var albumComplete = 0; foreach (var a in albums) { - if (a.NormalizationGain.HasValue || a.LUFS.HasValue) + if (!a.NormalizationGain.HasValue && !a.LUFS.HasValue) { - continue; + // Album gain + var albumTracks = ((MusicAlbum)a).Tracks.Where(x => x.IsFileProtocol).ToList(); + + // Skip albums that don't have multiple tracks, album gain is useless here + if (albumTracks.Count > 1) + { + _logger.LogInformation("Calculating LUFS for album: {Album} with id: {Id}", a.Name, a.Id); + var tempDir = _applicationPaths.TempDirectory; + Directory.CreateDirectory(tempDir); + var tempFile = Path.Join(tempDir, a.Id + ".concat"); + var inputLines = albumTracks.Select(x => string.Format(CultureInfo.InvariantCulture, "file '{0}'", x.Path.Replace("'", @"'\''", StringComparison.Ordinal))); + await File.WriteAllLinesAsync(tempFile, inputLines, cancellationToken).ConfigureAwait(false); + try + { + a.LUFS = await CalculateLUFSAsync( + string.Format(CultureInfo.InvariantCulture, "-f concat -safe 0 -i \"{0}\"", tempFile), + OperatingSystem.IsWindows(), // Wait for process to exit on Windows before we try deleting the concat file + cancellationToken).ConfigureAwait(false); + } + finally + { + File.Delete(tempFile); + } + } } - // Skip albums that don't have multiple tracks, album gain is useless here - var albumTracks = ((MusicAlbum)a).Tracks.Where(x => x.IsFileProtocol).ToList(); - if (albumTracks.Count <= 1) - { - continue; - } + // Update sub-progress for album gain + albumComplete++; + double albumPercent = albumComplete; + albumPercent /= albums.Count; - _logger.LogInformation("Calculating LUFS for album: {Album} with id: {Id}", a.Name, a.Id); - var tempDir = _applicationPaths.TempDirectory; - Directory.CreateDirectory(tempDir); - var tempFile = Path.Join(tempDir, a.Id + ".concat"); - var inputLines = albumTracks.Select(x => string.Format(CultureInfo.InvariantCulture, "file '{0}'", x.Path.Replace("'", @"'\''", StringComparison.Ordinal))); - await File.WriteAllLinesAsync(tempFile, inputLines, cancellationToken).ConfigureAwait(false); - try - { - a.LUFS = await CalculateLUFSAsync( - string.Format(CultureInfo.InvariantCulture, "-f concat -safe 0 -i \"{0}\"", tempFile), - cancellationToken).ConfigureAwait(false); - } - finally - { - File.Delete(tempFile); - } + progress.Report(100 * (percent + (albumPercent * nextPercent))); } + // Update progress to start at the track gain percent calculation + percent += nextPercent; + _itemRepository.SaveItems(albums, cancellationToken); // Track gain - var tracks = _libraryManager.GetItemList(new InternalItemsQuery - { - MediaTypes = [MediaType.Audio], - IncludeItemTypes = [BaseItemKind.Audio], - Parent = library, - Recursive = true - }); + var tracks = _libraryManager.GetItemList(new InternalItemsQuery { MediaTypes = [MediaType.Audio], IncludeItemTypes = [BaseItemKind.Audio], Parent = library, Recursive = true }); + var tracksComplete = 0; foreach (var t in tracks) { - if (t.NormalizationGain.HasValue || t.LUFS.HasValue || !t.IsFileProtocol) + if (!t.NormalizationGain.HasValue && !t.LUFS.HasValue && t.IsFileProtocol) { - continue; + t.LUFS = await CalculateLUFSAsync( + string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), + false, + cancellationToken).ConfigureAwait(false); } - t.LUFS = await CalculateLUFSAsync(string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)), cancellationToken).ConfigureAwait(false); + // Update sub-progress for track gain + tracksComplete++; + double trackPercent = tracksComplete; + trackPercent /= tracks.Count; + + progress.Report(100 * (percent + (trackPercent * nextPercent))); } _itemRepository.SaveItems(tracks, cancellationToken); + + // Update progress + numComplete++; + percent = numComplete; + percent /= libraries.Length; + + progress.Report(100 * percent); } + + progress.Report(100.0); } /// <inheritdoc /> public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() { - return - [ - new TaskTriggerInfo - { - Type = TaskTriggerInfoType.IntervalTrigger, - IntervalTicks = TimeSpan.FromHours(24).Ticks - } - ]; + yield return new TaskTriggerInfo + { + Type = TaskTriggerInfoType.IntervalTrigger, + IntervalTicks = TimeSpan.FromHours(24).Ticks + }; } - private async Task<float?> CalculateLUFSAsync(string inputArgs, CancellationToken cancellationToken) + private async Task<float?> CalculateLUFSAsync(string inputArgs, bool waitForExit, CancellationToken cancellationToken) { var args = $"-hide_banner {inputArgs} -af ebur128=framelog=verbose -f null -"; @@ -189,18 +207,28 @@ public partial class AudioNormalizationTask : IScheduledTask } using var reader = process.StandardError; - await foreach (var line in reader.ReadAllLinesAsync(cancellationToken)) + float? lufs = null; + await foreach (var line in reader.ReadAllLinesAsync(cancellationToken).ConfigureAwait(false)) { Match match = LUFSRegex().Match(line); - if (match.Success) { - return float.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + lufs = float.Parse(match.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + break; } } - _logger.LogError("Failed to find LUFS value in output"); - return null; + if (lufs is null) + { + _logger.LogError("Failed to find LUFS value in output"); + } + + if (waitForExit) + { + await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false); + } + + return lufs; } } } |
