diff --git a/server/player/index.html b/server/player/index.html index dc788c3..c5b8418 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -812,8 +812,13 @@ // playing. Showing the idle "Waiting for content..." overlay unconditionally covered // the live video (audio kept playing underneath) and the subsequent "Playlist // unchanged" left it up. Only fall to idle when nothing is actually playing. - const showIdle = window.PlayerMediaHealth - ? PlayerMediaHealth.shouldShowIdle({ isPlaying: isPlaying, hasContent: playlist.length > 0 }) + // Guard the METHOD, not just the object: a device running a stale-cached + // player-media-health.js (older module, no shouldShowIdle) would otherwise throw + // "shouldShowIdle is not a function" here and abort the rest of this handler. The + // !isPlaying fallback is equivalent for the playing case (playing -> not idle). + const PMH = window.PlayerMediaHealth; + const showIdle = (PMH && typeof PMH.shouldShowIdle === 'function') + ? PMH.shouldShowIdle({ isPlaying: isPlaying, hasContent: playlist.length > 0 }) : !isPlaying; if (showIdle) showStatus('Waiting for content...'); }); @@ -1338,7 +1343,7 @@ : null, surfaceAttached: !!(container && container.querySelector('video,img,iframe,.wall-stage')), }; - if (window.PlayerMediaHealth && PlayerMediaHealth.needsReattach(state)) { + if (window.PlayerMediaHealth && typeof PlayerMediaHealth.needsReattach === 'function' && PlayerMediaHealth.needsReattach(state)) { console.log('[refresh] media surface lost on no-change refresh — re-attaching current item'); playCurrentItem(); } else if (isPlaying) { diff --git a/server/test/player-media-health.test.js b/server/test/player-media-health.test.js index afdd0ba..150014a 100644 --- a/server/test/player-media-health.test.js +++ b/server/test/player-media-health.test.js @@ -6,7 +6,16 @@ const { test } = require('node:test'); const assert = require('node:assert/strict'); -const { needsReattach, shouldShowIdle } = require('../lib/player-media-health'); +const PlayerMediaHealth = require('../lib/player-media-health'); +const { needsReattach, shouldShowIdle } = PlayerMediaHealth; + +// Guards the define-vs-call class of bug: every method the player calls on +// PlayerMediaHealth must actually be exported (a missing one throws "X is not a function" +// at the browser call site and aborts the socket handler). +test('module surface: needsReattach AND shouldShowIdle are both exported functions', () => { + assert.equal(typeof PlayerMediaHealth.needsReattach, 'function'); + assert.equal(typeof PlayerMediaHealth.shouldShowIdle, 'function'); +}); const video = (o) => ({ isPlaying: true, hasCurrentItem: true, itemKind: 'video', videoEl: o, surfaceAttached: true });