fix: prefer local file for cover/lyrics save and update build dependencies

- Cover art: extract from downloaded file first, fall back to URL download
- Lyrics: check embedded lyrics/sidecar LRC before fetching online
- Add audioFilePath param to FetchAndSaveLyrics (Go, Kotlin, Swift, Dart)
- Handle SAF content:// URIs for lyrics extraction in Kotlin bridge
- Update Go 1.25.7 -> 1.25.8, Gradle 9.3.1 -> 9.4.1, Kotlin 2.2.21 -> 2.3.20
- Update NDK r27d -> r28b, Flutter FVM 3.41.4 -> 3.41.5
- Upgrade all Flutter and Go module dependencies to latest
This commit is contained in:
zarzet
2026-03-31 17:25:30 +07:00
parent 93e77aeb84
commit 7dba938299
12 changed files with 206 additions and 139 deletions
+38 -12
View File
@@ -1956,17 +1956,29 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
'${tempDir.path}${Platform.pathSeparator}$baseName.jpg';
Map<String, dynamic> result;
if (_coverUrl != null && _coverUrl!.isNotEmpty) {
if (_fileExists) {
// Prefer extracting cover from the already-downloaded file to avoid
// a redundant network request.
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
tempOutput,
);
// Fall back to downloading from URL if extraction failed.
if (result['error'] != null &&
_coverUrl != null &&
_coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
maxQuality: true,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
maxQuality: true,
);
} else if (_fileExists) {
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
tempOutput,
);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -2042,17 +2054,29 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
final outputPath = '$dir${Platform.pathSeparator}$baseName.jpg';
Map<String, dynamic> result;
if (_coverUrl != null && _coverUrl!.isNotEmpty) {
if (_fileExists) {
// Prefer extracting cover from the already-downloaded file to avoid
// a redundant network request.
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
outputPath,
);
// Fall back to downloading from URL if extraction failed.
if (result['error'] != null &&
_coverUrl != null &&
_coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
maxQuality: true,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
maxQuality: true,
);
} else if (_fileExists) {
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
outputPath,
);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -2110,6 +2134,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
spotifyId: _spotifyId ?? '',
durationMs: durationMs,
outputPath: tempOutput,
audioFilePath: _fileExists ? cleanFilePath : '',
);
if (result['error'] != null) {
@@ -2194,6 +2219,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
spotifyId: _spotifyId ?? '',
durationMs: durationMs,
outputPath: outputPath,
audioFilePath: _fileExists ? cleanFilePath : '',
);
if (mounted) {
+2
View File
@@ -341,6 +341,7 @@ class PlatformBridge {
required String spotifyId,
required int durationMs,
required String outputPath,
String audioFilePath = '',
}) async {
final result = await _channel.invokeMethod('fetchAndSaveLyrics', {
'track_name': trackName,
@@ -348,6 +349,7 @@ class PlatformBridge {
'spotify_id': spotifyId,
'duration_ms': durationMs,
'output_path': outputPath,
'audio_file_path': audioFilePath,
});
return jsonDecode(result as String) as Map<String, dynamic>;
}