From 4a65c4cec741e63ecec0ca8538abbb4618289e42 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 21:50:50 -0500 Subject: [PATCH] Tell the screens when the playlist they are showing is deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devices.playlist_id is ON DELETE SET NULL, so the database detached correctly — but the handler emitted nothing, so a screen kept displaying the deleted playlist until it happened to reconnect or was restarted. You delete a playlist to take content off the wall; the wall carried on showing it. Every sibling mutation in this file already pushes (publish, assign), and DELETE /devices/:id/playlist was given a push for precisely this reason: "so the screen stops, rather than leaving the old content up until something else happens to update it". The affected devices are read before the delete, since the association is gone the moment it runs. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- server/routes/playlists.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/routes/playlists.js b/server/routes/playlists.js index c418987..3419581 100644 --- a/server/routes/playlists.js +++ b/server/routes/playlists.js @@ -339,7 +339,28 @@ router.post('/:id/discard', requirePlaylistWrite, (req, res) => { // Delete playlist router.delete('/:id', requirePlaylistWrite, (req, res) => { + // Which screens are about to lose their playlist — read BEFORE the delete, because + // devices.playlist_id is ON DELETE SET NULL and the association is gone immediately after. + const affected = db.prepare('SELECT id FROM devices WHERE playlist_id = ?').all(req.params.id); + db.prepare('DELETE FROM playlists WHERE id = ?').run(req.params.id); + + // Tell them. The database detaches correctly, but nothing was emitted — so a screen kept showing + // the deleted playlist until it happened to reconnect or was restarted. You delete a playlist to + // take content off the wall; the wall carried on regardless. Every sibling mutation here already + // pushes (publish, assign), and DELETE /devices/:id/playlist was given a push for exactly this + // reason: "so the screen stops, rather than leaving the old content up". + try { + const io = req.app.get('io'); + if (io) { + const { buildPlaylistPayload } = require('../ws/deviceSocket'); + const commandQueue = require('../lib/command-queue'); + for (const d of affected) { + commandQueue.queueOrEmitPlaylistUpdate(io.of('/device'), d.id, buildPlaylistPayload); + } + } + } catch (e) { /* best-effort; the heartbeat refresh still picks it up */ } + res.json({ success: true }); });