From b4031936a05e0efbf8b6e7ad0c0d6edb8ee2d1bd Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 10 May 2026 21:27:54 +0700 Subject: [PATCH] feat: allow re-running audio quality analysis after cached result The audio analysis card used to read from a persistent cache but offered no way to refresh the result when the underlying file had been re-downloaded at a different quality (for example, re-downloading a track as FLAC after capturing it as AAC). Add an explicit rescan control that clears the cached JSON + spectrogram, reruns the FFmpeg probe and analysis pipeline, and swaps in the fresh data while keeping the loading copy distinct from first-run analysis. A retry button is also exposed in the error card so transient failures do not require navigating away. All audio_analysis strings now have a Re-analyze / Re-analyzing pair in the ARB catalog so every locale can translate them independently. --- 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 | 12 ++++ 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 | 12 ++++ lib/l10n/app_localizations_ru.dart | 6 ++ lib/l10n/app_localizations_tr.dart | 6 ++ lib/l10n/app_localizations_uk.dart | 6 ++ lib/l10n/app_localizations_zh.dart | 18 ++++++ lib/l10n/arb/app_de.arb | 8 +++ lib/l10n/arb/app_en.arb | 8 +++ lib/l10n/arb/app_es.arb | 8 +++ lib/l10n/arb/app_es_ES.arb | 8 +++ lib/l10n/arb/app_fr.arb | 8 +++ lib/l10n/arb/app_hi.arb | 8 +++ lib/l10n/arb/app_id.arb | 8 +++ lib/l10n/arb/app_ja.arb | 8 +++ lib/l10n/arb/app_ko.arb | 8 +++ lib/l10n/arb/app_nl.arb | 8 +++ lib/l10n/arb/app_pt.arb | 8 +++ lib/l10n/arb/app_pt_PT.arb | 8 +++ lib/l10n/arb/app_ru.arb | 8 +++ lib/l10n/arb/app_tr.arb | 8 +++ lib/l10n/arb/app_uk.arb | 8 +++ lib/l10n/arb/app_zh.arb | 8 +++ lib/l10n/arb/app_zh_CN.arb | 8 +++ lib/l10n/arb/app_zh_TW.arb | 8 +++ lib/widgets/audio_analysis_widget.dart | 80 ++++++++++++++++++++++---- 34 files changed, 333 insertions(+), 11 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 968d6838..2e31f9a1 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -5617,6 +5617,18 @@ abstract class AppLocalizations { /// **'Samples'** String get audioAnalysisSamples; + /// Tooltip/label for the button that re-runs the audio analysis, discarding cached results + /// + /// In en, this message translates to: + /// **'Re-analyze'** + String get audioAnalysisRescan; + + /// Loading text while audio is being re-analyzed after an explicit refresh + /// + /// In en, this message translates to: + /// **'Re-analyzing audio...'** + String get audioAnalysisRescanning; + /// Extensions page - subtitle for built-in search provider option /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 66b85c1e..cfb6c70b 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -3322,6 +3322,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get audioAnalysisSamples => 'Proben'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 9504bb4f..851ce51a 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -3287,6 +3287,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 806275ae..4a02e08c 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -3287,6 +3287,12 @@ class AppLocalizationsEs extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; @@ -7049,6 +7055,12 @@ class AppLocalizationsEsEs extends AppLocalizationsEs { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 784eecb6..013e8fd1 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -3291,6 +3291,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index 598e1be9..aa6e08c0 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -3288,6 +3288,12 @@ class AppLocalizationsHi extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index ad58c390..cd6ef465 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -3297,6 +3297,12 @@ class AppLocalizationsId extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index ffbabbae..4debefd4 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -3275,6 +3275,12 @@ class AppLocalizationsJa extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 61095077..9c42dc99 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -3268,6 +3268,12 @@ class AppLocalizationsKo extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index ab35af0e..e78d529d 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -3288,6 +3288,12 @@ class AppLocalizationsNl extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 6cc6ae1a..bbddce8a 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -3287,6 +3287,12 @@ class AppLocalizationsPt extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; @@ -7042,6 +7048,12 @@ class AppLocalizationsPtPt extends AppLocalizationsPt { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 47f67f6d..9272dec0 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -3347,6 +3347,12 @@ class AppLocalizationsRu extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 12a84e85..fe4c366f 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -3314,6 +3314,12 @@ class AppLocalizationsTr extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 495c602f..0b0a4d38 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -3343,6 +3343,12 @@ class AppLocalizationsUk extends AppLocalizations { @override String get audioAnalysisSamples => 'Семпли'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Пошук за допомогою$providerName'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 869c059c..83c404a4 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -3287,6 +3287,12 @@ class AppLocalizationsZh extends AppLocalizations { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; @@ -7008,6 +7014,12 @@ class AppLocalizationsZhCn extends AppLocalizationsZh { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; @@ -10485,6 +10497,12 @@ class AppLocalizationsZhTw extends AppLocalizationsZh { @override String get audioAnalysisSamples => 'Samples'; + @override + String get audioAnalysisRescan => 'Re-analyze'; + + @override + String get audioAnalysisRescanning => 'Re-analyzing audio...'; + @override String extensionsSearchWith(String providerName) { return 'Search with $providerName'; diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index de2b86c8..530a1ee4 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index ac2fab15..334e52ef 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -4279,6 +4279,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_es.arb b/lib/l10n/arb/app_es.arb index e6b5d3f6..91fe89a7 100644 --- a/lib/l10n/arb/app_es.arb +++ b/lib/l10n/arb/app_es.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_es_ES.arb b/lib/l10n/arb/app_es_ES.arb index 2d40f4df..5733e0c5 100644 --- a/lib/l10n/arb/app_es_ES.arb +++ b/lib/l10n/arb/app_es_ES.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_fr.arb b/lib/l10n/arb/app_fr.arb index 5626f34c..5568f8b0 100644 --- a/lib/l10n/arb/app_fr.arb +++ b/lib/l10n/arb/app_fr.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_hi.arb b/lib/l10n/arb/app_hi.arb index 5f05054a..dd2bf633 100644 --- a/lib/l10n/arb/app_hi.arb +++ b/lib/l10n/arb/app_hi.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_id.arb b/lib/l10n/arb/app_id.arb index 58fc1eb6..ee3c6324 100644 --- a/lib/l10n/arb/app_id.arb +++ b/lib/l10n/arb/app_id.arb @@ -4204,6 +4204,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_ja.arb b/lib/l10n/arb/app_ja.arb index 4ab8416e..38c65733 100644 --- a/lib/l10n/arb/app_ja.arb +++ b/lib/l10n/arb/app_ja.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_ko.arb b/lib/l10n/arb/app_ko.arb index 695bb684..59c3760b 100644 --- a/lib/l10n/arb/app_ko.arb +++ b/lib/l10n/arb/app_ko.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_nl.arb b/lib/l10n/arb/app_nl.arb index d53257a0..87cc7c7d 100644 --- a/lib/l10n/arb/app_nl.arb +++ b/lib/l10n/arb/app_nl.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_pt.arb b/lib/l10n/arb/app_pt.arb index 5c109f83..fb6038e8 100644 --- a/lib/l10n/arb/app_pt.arb +++ b/lib/l10n/arb/app_pt.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_pt_PT.arb b/lib/l10n/arb/app_pt_PT.arb index f9744986..267eaede 100644 --- a/lib/l10n/arb/app_pt_PT.arb +++ b/lib/l10n/arb/app_pt_PT.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_ru.arb b/lib/l10n/arb/app_ru.arb index abcdfb6a..2e114e03 100644 --- a/lib/l10n/arb/app_ru.arb +++ b/lib/l10n/arb/app_ru.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_tr.arb b/lib/l10n/arb/app_tr.arb index 6c8e0a68..c4cbe8cb 100644 --- a/lib/l10n/arb/app_tr.arb +++ b/lib/l10n/arb/app_tr.arb @@ -4027,6 +4027,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "downloadSingleFilenameFormat": "Single Dosya Adı Formatı", "@downloadSingleFilenameFormat": { "description": "Setting for output filename pattern for singles/EPs" diff --git a/lib/l10n/arb/app_uk.arb b/lib/l10n/arb/app_uk.arb index d585d905..39bed2c3 100644 --- a/lib/l10n/arb/app_uk.arb +++ b/lib/l10n/arb/app_uk.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Пошук за допомогою{providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_zh.arb b/lib/l10n/arb/app_zh.arb index c5deab20..f8ab208d 100644 --- a/lib/l10n/arb/app_zh.arb +++ b/lib/l10n/arb/app_zh.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_zh_CN.arb b/lib/l10n/arb/app_zh_CN.arb index 9c825991..214e4d62 100644 --- a/lib/l10n/arb/app_zh_CN.arb +++ b/lib/l10n/arb/app_zh_CN.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/l10n/arb/app_zh_TW.arb b/lib/l10n/arb/app_zh_TW.arb index 3cce0c94..e88dccdd 100644 --- a/lib/l10n/arb/app_zh_TW.arb +++ b/lib/l10n/arb/app_zh_TW.arb @@ -4196,6 +4196,14 @@ "@audioAnalysisSamples": { "description": "Total samples metric label" }, + "audioAnalysisRescan": "Re-analyze", + "@audioAnalysisRescan": { + "description": "Tooltip/label for the button that re-runs the audio analysis, discarding cached results" + }, + "audioAnalysisRescanning": "Re-analyzing audio...", + "@audioAnalysisRescanning": { + "description": "Loading text while audio is being re-analyzed after an explicit refresh" + }, "extensionsSearchWith": "Search with {providerName}", "@extensionsSearchWith": { "description": "Extensions page - subtitle for built-in search provider option", diff --git a/lib/widgets/audio_analysis_widget.dart b/lib/widgets/audio_analysis_widget.dart index d88c55b0..0c929101 100644 --- a/lib/widgets/audio_analysis_widget.dart +++ b/lib/widgets/audio_analysis_widget.dart @@ -165,15 +165,24 @@ class _AudioAnalysisCardState extends State { } } - Future _analyze() async { + Future _analyze({bool forceRefresh = false}) async { if (_analyzing) return; setState(() { _analyzing = true; _error = null; + if (forceRefresh) { + _spectrogramImage?.dispose(); + _spectrogramImage = null; + _data = null; + } }); try { - final cached = await _loadFromCache(widget.filePath); + if (forceRefresh) { + await _clearCache(widget.filePath); + } + + final cached = forceRefresh ? null : await _loadFromCache(widget.filePath); AudioAnalysisData data; bool fromCache = false; @@ -214,6 +223,21 @@ class _AudioAnalysisCardState extends State { } } + static Future _clearCache(String filePath) async { + try { + final dir = await _cacheDir(); + final key = _cacheKey(filePath); + final jsonFile = File('${dir.path}/$key.json'); + final imageFile = File('${dir.path}/$key.png'); + if (await jsonFile.exists()) { + await jsonFile.delete(); + } + if (await imageFile.exists()) { + await imageFile.delete(); + } + } catch (_) {} + } + static String _cacheKey(String filePath) { var hash = 0xcbf29ce484222325; for (final byte in utf8.encode(filePath)) { @@ -499,6 +523,7 @@ class _AudioAnalysisCardState extends State { if (_checkingCache) return const SizedBox.shrink(); if (_analyzing) { + final isRescan = _data != null || _spectrogramImage != null; return Card( color: cs.surfaceContainerLow, child: Padding( @@ -514,7 +539,9 @@ class _AudioAnalysisCardState extends State { ), const SizedBox(height: 12), Text( - l10n.audioAnalysisAnalyzing, + isRescan + ? l10n.audioAnalysisRescanning + : l10n.audioAnalysisAnalyzing, style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13), ), ], @@ -539,6 +566,18 @@ class _AudioAnalysisCardState extends State { style: TextStyle(color: cs.onErrorContainer, fontSize: 13), ), ), + IconButton( + icon: const Icon(Icons.refresh, size: 20), + tooltip: l10n.audioAnalysisRescan, + visualDensity: VisualDensity.compact, + padding: EdgeInsets.zero, + constraints: const BoxConstraints( + minWidth: 32, + minHeight: 32, + ), + color: cs.onErrorContainer, + onPressed: () => _analyze(forceRefresh: true), + ), ], ), ), @@ -592,7 +631,10 @@ class _AudioAnalysisCardState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _AudioInfoCard(data: data), + _AudioInfoCard( + data: data, + onRescan: () => _analyze(forceRefresh: true), + ), if (_spectrogramImage != null) ...[ const SizedBox(height: 12), _SpectrogramView( @@ -796,8 +838,9 @@ Float64List _fft(Float64List realInput) { class _AudioInfoCard extends StatelessWidget { final AudioAnalysisData data; + final VoidCallback? onRescan; - const _AudioInfoCard({required this.data}); + const _AudioInfoCard({required this.data, this.onRescan}); @override Widget build(BuildContext context) { @@ -815,14 +858,29 @@ class _AudioInfoCard extends StatelessWidget { children: [ Icon(Icons.analytics_outlined, color: cs.primary, size: 20), const SizedBox(width: 8), - Text( - context.l10n.audioAnalysisTitle, - style: TextStyle( - color: cs.onSurface, - fontWeight: FontWeight.w600, - fontSize: 14, + Expanded( + child: Text( + context.l10n.audioAnalysisTitle, + style: TextStyle( + color: cs.onSurface, + fontWeight: FontWeight.w600, + fontSize: 14, + ), ), ), + if (onRescan != null) + IconButton( + icon: const Icon(Icons.refresh, size: 20), + tooltip: context.l10n.audioAnalysisRescan, + visualDensity: VisualDensity.compact, + padding: EdgeInsets.zero, + constraints: const BoxConstraints( + minWidth: 32, + minHeight: 32, + ), + color: cs.onSurfaceVariant, + onPressed: onRescan, + ), ], ), const SizedBox(height: 12),