Turn captions off for real

cc_load_policy is only a request: the viewer's "always show captions"
preference overrides it, and auto-generated tracks ignore it. Unloading
the caption module is the part that sticks, and it has to run again after
every load because a new video brings the module back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9v2Xoa6eoeGYpK6gp95k9
This commit is contained in:
simon
2026-08-10 21:03:37 +02:00
co-authored by Claude Opus 5
parent db672b8b32
commit fc105d4e5e
+32 -3
View File
@@ -25,9 +25,28 @@ const PLAYER_VARS = {
playsinline: 1, playsinline: 1,
iv_load_policy: 3, iv_load_policy: 3,
fs: 0, fs: 0,
cc_load_policy: 0, // no burned-in captions over the visuals cc_load_policy: 0, // asks for no captions; see hideCaptions for the rest
}; };
/**
* Turn captions off for real.
*
* cc_load_policy is only a request: the viewer's "always show captions"
* preference overrides it, and auto-generated tracks ignore it outright.
* Unloading the module is the part that sticks. Two names because the module
* is 'captions' on the HTML5 player and 'cc' on the older one, and loading a
* new video resets both — so this has to run again after every load.
*/
export function hideCaptions(player) {
for (const module of ['captions', 'cc']) {
try {
player.unloadModule(module);
} catch {
// Module was never loaded for this video; nothing to turn off.
}
}
}
/** /**
* Create a player in the given element id. Resolves once the player is ready. * Create a player in the given element id. Resolves once the player is ready.
* `onStateChange` receives the raw YT state code. * `onStateChange` receives the raw YT state code.
@@ -41,8 +60,18 @@ export async function createPlayer(elementId, { onStateChange } = {}) {
height: '100%', height: '100%',
playerVars: { ...PLAYER_VARS, origin: location.origin }, playerVars: { ...PLAYER_VARS, origin: location.origin },
events: { events: {
onReady: () => resolve(player), onReady: () => {
onStateChange: (event) => onStateChange?.(event.data, player), hideCaptions(player);
resolve(player);
},
onStateChange: (event) => {
// The caption module comes back with each new video, and only once
// the video is actually running, so re-clear it here.
if (event.data === STATE.PLAYING || event.data === STATE.CUED) {
hideCaptions(player);
}
onStateChange?.(event.data, player);
},
}, },
}); });
}); });