From 355f2eba2a2d0f6135ab5f82da13980f04a8e87a Mon Sep 17 00:00:00 2001 From: zarzet Date: Fri, 3 Apr 2026 00:56:09 +0700 Subject: [PATCH] fix: resolve missing track/disc numbers from search downloads and suppress FFmpeg log noise - Tidal: use actual API track_number/disc_number when request values are 0 (fixes search/popular downloads having no track position in metadata) - Extension enrichment: copy TrackNumber/DiscNumber back from enriched results - Extension fallback download: add request metadata fallback for non-source extensions (Album, AlbumArtist, ReleaseDate, ISRC, TrackNumber, DiscNumber) - FFmpeg: add -v error -hide_banner to all commands (embed, convert, CUE split) to eliminate banner, build config, and full metadata/lyrics dump in logcat - ebur128: add framelog=quiet to suppress per-frame loudness measurements while keeping the summary needed for ReplayGain parsing - Track metadata screen: separate embedded lyrics check from online fetch, show file-only state with manual online fetch button --- go_backend/extension_providers.go | 30 +++++ go_backend/tidal.go | 17 ++- lib/l10n/app_localizations.dart | 12 ++ lib/l10n/app_localizations_de.dart | 6 + lib/l10n/app_localizations_en.dart | 6 + lib/l10n/app_localizations_es.dart | 6 + lib/l10n/app_localizations_fr.dart | 6 + lib/l10n/app_localizations_hi.dart | 6 + lib/l10n/app_localizations_id.dart | 6 + lib/l10n/app_localizations_ja.dart | 6 + lib/l10n/app_localizations_ko.dart | 6 + lib/l10n/app_localizations_nl.dart | 6 + lib/l10n/app_localizations_pt.dart | 6 + lib/l10n/app_localizations_ru.dart | 6 + lib/l10n/app_localizations_tr.dart | 6 + lib/l10n/app_localizations_zh.dart | 6 + lib/l10n/arb/app_en.arb | 8 ++ lib/screens/track_metadata_screen.dart | 174 ++++++++++++++++++++++++- lib/services/ffmpeg_service.dart | 31 +++-- 19 files changed, 336 insertions(+), 14 deletions(-) diff --git a/go_backend/extension_providers.go b/go_backend/extension_providers.go index 04ed4085..c33015b7 100644 --- a/go_backend/extension_providers.go +++ b/go_backend/extension_providers.go @@ -1037,6 +1037,14 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro GoLog("[DownloadWithExtensionFallback] ReleaseDate from enrichment: %s\n", enrichedTrack.ReleaseDate) req.ReleaseDate = enrichedTrack.ReleaseDate } + if enrichedTrack.TrackNumber > 0 && req.TrackNumber == 0 { + GoLog("[DownloadWithExtensionFallback] TrackNumber from enrichment: %d\n", enrichedTrack.TrackNumber) + req.TrackNumber = enrichedTrack.TrackNumber + } + if enrichedTrack.DiscNumber > 0 && req.DiscNumber == 0 { + GoLog("[DownloadWithExtensionFallback] DiscNumber from enrichment: %d\n", enrichedTrack.DiscNumber) + req.DiscNumber = enrichedTrack.DiscNumber + } } } } @@ -1427,6 +1435,28 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro } } + if req.AlbumName != "" && resp.Album == "" { + resp.Album = req.AlbumName + } + if req.AlbumArtist != "" && resp.AlbumArtist == "" { + resp.AlbumArtist = req.AlbumArtist + } + if req.ReleaseDate != "" && resp.ReleaseDate == "" { + resp.ReleaseDate = req.ReleaseDate + } + if req.ISRC != "" && resp.ISRC == "" { + resp.ISRC = req.ISRC + } + if req.TrackNumber > 0 && resp.TrackNumber == 0 { + resp.TrackNumber = req.TrackNumber + } + if req.DiscNumber > 0 && resp.DiscNumber == 0 { + resp.DiscNumber = req.DiscNumber + } + if req.CoverURL != "" && resp.CoverURL == "" { + resp.CoverURL = req.CoverURL + } + return resp, nil } diff --git a/go_backend/tidal.go b/go_backend/tidal.go index 41e639b2..9cbc9c22 100644 --- a/go_backend/tidal.go +++ b/go_backend/tidal.go @@ -2162,13 +2162,26 @@ func resolveTidalTrackForRequest(req DownloadRequest, downloader *TidalDownloade GoLog("[%s] Track %d verified: '%s - %s' ✓\n", logPrefix, trackID, resolved.ArtistName, resolved.Title) } + // Use track_number / disc_number from the actual Tidal API data when the + // request doesn't carry them (e.g. downloads from search results / popular). + resolvedTrackNumber := req.TrackNumber + resolvedDiscNumber := req.DiscNumber + if actualTrack != nil { + if resolvedTrackNumber == 0 && actualTrack.TrackNumber > 0 { + resolvedTrackNumber = actualTrack.TrackNumber + } + if resolvedDiscNumber == 0 && actualTrack.VolumeNumber > 0 { + resolvedDiscNumber = actualTrack.VolumeNumber + } + } + track := &TidalTrack{ ID: trackID, Title: strings.TrimSpace(req.TrackName), ISRC: strings.TrimSpace(req.ISRC), Duration: expectedDurationSec, - TrackNumber: req.TrackNumber, - VolumeNumber: req.DiscNumber, + TrackNumber: resolvedTrackNumber, + VolumeNumber: resolvedDiscNumber, } track.Artist.Name = strings.TrimSpace(req.ArtistName) track.Album.Title = strings.TrimSpace(req.AlbumName) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index de4b5f10..4b0e665b 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -2248,6 +2248,18 @@ abstract class AppLocalizations { /// **'Lyrics not available for this track'** String get trackLyricsNotAvailable; + /// Message when no embedded lyrics in audio file + /// + /// In en, this message translates to: + /// **'No lyrics found in this file'** + String get trackLyricsNotInFile; + + /// Action - fetch lyrics from online providers + /// + /// In en, this message translates to: + /// **'Fetch from Online'** + String get trackFetchOnlineLyrics; + /// Message when lyrics request times out /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 5adcf1b9..2a2bf390 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1219,6 +1219,12 @@ class AppLocalizationsDe extends AppLocalizations { String get trackLyricsNotAvailable => 'Lyrics sind für diesen Titel nicht verfügbar'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Anfrage Timeout. Versuche es später erneut.'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 656e2304..0b589b64 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index de6e5ce9..96051fdd 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsEs extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index f61302b8..63884624 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -1202,6 +1202,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index 79202053..723e03a0 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsHi extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 01182d71..77cfef69 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -1207,6 +1207,12 @@ class AppLocalizationsId extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lirik tidak tersedia untuk lagu ini'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Permintaan timeout. Coba lagi nanti.'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 1f8bb5a8..091ada88 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -1194,6 +1194,12 @@ class AppLocalizationsJa extends AppLocalizations { @override String get trackLyricsNotAvailable => 'このトラックの歌詞は利用できません'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'リクエストがタイムアウトしました。後ほどお試しください。'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 36065943..3a94834e 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -1180,6 +1180,12 @@ class AppLocalizationsKo extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index a4c30bf7..df016151 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsNl extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 54f46b2b..7044bf0f 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsPt extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 14a6a7c5..937ffc90 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -1220,6 +1220,12 @@ class AppLocalizationsRu extends AppLocalizations { String get trackLyricsNotAvailable => 'Текст песни недоступен для этого трека'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Время ожидания запроса истекло. Повторите попытку позже.'; diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 39b9f84e..243312f9 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -1206,6 +1206,12 @@ class AppLocalizationsTr extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 7b3a1ce8..0028c509 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -1200,6 +1200,12 @@ class AppLocalizationsZh extends AppLocalizations { @override String get trackLyricsNotAvailable => 'Lyrics not available for this track'; + @override + String get trackLyricsNotInFile => 'No lyrics found in this file'; + + @override + String get trackFetchOnlineLyrics => 'Fetch from Online'; + @override String get trackLyricsTimeout => 'Request timed out. Try again later.'; diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index e067b198..d182a6e3 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1563,6 +1563,14 @@ "@trackLyricsNotAvailable": { "description": "Message when lyrics not found" }, + "trackLyricsNotInFile": "No lyrics found in this file", + "@trackLyricsNotInFile": { + "description": "Message when no embedded lyrics in audio file" + }, + "trackFetchOnlineLyrics": "Fetch from Online", + "@trackFetchOnlineLyrics": { + "description": "Action - fetch lyrics from online providers" + }, "trackLyricsTimeout": "Request timed out. Try again later.", "@trackLyricsTimeout": { "description": "Message when lyrics request times out" diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index d3d97132..506056ea 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -69,6 +69,7 @@ class _TrackMetadataScreenState extends ConsumerState { bool _lyricsEmbedded = false; bool _isEmbedding = false; bool _isInstrumental = false; + bool _embeddedLyricsChecked = false; bool _isConverting = false; bool _hasMetadataChanges = false; bool _hasLoadedResolvedAudioMetadata = false; @@ -241,7 +242,7 @@ class _TrackMetadataScreenState extends ConsumerState { } if (mounted && exists && _lyrics == null && !_lyricsLoading) { - _fetchLyrics(); + _checkEmbeddedLyrics(); } if (mounted && exists && @@ -1664,7 +1665,7 @@ class _TrackMetadataScreenState extends ConsumerState { ), ), TextButton( - onPressed: _fetchLyrics, + onPressed: _fetchOnlineLyrics, child: Text(context.l10n.dialogRetry), ), ], @@ -1732,6 +1733,46 @@ class _TrackMetadataScreenState extends ConsumerState { ], ], ) + else if (_embeddedLyricsChecked && _fileExists) + Column( + children: [ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest.withValues( + alpha: 0.5, + ), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + Icons.lyrics_outlined, + color: colorScheme.onSurfaceVariant, + size: 20, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + context.l10n.trackLyricsNotInFile, + style: TextStyle( + color: colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 12), + Center( + child: FilledButton.tonalIcon( + onPressed: _fetchOnlineLyrics, + icon: const Icon(Icons.cloud_download_outlined), + label: Text(context.l10n.trackFetchOnlineLyrics), + ), + ), + ], + ) else Center( child: FilledButton.tonalIcon( @@ -1746,6 +1787,134 @@ class _TrackMetadataScreenState extends ConsumerState { ); } + /// Check for lyrics embedded in the audio file only (no network requests). + /// Called automatically when the screen opens. + Future _checkEmbeddedLyrics() async { + if (_lyricsLoading || !_fileExists) return; + + setState(() { + _lyricsLoading = true; + _lyricsError = null; + _isInstrumental = false; + _lyricsSource = null; + }); + + try { + final embeddedResult = + await PlatformBridge.getLyricsLRCWithSource( + '', + trackName, + artistName, + filePath: cleanFilePath, + durationMs: 0, + ).timeout( + const Duration(seconds: 5), + onTimeout: () => {'lyrics': '', 'source': ''}, + ); + + final embeddedLyrics = embeddedResult['lyrics']?.toString() ?? ''; + final embeddedSource = embeddedResult['source']?.toString() ?? ''; + + if (mounted) { + if (embeddedLyrics.isNotEmpty) { + final cleanLyrics = _cleanLrcForDisplay(embeddedLyrics); + setState(() { + _lyrics = cleanLyrics; + _rawLyrics = embeddedLyrics; + _lyricsSource = embeddedSource.isNotEmpty + ? embeddedSource + : 'Embedded'; + _lyricsEmbedded = true; + _lyricsLoading = false; + _embeddedLyricsChecked = true; + }); + } else { + setState(() { + _lyricsLoading = false; + _embeddedLyricsChecked = true; + }); + } + } + } catch (e) { + if (mounted) { + setState(() { + _lyricsLoading = false; + _embeddedLyricsChecked = true; + }); + } + } + } + + /// Fetch lyrics from online providers. Only called by user action. + Future _fetchOnlineLyrics() async { + if (_lyricsLoading) return; + + setState(() { + _lyricsLoading = true; + _lyricsError = null; + _isInstrumental = false; + _lyricsSource = null; + }); + + try { + final durationMs = (duration ?? 0) * 1000; + + final result = await PlatformBridge.getLyricsLRCWithSource( + _spotifyId ?? '', + trackName, + artistName, + filePath: null, + durationMs: durationMs, + ).timeout(const Duration(seconds: 20)); + + final lrcText = result['lyrics']?.toString() ?? ''; + final source = result['source']?.toString() ?? ''; + final instrumental = + (result['instrumental'] as bool? ?? false) || + lrcText == '[instrumental:true]'; + + if (mounted) { + if (instrumental) { + setState(() { + _isInstrumental = true; + _lyricsSource = source.isNotEmpty ? source : null; + _lyricsLoading = false; + }); + } else if (lrcText.isEmpty) { + setState(() { + _lyricsError = context.l10n.trackLyricsNotAvailable; + _lyricsLoading = false; + }); + } else { + final cleanLyrics = _cleanLrcForDisplay(lrcText); + setState(() { + _lyrics = cleanLyrics; + _rawLyrics = lrcText; + _lyricsSource = source.isNotEmpty ? source : null; + _lyricsEmbedded = false; + _lyricsLoading = false; + }); + } + } + } on TimeoutException { + if (mounted) { + setState(() { + _lyricsError = context.l10n.trackLyricsTimeout; + _lyricsLoading = false; + }); + } + } catch (e) { + if (mounted) { + setState(() { + _lyricsError = context.l10n.trackLyricsLoadFailed; + _lyricsLoading = false; + }); + } + } + } + + /// Full lyrics fetch: check embedded first, then online. + /// Used by the "Load Lyrics" button when file doesn't exist (non-local items). Future _fetchLyrics() async { if (_lyricsLoading) return; @@ -1786,6 +1955,7 @@ class _TrackMetadataScreenState extends ConsumerState { : 'Embedded'; _lyricsEmbedded = true; _lyricsLoading = false; + _embeddedLyricsChecked = true; }); } return; diff --git a/lib/services/ffmpeg_service.dart b/lib/services/ffmpeg_service.dart index 134d3e08..1555f8fb 100644 --- a/lib/services/ffmpeg_service.dart +++ b/lib/services/ffmpeg_service.dart @@ -190,10 +190,10 @@ class FFmpegService { String command; if (format == 'opus') { command = - '-i "$inputPath" -codec:a libopus -b:a $bitrateValue -vbr on -compression_level 10 -map 0:a "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libopus -b:a $bitrateValue -vbr on -compression_level 10 -map 0:a "$outputPath" -y'; } else { command = - '-i "$inputPath" -codec:a libmp3lame -b:a $bitrateValue -map 0:a -id3v2_version 3 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libmp3lame -b:a $bitrateValue -map 0:a -id3v2_version 3 "$outputPath" -y'; } final result = await _execute(command); @@ -327,7 +327,7 @@ class FFmpegService { final outputPath = _buildOutputPath(inputPath, '.mp3'); final command = - '-i "$inputPath" -codec:a libmp3lame -b:a $bitrate -map 0:a -map_metadata 0 -id3v2_version 3 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libmp3lame -b:a $bitrate -map 0:a -map_metadata 0 -id3v2_version 3 "$outputPath" -y'; final result = await _execute(command); @@ -779,7 +779,7 @@ class FFmpegService { final outputPath = _buildOutputPath(inputPath, '.opus'); final command = - '-i "$inputPath" -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a -map_metadata 0 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a -map_metadata 0 "$outputPath" -y'; final result = await _execute(command); @@ -852,10 +852,10 @@ class FFmpegService { String command; if (codec == 'alac') { command = - '-i "$inputPath" -codec:a alac -map 0:a -map_metadata 0 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a alac -map 0:a -map_metadata 0 "$outputPath" -y'; } else { command = - '-i "$inputPath" -codec:a aac -b:a $bitrate -map 0:a -map_metadata 0 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a aac -b:a $bitrate -map 0:a -map_metadata 0 "$outputPath" -y'; } final result = await _execute(command); @@ -895,8 +895,10 @@ class FFmpegService { // Run FFmpeg with ebur128 filter + astats for true peak. // -nostats suppresses the interactive progress line. // ebur128=peak=true prints integrated loudness + true peak. + // framelog=quiet suppresses per-frame measurements (very verbose), + // keeping only the final summary which we parse. final command = - '-nostats -i "$filePath" -filter_complex ebur128=peak=true -f null -'; + '-hide_banner -nostats -i "$filePath" -filter_complex ebur128=peak=true:framelog=quiet -f null -'; _log.d( 'Scanning ReplayGain for: ${filePath.split(Platform.pathSeparator).last}', @@ -998,7 +1000,7 @@ class FFmpegService { // -map_metadata 0 preserves all existing metadata from the input. // -metadata flags add/overwrite only the specified keys. final command = - '-i "$filePath" -map 0 -c copy -map_metadata 0 ' + '-v error -hide_banner -i "$filePath" -map 0 -c copy -map_metadata 0 ' '-metadata REPLAYGAIN_ALBUM_GAIN="$sanitizedGain" ' '-metadata REPLAYGAIN_ALBUM_PEAK="$sanitizedPeak" ' '"$tempOutput" -y'; @@ -1048,6 +1050,7 @@ class FFmpegService { final tempOutput = _nextTempEmbedPath(tempDir.path, '.flac'); final StringBuffer cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$flacPath" '); if (coverPath != null) { @@ -1127,6 +1130,7 @@ class FFmpegService { final tempOutput = _nextTempEmbedPath(tempDir.path, '.mp3'); final StringBuffer cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$mp3Path" '); if (coverPath != null) { @@ -1213,6 +1217,9 @@ class FFmpegService { final tempOutput = _nextTempEmbedPath(tempDir.path, '.opus'); final mapMetaValue = preserveMetadata ? '0' : '-1'; final arguments = [ + '-v', + 'error', + '-hide_banner', '-i', opusPath, '-map', @@ -1305,6 +1312,7 @@ class FFmpegService { final tempOutput = _nextTempEmbedPath(tempDir.path, '.m4a'); final cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$m4aPath" '); final hasCover = coverPath != null && await File(coverPath).exists(); @@ -1528,10 +1536,10 @@ class FFmpegService { String command; if (format == 'opus') { command = - '-i "$inputPath" -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libopus -b:a $bitrate -vbr on -compression_level 10 -map 0:a "$outputPath" -y'; } else { command = - '-i "$inputPath" -codec:a libmp3lame -b:a $bitrate -map 0:a -id3v2_version 3 "$outputPath" -y'; + '-v error -hide_banner -i "$inputPath" -codec:a libmp3lame -b:a $bitrate -map 0:a -id3v2_version 3 "$outputPath" -y'; } _log.i( @@ -1605,6 +1613,7 @@ class FFmpegService { final outputPath = _buildOutputPath(inputPath, '.m4a'); final cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$inputPath" '); final hasCover = @@ -1667,6 +1676,7 @@ class FFmpegService { final outputPath = _buildOutputPath(inputPath, '.flac'); final cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$inputPath" '); final hasCover = @@ -2080,6 +2090,7 @@ class FFmpegService { final outputPath = '$outputDir${Platform.pathSeparator}$outputFileName'; final StringBuffer cmdBuffer = StringBuffer(); + cmdBuffer.write('-v error -hide_banner '); cmdBuffer.write('-i "$audioPath" '); final startTime = _formatSecondsForFFmpeg(track.startSec);