mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-03 16:50:40 +02:00
fix(download): preserve finalization metadata and library labels
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:spotiflac_android/utils/audio_format_utils.dart';
|
||||
import 'package:spotiflac_android/utils/audio_quality_badge_policy.dart';
|
||||
import 'package:spotiflac_android/services/downloaded_embedded_cover_resolver.dart';
|
||||
import 'package:spotiflac_android/services/library_database.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
@@ -31,6 +33,37 @@ bool shouldRetainQueueLibraryPageSnapshot({
|
||||
required bool activeDownloadFallbackAvailable,
|
||||
}) => currentIsEmpty && cachedHasContent && activeDownloadFallbackAvailable;
|
||||
|
||||
/// Keeps a completed queue card visible until the matching Library row has
|
||||
/// actually landed. A completed item with an in-memory History row must not
|
||||
/// expire merely because the rest of its album batch was cancelled: the
|
||||
/// paged Library query can still be one refresh behind that persisted row.
|
||||
bool shouldRetainCompletionBridge({
|
||||
required bool isRequeued,
|
||||
required bool hasActiveDownloads,
|
||||
required bool libraryRowLanded,
|
||||
required bool hasHistoryItem,
|
||||
required bool expired,
|
||||
}) {
|
||||
if (isRequeued || libraryRowLanded) return false;
|
||||
if (hasActiveDownloads || hasHistoryItem) return true;
|
||||
return !expired;
|
||||
}
|
||||
|
||||
/// Builds a mode-aware label while a just-completed item is waiting for its
|
||||
/// History row. This avoids pinning the card to the track's old quality text
|
||||
/// when the user changes the Library label setting during an album download.
|
||||
String? buildCompletionBridgeFallbackQualityLabel({
|
||||
required String mode,
|
||||
required String? completedItemFilePath,
|
||||
required String? storedQuality,
|
||||
}) {
|
||||
return buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: audioFormatForPath(completedItemFilePath),
|
||||
storedQuality: storedQuality,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns distinct final-path candidates for a just-completed download.
|
||||
/// History is authoritative after conversion/SAF publication, while the
|
||||
/// completed queue item remains a safe fallback if the matched history row is
|
||||
|
||||
@@ -121,7 +121,11 @@ extension _QueueTabCollectionItemWidgets on _QueueTabState {
|
||||
: UnifiedLibraryItem.fromDownloadHistory(historyItem);
|
||||
final quality =
|
||||
unifiedItem?.qualityForMode(_libraryQualityLabelMode) ??
|
||||
track.audioQuality;
|
||||
buildCompletionBridgeFallbackQualityLabel(
|
||||
mode: _libraryQualityLabelMode,
|
||||
completedItemFilePath: item.filePath,
|
||||
storedQuality: track.audioQuality,
|
||||
);
|
||||
final playablePath = resolveCompletionBridgePlayablePath(
|
||||
historyFilePath: historyItem?.filePath,
|
||||
completedItemFilePath: item.filePath,
|
||||
@@ -258,7 +262,11 @@ extension _QueueTabCollectionItemWidgets on _QueueTabState {
|
||||
: UnifiedLibraryItem.fromDownloadHistory(historyItem);
|
||||
final quality =
|
||||
unifiedItem?.qualityForMode(_libraryQualityLabelMode) ??
|
||||
track.audioQuality;
|
||||
buildCompletionBridgeFallbackQualityLabel(
|
||||
mode: _libraryQualityLabelMode,
|
||||
completedItemFilePath: item.filePath,
|
||||
storedQuality: track.audioQuality,
|
||||
);
|
||||
final playablePath = resolveCompletionBridgePlayablePath(
|
||||
historyFilePath: historyItem?.filePath,
|
||||
completedItemFilePath: item.filePath,
|
||||
|
||||
@@ -81,23 +81,26 @@ extension _QueueTabFilterWidgets on _QueueTabState {
|
||||
final pending = <String>[];
|
||||
final hasActiveDownloads = activeDownloadIds.isNotEmpty;
|
||||
_completionBridge.forEach((id, _) {
|
||||
final historyId = bridgeHistoryById[id]?.id ?? id;
|
||||
final historyItem = bridgeHistoryById[id];
|
||||
final historyId = historyItem?.id ?? id;
|
||||
final landed = libIdSet.contains('dl_$historyId');
|
||||
final addedAt = _completionBridgeAt[id];
|
||||
final expired =
|
||||
addedAt == null || now.difference(addedAt).inSeconds >= 6;
|
||||
if (activeDownloadIds.contains(id)) {
|
||||
// Re-queued (retry): the live row takes over from the bridge.
|
||||
stale.add(id);
|
||||
} else if (hasActiveDownloads) {
|
||||
// Keep just-completed tracks pinned in the lead zone while the
|
||||
// rest of the batch is still downloading, so they don't jump
|
||||
// below the remaining queue the moment they finish.
|
||||
if (shouldRetainCompletionBridge(
|
||||
isRequeued: activeDownloadIds.contains(id),
|
||||
hasActiveDownloads: hasActiveDownloads,
|
||||
libraryRowLanded: landed,
|
||||
hasHistoryItem: historyItem != null,
|
||||
expired: expired,
|
||||
)) {
|
||||
// Keep completed tracks pinned while their batch is active or their
|
||||
// persisted History row is still waiting for the paged Library.
|
||||
pending.add(id);
|
||||
} else if (landed || expired) {
|
||||
stale.add(id);
|
||||
} else {
|
||||
pending.add(id);
|
||||
// Re-queued items are represented by the live row; landed items by
|
||||
// the normal Library row; unpersisted bridges retain a short grace.
|
||||
stale.add(id);
|
||||
}
|
||||
});
|
||||
bridgeIds = pending;
|
||||
|
||||
Reference in New Issue
Block a user