From 92d655fd1cf102d3b0296a1a3a868af12fafffb0 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:52:48 +0700 Subject: [PATCH] refactor(player): move restorePlaybackSession to playback_session_restore.dart The function only needed the file-level logger, which the new file declares with the same tag. It is re-exported from music_player_service.dart. --- lib/services/music_player_service.dart | 103 +------------------ lib/services/playback_session_restore.dart | 111 +++++++++++++++++++++ 2 files changed, 112 insertions(+), 102 deletions(-) create mode 100644 lib/services/playback_session_restore.dart diff --git a/lib/services/music_player_service.dart b/lib/services/music_player_service.dart index c30804b7..e18e53ab 100644 --- a/lib/services/music_player_service.dart +++ b/lib/services/music_player_service.dart @@ -14,7 +14,6 @@ import 'package:audio_session/audio_session.dart' AudioSessionConfiguration, AudioInterruptionType; import 'package:audioplayers/audioplayers.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:spotiflac_android/services/listening_statistics.dart' show ListeningTrack; import 'package:spotiflac_android/services/library_database.dart'; @@ -43,6 +42,7 @@ import 'package:spotiflac_android/services/playable_media.dart'; export 'music_player_runtime.dart'; export 'playable_media.dart'; export 'playback_metadata.dart'; +export 'playback_session_restore.dart'; part 'music_player_automix.dart'; part 'music_player_autoplay.dart'; @@ -2094,104 +2094,3 @@ Future initMusicPlayer() => musicPlayerRuntime.initialize(); /// exist are dropped; content URIs are kept and fail gracefully at play time. Future restorePersistedPlaybackSession() => musicPlayerRuntime.restoreSession(); - -Future restorePlaybackSession(MusicPlayerRuntime runtime) async { - try { - final database = runtime.dependencies.sessionDatabase; - final session = await database.getPlaybackSession(); - if (session == null) return; - - final rawMedia = session['media']; - if (rawMedia is! List) { - await database.clearPlaybackSession(); - return; - } - - final items = []; - final keptOriginalIndices = []; - final iosDocumentsPath = runtime.dependencies.isIOS - ? (await getApplicationDocumentsDirectory()).path - : null; - var artworkRelocated = false; - for (var i = 0; i < rawMedia.length; i++) { - final entry = rawMedia[i]; - if (entry is! Map) continue; - final media = PlayableMedia.fromJson( - Map.from(entry), - iosDocumentsPath: iosDocumentsPath, - ); - if (media == null) continue; - if (!media.isContentUri && - !media.isNetwork && - !await File(media.source).exists()) { - continue; - } - final artwork = await resolveRestoredArtworkUri( - media.artUri, - loadLibraryCoverPath: () async { - final database = runtime.dependencies.libraryDatabase; - final row = await database.getById(media.id); - if (row != null) return row['coverPath'] as String?; - final matches = await database.findByTrackAndArtist( - media.title, - media.artist, - ); - for (final row in matches) { - if (row['filePath'] == media.source) { - return row['coverPath'] as String?; - } - } - return null; - }, - ); - items.add( - artwork == media.artUri - ? media - : PlayableMedia.fromJson({...media.toJson(), 'artUri': artwork})!, - ); - artworkRelocated |= artwork != entry['artUri']; - keptOriginalIndices.add(i); - } - if (items.isEmpty) { - await database.clearPlaybackSession(); - return; - } - - // Remap the saved index onto the surviving list; if the current track - // itself was dropped, land on the nearest earlier survivor at 0:00. - final savedIndex = (session['index'] as num?)?.toInt() ?? 0; - var index = 0; - for (var i = 0; i < keptOriginalIndices.length; i++) { - if (keptOriginalIndices[i] <= savedIndex) index = i; - } - var position = Duration( - milliseconds: (session['positionMs'] as num?)?.toInt() ?? 0, - ); - if (keptOriginalIndices[index] != savedIndex) { - position = Duration.zero; - } - - final handler = await runtime.initialize(); - await handler.restoreSession( - items: items, - index: index, - position: position, - shuffle: session['shuffle'] == true, - originalOrder: [ - for (final i in keptOriginalIndices) - ((rawMedia[i] as Map)['queueOriginalIndex'] as num?)?.toInt() ?? i, - ], - queueNeedsRewrite: items.length != rawMedia.length || artworkRelocated, - repeatMode: AudioServiceRepeatMode.values.firstWhere( - (mode) => mode.name == session['repeat'], - orElse: () => AudioServiceRepeatMode.none, - ), - ); - _log.i( - 'Restored playback session: ${items.length} track(s), paused at ' - '${position.inSeconds}s', - ); - } catch (e) { - _log.w('Failed to restore playback session: $e'); - } -} diff --git a/lib/services/playback_session_restore.dart b/lib/services/playback_session_restore.dart new file mode 100644 index 00000000..ade7ccd9 --- /dev/null +++ b/lib/services/playback_session_restore.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:audio_service/audio_service.dart' show AudioServiceRepeatMode; +import 'package:path_provider/path_provider.dart'; +import 'package:spotiflac_android/services/music_player_runtime.dart'; +import 'package:spotiflac_android/services/playable_media.dart'; +import 'package:spotiflac_android/utils/logger.dart'; +import 'package:spotiflac_android/utils/playback_artwork.dart'; + +final _log = AppLogger('MusicPlayer'); + +Future restorePlaybackSession(MusicPlayerRuntime runtime) async { + try { + final database = runtime.dependencies.sessionDatabase; + final session = await database.getPlaybackSession(); + if (session == null) return; + + final rawMedia = session['media']; + if (rawMedia is! List) { + await database.clearPlaybackSession(); + return; + } + + final items = []; + final keptOriginalIndices = []; + final iosDocumentsPath = runtime.dependencies.isIOS + ? (await getApplicationDocumentsDirectory()).path + : null; + var artworkRelocated = false; + for (var i = 0; i < rawMedia.length; i++) { + final entry = rawMedia[i]; + if (entry is! Map) continue; + final media = PlayableMedia.fromJson( + Map.from(entry), + iosDocumentsPath: iosDocumentsPath, + ); + if (media == null) continue; + if (!media.isContentUri && + !media.isNetwork && + !await File(media.source).exists()) { + continue; + } + final artwork = await resolveRestoredArtworkUri( + media.artUri, + loadLibraryCoverPath: () async { + final database = runtime.dependencies.libraryDatabase; + final row = await database.getById(media.id); + if (row != null) return row['coverPath'] as String?; + final matches = await database.findByTrackAndArtist( + media.title, + media.artist, + ); + for (final row in matches) { + if (row['filePath'] == media.source) { + return row['coverPath'] as String?; + } + } + return null; + }, + ); + items.add( + artwork == media.artUri + ? media + : PlayableMedia.fromJson({...media.toJson(), 'artUri': artwork})!, + ); + artworkRelocated |= artwork != entry['artUri']; + keptOriginalIndices.add(i); + } + if (items.isEmpty) { + await database.clearPlaybackSession(); + return; + } + + // Remap the saved index onto the surviving list; if the current track + // itself was dropped, land on the nearest earlier survivor at 0:00. + final savedIndex = (session['index'] as num?)?.toInt() ?? 0; + var index = 0; + for (var i = 0; i < keptOriginalIndices.length; i++) { + if (keptOriginalIndices[i] <= savedIndex) index = i; + } + var position = Duration( + milliseconds: (session['positionMs'] as num?)?.toInt() ?? 0, + ); + if (keptOriginalIndices[index] != savedIndex) { + position = Duration.zero; + } + + final handler = await runtime.initialize(); + await handler.restoreSession( + items: items, + index: index, + position: position, + shuffle: session['shuffle'] == true, + originalOrder: [ + for (final i in keptOriginalIndices) + ((rawMedia[i] as Map)['queueOriginalIndex'] as num?)?.toInt() ?? i, + ], + queueNeedsRewrite: items.length != rawMedia.length || artworkRelocated, + repeatMode: AudioServiceRepeatMode.values.firstWhere( + (mode) => mode.name == session['repeat'], + orElse: () => AudioServiceRepeatMode.none, + ), + ); + _log.i( + 'Restored playback session: ${items.length} track(s), paused at ' + '${position.inSeconds}s', + ); + } catch (e) { + _log.w('Failed to restore playback session: $e'); + } +}