SpotiFLAC-Mobile/lib/services/download_request_payload.dart
zarzet cfc8e699f3 perf: optimize native worker snapshot writes with delta mode
Replace full-queue snapshot writes on every progress tick with a
lightweight delta mode that only includes the active item.

- Progress tick (1s loop) now writes item_delta with only the
  active item instead of serializing the entire queue
- Full compact_items snapshots are reserved for important events:
  start, pause/resume/cancel, item boundaries, finish, and
  service stop/destroy
- Compact items omit large static fields (item_json, track_name,
  artist_name) that Dart already has from queue restore
- Snapshot always carries item_ids for adoption correlation
- Dart-side _applyAndroidNativeWorkerSnapshot handles item_delta
  as a single-item fallback when items array is absent
- Dart-side _tryAdoptAndroidNativeWorkerSnapshot reads item_ids
  as fallback when items is not present
- Add deferred SAF publish: native worker writes to cache, runs
  all finalization locally, then publishes once to SAF at the end
- Forward defer_saf_publish through DownloadRequestPayload
2026-05-05 03:17:38 +07:00

204 lines
5.9 KiB
Dart

class DownloadRequestPayload {
static const int nativeWorkerContractVersion = 1;
final int contractVersion;
final String isrc;
final String service;
final String spotifyId;
final String trackName;
final String artistName;
final String albumName;
final String albumArtist;
final String coverUrl;
final String outputDir;
final String filenameFormat;
final String quality;
final bool embedMetadata;
final String artistTagMode;
final bool embedLyrics;
final bool embedMaxQualityCover;
final bool embedReplayGain;
final bool postProcessingEnabled;
final String tidalHighFormat;
final int trackNumber;
final int discNumber;
final int totalTracks;
final int totalDiscs;
final String releaseDate;
final String itemId;
final int durationMs;
final String source;
final String genre;
final String label;
final String copyright;
final String composer;
final String tidalId;
final String qobuzId;
final String deezerId;
final String lyricsMode;
final bool useExtensions;
final bool useFallback;
final String storageMode;
final String safTreeUri;
final String safRelativeDir;
final String safFileName;
final String safOutputExt;
final bool stageSafOutput;
final bool deferSafPublish;
final bool requiresContainerConversion;
final String songLinkRegion;
const DownloadRequestPayload({
this.contractVersion = nativeWorkerContractVersion,
this.isrc = '',
this.service = '',
this.spotifyId = '',
required this.trackName,
required this.artistName,
required this.albumName,
this.albumArtist = '',
this.coverUrl = '',
required this.outputDir,
required this.filenameFormat,
this.quality = 'LOSSLESS',
this.embedMetadata = true,
this.artistTagMode = 'joined',
this.embedLyrics = true,
this.embedMaxQualityCover = true,
this.embedReplayGain = false,
this.postProcessingEnabled = false,
this.tidalHighFormat = 'mp3_320',
this.trackNumber = 0,
this.discNumber = 0,
this.totalTracks = 1,
this.totalDiscs = 0,
this.releaseDate = '',
this.itemId = '',
this.durationMs = 0,
this.source = '',
this.genre = '',
this.label = '',
this.copyright = '',
this.composer = '',
this.tidalId = '',
this.qobuzId = '',
this.deezerId = '',
this.lyricsMode = 'embed',
this.useExtensions = false,
this.useFallback = false,
this.storageMode = 'app',
this.safTreeUri = '',
this.safRelativeDir = '',
this.safFileName = '',
this.safOutputExt = '',
this.stageSafOutput = false,
this.deferSafPublish = false,
this.requiresContainerConversion = false,
this.songLinkRegion = 'US',
});
Map<String, dynamic> toJson() {
return {
'contract_version': contractVersion,
'isrc': isrc,
'service': service,
'spotify_id': spotifyId,
'track_name': trackName,
'artist_name': artistName,
'album_name': albumName,
'album_artist': albumArtist,
'cover_url': coverUrl,
'output_dir': outputDir,
'filename_format': filenameFormat,
'quality': quality,
'embed_metadata': embedMetadata,
'artist_tag_mode': artistTagMode,
'embed_lyrics': embedLyrics,
'embed_max_quality_cover': embedMaxQualityCover,
'embed_replaygain': embedReplayGain,
'post_processing_enabled': postProcessingEnabled,
'tidal_high_format': tidalHighFormat,
'track_number': trackNumber,
'disc_number': discNumber,
'total_tracks': totalTracks,
'total_discs': totalDiscs,
'release_date': releaseDate,
'item_id': itemId,
'duration_ms': durationMs,
'source': source,
'genre': genre,
'label': label,
'copyright': copyright,
'composer': composer,
'tidal_id': tidalId,
'qobuz_id': qobuzId,
'deezer_id': deezerId,
'lyrics_mode': lyricsMode,
'use_extensions': useExtensions,
'use_fallback': useFallback,
'storage_mode': storageMode,
'saf_tree_uri': safTreeUri,
'saf_relative_dir': safRelativeDir,
'saf_file_name': safFileName,
'saf_output_ext': safOutputExt,
'stage_saf_output': stageSafOutput,
'defer_saf_publish': deferSafPublish,
'requires_container_conversion': requiresContainerConversion,
'songlink_region': songLinkRegion,
};
}
DownloadRequestPayload withStrategy({
bool? useExtensions,
bool? useFallback,
}) {
return DownloadRequestPayload(
contractVersion: contractVersion,
isrc: isrc,
service: service,
spotifyId: spotifyId,
trackName: trackName,
artistName: artistName,
albumName: albumName,
albumArtist: albumArtist,
coverUrl: coverUrl,
outputDir: outputDir,
filenameFormat: filenameFormat,
quality: quality,
embedMetadata: embedMetadata,
artistTagMode: artistTagMode,
embedLyrics: embedLyrics,
embedMaxQualityCover: embedMaxQualityCover,
embedReplayGain: embedReplayGain,
postProcessingEnabled: postProcessingEnabled,
tidalHighFormat: tidalHighFormat,
trackNumber: trackNumber,
discNumber: discNumber,
totalTracks: totalTracks,
totalDiscs: totalDiscs,
releaseDate: releaseDate,
itemId: itemId,
durationMs: durationMs,
source: source,
genre: genre,
label: label,
copyright: copyright,
composer: composer,
tidalId: tidalId,
qobuzId: qobuzId,
deezerId: deezerId,
lyricsMode: lyricsMode,
useExtensions: useExtensions ?? this.useExtensions,
useFallback: useFallback ?? this.useFallback,
storageMode: storageMode,
safTreeUri: safTreeUri,
safRelativeDir: safRelativeDir,
safFileName: safFileName,
safOutputExt: safOutputExt,
stageSafOutput: stageSafOutput,
deferSafPublish: deferSafPublish,
requiresContainerConversion: requiresContainerConversion,
songLinkRegion: songLinkRegion,
);
}
}