diff --git a/server/player/index.html b/server/player/index.html index 11c5815..91d1646 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -1320,6 +1320,14 @@ const container = document.getElementById('playerContainer'); container.style.display = 'block'; + // Multi-zone: each zone pulls its own content by zone_id, independent of the + // rotating "current item". Render zones here (before the single-item bail) so + // an empty/placeholder current item can't blank the whole multi-zone screen. + if (layout && layout.zones && layout.zones.length > 1 && !wallConfig) { + renderZones(container, item); + return; + } + // Defense in depth: bail to waiting state on missing/malformed item rather // than fall through every branch and leave a blank container. const hasRenderableType = item && ( diff --git a/server/ws/deviceSocket.js b/server/ws/deviceSocket.js index ec1900f..7ef1cff 100644 --- a/server/ws/deviceSocket.js +++ b/server/ws/deviceSocket.js @@ -154,6 +154,16 @@ function buildPlaylistPayload(deviceId) { } } + // Zone reset: if the device isn't in a real multi-zone layout (single zone or + // no layout), strip any leftover zone_id from assignments. Otherwise, after + // switching a device from a multi-zone layout back to single/fullscreen, the + // content stays bound to a now-gone left/right zone_id and never plays. With + // zone_id nulled, both players fall back to the default fullscreen renderer. + const zoneCount = layout?.zones?.length || 0; + if (zoneCount < 2 && Array.isArray(assignments)) { + assignments = assignments.map(a => (a && a.zone_id != null ? { ...a, zone_id: null } : a)); + } + return { assignments, layout, orientation: device?.orientation || 'landscape', wall_config }; }