diff --git a/flutter_client/lib/playlists/playlist_detail_screen.dart b/flutter_client/lib/playlists/playlist_detail_screen.dart index 50c4d12b..957abd9d 100644 --- a/flutter_client/lib/playlists/playlist_detail_screen.dart +++ b/flutter_client/lib/playlists/playlist_detail_screen.dart @@ -263,24 +263,32 @@ class _Header extends ConsumerWidget { ), const Spacer(), if (playable.isNotEmpty) ...[ - OutlinedButton.icon( - key: const Key('download_playlist_button'), - onPressed: () { - final mgr = ref.read(audioCacheManagerProvider); - for (final t in playable) { - final id = t.trackId ?? ''; - if (id.isEmpty) continue; - // Fire-and-forget; downloads happen in background. - // ignore: unawaited_futures - mgr.pin(id, source: CacheSource.autoPlaylist); - } - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Downloading ${playable.length} tracks…')), - ); - }, - icon: const Icon(Icons.download, size: 16), - label: const Text('Download'), - ), + if (p.isSystem) + OutlinedButton.icon( + key: const Key('regenerate_playlist_button'), + onPressed: () => _regenerate(context, ref), + icon: const Icon(Icons.refresh, size: 16), + label: const Text('Regenerate'), + ) + else + OutlinedButton.icon( + key: const Key('download_playlist_button'), + onPressed: () { + final mgr = ref.read(audioCacheManagerProvider); + for (final t in playable) { + final id = t.trackId ?? ''; + if (id.isEmpty) continue; + // Fire-and-forget; downloads happen in background. + // ignore: unawaited_futures + mgr.pin(id, source: CacheSource.autoPlaylist); + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Downloading ${playable.length} tracks…')), + ); + }, + icon: const Icon(Icons.download, size: 16), + label: const Text('Download'), + ), const SizedBox(width: 8), FilledButton.icon( onPressed: () { @@ -299,6 +307,37 @@ class _Header extends ConsumerWidget { ]), ); } + + /// Forces a server-side rebuild of this system playlist. The + /// rebuild rotates the playlist UUID, so the old detail route now + /// 404s — rebind by pushReplacement-ing to the new id. The + /// aggregate list is invalidated too so the home row's tile points + /// at the fresh UUID. ScaffoldMessenger + router captured before + /// the await so we don't touch a stale BuildContext after. + Future _regenerate(BuildContext context, WidgetRef ref) async { + final messenger = ScaffoldMessenger.of(context); + final router = GoRouter.of(context); + final p = detail.playlist; + try { + final api = await ref.read(playlistsApiProvider.future); + final newId = await api.refreshSystem(p.systemVariant!); + ref.invalidate(playlistsListProvider); + if (newId == null) { + messenger.showSnackBar(const SnackBar( + content: Text('Nothing to build yet — library is empty.'), + )); + return; + } + messenger.showSnackBar( + SnackBar(content: Text('${p.name} regenerated')), + ); + router.pushReplacement('/playlists/$newId'); + } catch (e) { + messenger.showSnackBar( + SnackBar(content: Text("Couldn't regenerate: $e")), + ); + } + } } class _PlaylistTrackRow extends ConsumerWidget {