diff --git a/flutter_client/lib/playlists/playlists_provider.dart b/flutter_client/lib/playlists/playlists_provider.dart index 092bca37..52fa1c51 100644 --- a/flutter_client/lib/playlists/playlists_provider.dart +++ b/flutter_client/lib/playlists/playlists_provider.dart @@ -158,13 +158,29 @@ final playlistDetailProvider = debugPrint( 'playlistDetailProvider($id): got ${fresh.tracks.length} tracks, writing'); - // Collect the artist + album rows referenced by these tracks so - // the JOINs that build artistName/albumTitle in the row view - // have something to bind to. Without this, system-playlist tracks - // surface with empty artist/album columns. + // Collect the track + artist + album rows referenced by these + // playlist entries. Without writing the cachedTracks rows + // themselves, the detail-screen JOIN against cachedTracks + // returns null on every row (only the playlist_tracks join + // succeeds), so the UI shows a list with empty titles and + // unplayable tracks. + final tracks = {}; final artists = {}; final albums = {}; for (final t in fresh.tracks) { + final tId = t.trackId; + if (tId != null && tId.isNotEmpty) { + tracks.putIfAbsent( + tId, + () => _TrackRefRow( + id: tId, + title: t.title, + albumId: t.albumId ?? '', + artistId: t.artistId ?? '', + durationSec: t.durationSec, + ), + ); + } final aId = t.artistId; final aName = t.artistName; if (aId != null && aId.isNotEmpty && aName.isNotEmpty) { @@ -213,6 +229,25 @@ final playlistDetailProvider = .toList(), ); } + if (tracks.isNotEmpty) { + // Insert/update cachedTracks so the detail screen's JOIN + // produces real titles + durations. We don't have + // track_number / disc_number on the wire (PlaylistTrack + // omits them), so they default to 0 — UI doesn't surface + // them on this screen so it's fine. + b.insertAllOnConflictUpdate( + db.cachedTracks, + tracks.values + .map((t) => CachedTracksCompanion.insert( + id: t.id, + albumId: t.albumId, + artistId: t.artistId, + title: t.title, + durationMs: drift.Value(t.durationSec * 1000), + )) + .toList(), + ); + } if (albums.isNotEmpty) { b.insertAllOnConflictUpdate( db.cachedAlbums, @@ -339,6 +374,21 @@ class AlbumRefRow { final String artistId; } +class _TrackRefRow { + _TrackRefRow({ + required this.id, + required this.title, + required this.albumId, + required this.artistId, + required this.durationSec, + }); + final String id; + final String title; + final String albumId; + final String artistId; + final int durationSec; +} + final systemPlaylistsStatusProvider = FutureProvider((ref) async { final dio = await ref.watch(dioProvider.future);