fix(content): YouTube preview 153 — drop enablejsapi/origin from the passive embed

The content-tab preview embedded a RAW iframe with enablejsapi=1 (baked into the stored
/embed URL by /youtube) plus origin=window.location.origin — but the content tab loads the
YouTube IFrame API zero times. enablejsapi=1 + origin tells YouTube's player to expect a
postMessage handshake from a parent JS API that never exists here, which surfaces as "Video
player configuration error" (153). Same-video proof: it plays on the device player (which
loads iframe_api + uses YT.Player, so the handshake completes) and failed only on the content
tab — so it was never a video/embeddability problem, purely the embed construction.

Fix: the preview is passive (never drives playback via JS), so it must not declare the JS API
— strip enablejsapi + origin, leaving a plain /embed/ID (the form that plays in a bare tab).
Did NOT touch /youtube storage (the player extracts the videoId and ignores stored params, so
the baked-in enablejsapi is harmless there). Retracts the earlier wrong "validate
embeddability at add-time" diagnosis (never built — it would have rejected this embeddable
video). Frontend-only; suite 149 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ScreenTinker 2026-06-14 18:18:58 -05:00
parent ed45a9a23d
commit 7f7dc80a8c

View file

@ -635,7 +635,7 @@ function showPreview(content) {
<button style="position:absolute;top:8px;right:8px;z-index:1;background:rgba(0,0,0,0.7);border:none;color:white;width:32px;height:32px;border-radius:50%;font-size:18px;cursor:pointer" id="closePreview">&times;</button> <button style="position:absolute;top:8px;right:8px;z-index:1;background:rgba(0,0,0,0.7);border:none;color:white;width:32px;height:32px;border-radius:50%;font-size:18px;cursor:pointer" id="closePreview">&times;</button>
<div style="max-width:80vw;max-height:80vh"> <div style="max-width:80vw;max-height:80vh">
${isYoutube ${isYoutube
? `<iframe src="${(() => { try { const u = new URL(src); if (!u.searchParams.has('mute')) u.searchParams.set('mute','1'); if (!u.searchParams.has('enablejsapi')) u.searchParams.set('enablejsapi','1'); if (!u.searchParams.has('origin')) u.searchParams.set('origin', window.location.origin); return u.toString(); } catch { return src; } })()}" style="width:80vw;height:45vw;max-height:80vh;display:block;border:none" allow="autoplay;encrypted-media" allowfullscreen></iframe>` ? `<iframe src="${(() => { try { const u = new URL(src); u.searchParams.set('mute', '1'); /* #YT153: this is a PASSIVE preview - it never drives playback via JS, so it must NOT declare the JS IFrame API. enablejsapi=1 (baked into the stored URL) + origin tells YouTube to expect a postMessage handshake from a parent API that this page never loads -> "Video player configuration error" (153). The player works because it loads iframe_api + uses YT.Player; the preview just needs a plain /embed/ID. */ u.searchParams.delete('enablejsapi'); u.searchParams.delete('origin'); return u.toString(); } catch { return src; } })()}" style="width:80vw;height:45vw;max-height:80vh;display:block;border:none" allow="autoplay;encrypted-media" allowfullscreen></iframe>`
: isVideo : isVideo
? `<video src="${esc(src)}" controls autoplay style="max-width:80vw;max-height:80vh;display:block"></video>` ? `<video src="${esc(src)}" controls autoplay style="max-width:80vw;max-height:80vh;display:block"></video>`
: `<img src="${esc(src)}" style="max-width:80vw;max-height:80vh;display:block">` : `<img src="${esc(src)}" style="max-width:80vw;max-height:80vh;display:block">`