fix(library): index native completions and refresh background downloads

This commit is contained in:
zarzet
2026-09-28 12:13:17 +07:00
parent e3bec31b36
commit b15bb2673c
6 changed files with 132 additions and 35 deletions
+7
View File
@@ -394,6 +394,13 @@ class _EagerInitializationState extends ConsumerState<EagerInitialization>
if (state == AppLifecycleState.resumed) {
unawaited(_consumeVerificationNotification());
CoverCacheManager.scheduleMaintenance();
// Native downloads can finish while Flutter is suspended. Refresh the
// persisted history even when no queued item remains to reconcile.
if (ref.exists(downloadHistoryProvider)) {
unawaited(
ref.read(downloadHistoryProvider.notifier).reloadFromStorage(),
);
}
_maybeAutoScanLocalLibrary();
if (ref.exists(localLibraryProvider)) {
unawaited(
+6 -4
View File
@@ -82,6 +82,7 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
bool _isAudioMetadataBackfillInProgress = false;
bool _startupMaintenanceScheduled = false;
Future<void> _historyWriteChain = Future<void>.value();
Future<void>? _reloadFuture;
Timer? _indexBumpTimer;
DateTime? _lastIndexBumpAt;
static const _indexBumpWindow = Duration(seconds: 1);
@@ -97,7 +98,7 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
if (_isLoaded) return;
_isLoaded = true;
Future.microtask(() async {
await _loadFromDatabase();
await reloadFromStorage();
});
}
@@ -137,9 +138,10 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
}
}
Future<void> reloadFromStorage() async {
await _loadFromDatabase();
}
Future<void> reloadFromStorage() => _reloadFuture ??=
_enqueueHistoryWrite(_loadFromDatabase).whenComplete(() {
_reloadFuture = null;
});
void _bumpHistoryRevision() {
state = state.copyWith(loadedIndexVersion: state.loadedIndexVersion + 1);
@@ -43,6 +43,7 @@ import 'package:spotiflac_android/utils/lyrics_metadata_helper.dart';
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';
export 'package:spotiflac_android/providers/download_history_provider.dart';
export 'package:spotiflac_android/providers/download_queue_state.dart';
@@ -1261,40 +1261,20 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
await persistBeforePublishingDownloadCompletion(
persist: () async {
if (!settings.saveDownloadHistory) return;
final historyItem = result['history_item'];
if (historyItem is Map) {
try {
await ref
.read(downloadHistoryProvider.notifier)
.adoptNativeHistoryItem(
DownloadHistoryItem.fromJson(
Map<String, dynamic>.from(historyItem),
),
preserveTrackVariant: item.preserveQualityVariant,
);
} catch (e) {
_log.w('Failed to adopt native history item: $e');
await _persistNativeFinalizedHistoryFallback(
context,
result,
nativeFinalizedFilePath,
);
}
} else if (result['history_written'] == true ||
result['already_exists'] == true) {
await ref
.read(downloadHistoryProvider.notifier)
.reloadFromStorage();
} else {
_log.w(
'Native finalizer completed without history; persisting Dart fallback',
);
await _persistNativeFinalizedHistoryFallback(
final history = ref.read(downloadHistoryProvider.notifier);
await reconcileNativeDownloadHistory(
result: result,
adopt: (json) => history.adoptNativeHistoryItem(
DownloadHistoryItem.fromJson(json),
preserveTrackVariant: item.preserveQualityVariant,
),
reload: history.reloadFromStorage,
persistFallback: () => _persistNativeFinalizedHistoryFallback(
context,
result,
nativeFinalizedFilePath,
);
}
),
);
},
publish: () {
_completedInSession++;
+26
View File
@@ -0,0 +1,26 @@
import 'package:spotiflac_android/utils/logger.dart';
/// A completed file and a persisted Library entry are separate guarantees.
/// In particular, `already_exists` does not mean history was ever saved.
Future<void> reconcileNativeDownloadHistory({
required Map<String, dynamic> result,
required Future<void> Function(Map<String, dynamic>) adopt,
required Future<void> Function() reload,
required Future<void> Function() persistFallback,
}) async {
final historyItem = result['history_item'];
if (historyItem is Map) {
try {
await adopt(Map<String, dynamic>.from(historyItem));
return;
} catch (error) {
AppLogger('DownloadHistory').w(
'Failed to adopt native history item, retrying from download result: $error',
);
}
} else if (result['history_written'] == true) {
await reload();
return;
}
await persistFallback();
}