fix(download): align native metadata preparation and ReplayGain support

This commit is contained in:
zarzet committed 2026-09-28 12:21:15 +07:00
1 parent c0997de19c
commit e61e54f93f
11 files changed
+288 -117

No files matched your search

@@ -44,6 +44,7 @@ import 'package:spotiflac_android/utils/progress_stream_poller.dart';
import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/services/native_download_history.dart';
import 'package:spotiflac_android/services/download_track_metadata.dart';
export 'package:spotiflac_android/providers/download_history_provider.dart';
export 'package:spotiflac_android/providers/download_queue_state.dart';
@@ -22,6 +22,13 @@ class _DeezerExtendedMetadataFields {
}
extension _DownloadQueueEmbedding on DownloadQueueNotifier {
Future<Track> _prepareDownloadSourceTrack(Track track) async {
if (!track.id.startsWith('deezer:')) return track;
final id = track.id.substring('deezer:'.length);
final enriched = await enrichIncompleteDownloadTrack(track, 'deezer', id);
return identical(enriched, track) ? track : enriched.copyWith(deezerId: id);
}
Future<Track> _resolveDownloadAlbumCredit(
Track track,
AppSettings settings,
@@ -877,8 +877,9 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
return null;
}
final sourceTrack = await _prepareDownloadSourceTrack(item.track);
item = item.copyWith(
track: await _resolveDownloadAlbumCredit(item.track, settings),
track: await _resolveDownloadAlbumCredit(sourceTrack, settings),
);
final isSafMode = _isSafMode(settings);
@@ -915,12 +916,6 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
}
final outputExt = _determineOutputExt(quality, item.service);
if (settings.embedReplayGain &&
outputExt != '.flac' &&
outputExt != '.m4a') {
return null;
}
String? safFileName;
final safOutputExt = isSafMode ? outputExt : '';
final baseFilenameFormat = _shouldTreatAsSingleRelease(item.track)
@@ -992,7 +987,7 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
).withStrategy(useExtensions: true, useFallback: state.autoFallback);
return _NativeWorkerRequestContext(
item: item,
item: item.copyWith(track: trackForPayload),
requestJson: jsonEncode(payload.toJson()),
outputDir: outputDir,
quality: quality,
@@ -307,107 +307,8 @@ class _DownloadRun {
}
Future<bool> _enrichDeezerTrackIfNeeded() async {
final needsEnrichment =
trackToDownload.id.startsWith('deezer:') &&
(trackToDownload.isrc == null ||
trackToDownload.isrc!.isEmpty ||
trackToDownload.trackNumber == null ||
trackToDownload.trackNumber == 0 ||
trackToDownload.totalTracks == null ||
trackToDownload.totalTracks == 0 ||
(trackToDownload.composer == null ||
trackToDownload.composer!.isEmpty));
if (needsEnrichment) {
try {
_log.d(
'Enriching incomplete metadata for Deezer track: ${trackToDownload.name}',
);
_log.d(
'Current ISRC: ${trackToDownload.isrc}, TrackNumber: ${trackToDownload.trackNumber}',
);
final rawId = trackToDownload.id.split(':')[1];
_log.d('Fetching full metadata for Deezer ID: $rawId');
final fullData = await PlatformBridge.getProviderMetadata(
'deezer',
'track',
rawId,
);
_log.d('Got response keys: ${fullData.keys.toList()}');
if (fullData.containsKey('track')) {
final trackData = fullData['track'];
_log.d('Track data type: ${trackData.runtimeType}');
if (trackData is Map<String, dynamic>) {
final data = trackData;
_log.d('Track data keys: ${data.keys.toList()}');
_log.d('ISRC from API: ${data['isrc']}');
_log.d('album_type from API: ${data['album_type']}');
final enrichedTotalTracks = readPositiveInt(data['total_tracks']);
final enrichedTotalDiscs = readPositiveInt(data['total_discs']);
final enrichedComposer = normalizeOptionalString(
data['composer']?.toString(),
);
trackToDownload = trackToDownload.copyWith(
id: (data['spotify_id'] as String?) ?? trackToDownload.id,
name: (data['name'] as String?) ?? trackToDownload.name,
artistName:
(data['artists'] as String?) ?? trackToDownload.artistName,
albumName:
(data['album_name'] as String?) ?? trackToDownload.albumName,
albumArtist: data['album_artist'] as String?,
artistId:
(data['artist_id'] ?? data['artistId'])?.toString() ??
trackToDownload.artistId,
albumId: data['album_id']?.toString() ?? trackToDownload.albumId,
coverUrl: data['images'] as String?,
duration:
((data['duration_ms'] as int?) ??
(trackToDownload.duration * 1000)) ~/
1000,
isrc: (data['isrc'] as String?) ?? trackToDownload.isrc,
trackNumber: data['track_number'] as int?,
discNumber: data['disc_number'] as int?,
totalDiscs: enrichedTotalDiscs ?? trackToDownload.totalDiscs,
releaseDate: data['release_date'] as String?,
deezerId: rawId,
albumType:
(data['album_type'] as String?) ?? trackToDownload.albumType,
totalTracks: enrichedTotalTracks ?? trackToDownload.totalTracks,
composer: enrichedComposer ?? trackToDownload.composer,
genre: data['genre']?.toString() ?? trackToDownload.genre,
label: data['label']?.toString() ?? trackToDownload.label,
copyright:
data['copyright']?.toString() ?? trackToDownload.copyright,
comment: data['comment']?.toString() ?? trackToDownload.comment,
explicit:
parseExplicitFlag(data['explicit']) ??
trackToDownload.explicit,
upc:
(data['upc'] ?? data['barcode'])?.toString() ??
trackToDownload.upc,
);
_log.d(
'Metadata enriched: Track ${trackToDownload.trackNumber}, Disc ${trackToDownload.discNumber}, ISRC ${trackToDownload.isrc}, AlbumType ${trackToDownload.albumType}',
);
} else {
_log.w('Unexpected track data type: ${trackData.runtimeType}');
}
} else {
_log.w('Response does not contain track key');
}
} catch (e, stack) {
_log.w('Failed to enrich metadata: $e');
_log.w('Stack trace: $stack');
}
if (await _shouldAbort('during metadata enrichment')) {
return false;
}
}
_log.d('Track coverUrl after enrichment: ${trackToDownload.coverUrl}');
return true;
trackToDownload = await n._prepareDownloadSourceTrack(trackToDownload);
return !await _shouldAbort('during metadata enrichment');
}
Future<void> _resolveOutputTarget() async {