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 });