mirror of
https://github.com/spotiflacapp/SpotiFLAC-Mobile.git
synced 2026-06-01 03:15:17 +07:00
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.
This commit is contained in:
parent
f84a33bbf2
commit
b4031936a0
34 changed files with 333 additions and 11 deletions
|
|
@ -5617,6 +5617,18 @@ abstract class AppLocalizations {
|
||||||
/// **'Samples'**
|
/// **'Samples'**
|
||||||
String get audioAnalysisSamples;
|
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
|
/// Extensions page - subtitle for built-in search provider option
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
|
||||||
|
|
@ -3322,6 +3322,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Proben';
|
String get audioAnalysisSamples => 'Proben';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3287,6 +3287,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3287,6 +3287,12 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
@ -7049,6 +7055,12 @@ class AppLocalizationsEsEs extends AppLocalizationsEs {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3291,6 +3291,12 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3288,6 +3288,12 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3297,6 +3297,12 @@ class AppLocalizationsId extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3275,6 +3275,12 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3268,6 +3268,12 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3288,6 +3288,12 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3287,6 +3287,12 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
@ -7042,6 +7048,12 @@ class AppLocalizationsPtPt extends AppLocalizationsPt {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3347,6 +3347,12 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3314,6 +3314,12 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -3343,6 +3343,12 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Семпли';
|
String get audioAnalysisSamples => 'Семпли';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Пошук за допомогою$providerName';
|
return 'Пошук за допомогою$providerName';
|
||||||
|
|
|
||||||
|
|
@ -3287,6 +3287,12 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
@ -7008,6 +7014,12 @@ class AppLocalizationsZhCn extends AppLocalizationsZh {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
@ -10485,6 +10497,12 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
|
||||||
@override
|
@override
|
||||||
String get audioAnalysisSamples => 'Samples';
|
String get audioAnalysisSamples => 'Samples';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescan => 'Re-analyze';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get audioAnalysisRescanning => 'Re-analyzing audio...';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String extensionsSearchWith(String providerName) {
|
String extensionsSearchWith(String providerName) {
|
||||||
return 'Search with $providerName';
|
return 'Search with $providerName';
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4279,6 +4279,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4204,6 +4204,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4027,6 +4027,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Single Dosya Adı Formatı",
|
||||||
"@downloadSingleFilenameFormat": {
|
"@downloadSingleFilenameFormat": {
|
||||||
"description": "Setting for output filename pattern for singles/EPs"
|
"description": "Setting for output filename pattern for singles/EPs"
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Пошук за допомогою{providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -4196,6 +4196,14 @@
|
||||||
"@audioAnalysisSamples": {
|
"@audioAnalysisSamples": {
|
||||||
"description": "Total samples metric label"
|
"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": "Search with {providerName}",
|
||||||
"@extensionsSearchWith": {
|
"@extensionsSearchWith": {
|
||||||
"description": "Extensions page - subtitle for built-in search provider option",
|
"description": "Extensions page - subtitle for built-in search provider option",
|
||||||
|
|
|
||||||
|
|
@ -165,15 +165,24 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _analyze() async {
|
Future<void> _analyze({bool forceRefresh = false}) async {
|
||||||
if (_analyzing) return;
|
if (_analyzing) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_analyzing = true;
|
_analyzing = true;
|
||||||
_error = null;
|
_error = null;
|
||||||
|
if (forceRefresh) {
|
||||||
|
_spectrogramImage?.dispose();
|
||||||
|
_spectrogramImage = null;
|
||||||
|
_data = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final cached = await _loadFromCache(widget.filePath);
|
if (forceRefresh) {
|
||||||
|
await _clearCache(widget.filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
final cached = forceRefresh ? null : await _loadFromCache(widget.filePath);
|
||||||
AudioAnalysisData data;
|
AudioAnalysisData data;
|
||||||
bool fromCache = false;
|
bool fromCache = false;
|
||||||
|
|
||||||
|
|
@ -214,6 +223,21 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> _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) {
|
static String _cacheKey(String filePath) {
|
||||||
var hash = 0xcbf29ce484222325;
|
var hash = 0xcbf29ce484222325;
|
||||||
for (final byte in utf8.encode(filePath)) {
|
for (final byte in utf8.encode(filePath)) {
|
||||||
|
|
@ -499,6 +523,7 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
|
||||||
if (_checkingCache) return const SizedBox.shrink();
|
if (_checkingCache) return const SizedBox.shrink();
|
||||||
|
|
||||||
if (_analyzing) {
|
if (_analyzing) {
|
||||||
|
final isRescan = _data != null || _spectrogramImage != null;
|
||||||
return Card(
|
return Card(
|
||||||
color: cs.surfaceContainerLow,
|
color: cs.surfaceContainerLow,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -514,7 +539,9 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
l10n.audioAnalysisAnalyzing,
|
isRescan
|
||||||
|
? l10n.audioAnalysisRescanning
|
||||||
|
: l10n.audioAnalysisAnalyzing,
|
||||||
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13),
|
style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -539,6 +566,18 @@ class _AudioAnalysisCardState extends State<AudioAnalysisCard> {
|
||||||
style: TextStyle(color: cs.onErrorContainer, fontSize: 13),
|
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<AudioAnalysisCard> {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_AudioInfoCard(data: data),
|
_AudioInfoCard(
|
||||||
|
data: data,
|
||||||
|
onRescan: () => _analyze(forceRefresh: true),
|
||||||
|
),
|
||||||
if (_spectrogramImage != null) ...[
|
if (_spectrogramImage != null) ...[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_SpectrogramView(
|
_SpectrogramView(
|
||||||
|
|
@ -796,8 +838,9 @@ Float64List _fft(Float64List realInput) {
|
||||||
|
|
||||||
class _AudioInfoCard extends StatelessWidget {
|
class _AudioInfoCard extends StatelessWidget {
|
||||||
final AudioAnalysisData data;
|
final AudioAnalysisData data;
|
||||||
|
final VoidCallback? onRescan;
|
||||||
|
|
||||||
const _AudioInfoCard({required this.data});
|
const _AudioInfoCard({required this.data, this.onRescan});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -815,14 +858,29 @@ class _AudioInfoCard extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.analytics_outlined, color: cs.primary, size: 20),
|
Icon(Icons.analytics_outlined, color: cs.primary, size: 20),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Expanded(
|
||||||
context.l10n.audioAnalysisTitle,
|
child: Text(
|
||||||
style: TextStyle(
|
context.l10n.audioAnalysisTitle,
|
||||||
color: cs.onSurface,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
color: cs.onSurface,
|
||||||
fontSize: 14,
|
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),
|
const SizedBox(height: 12),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue