Update css/player-base.js

This commit is contained in:
ashley 2025-11-07 12:36:53 +01:00
parent 06030b63c6
commit 6ec6012b2b

View File

@ -35,8 +35,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
videoEl.setAttribute('webkit-playsinline', ''); videoEl.setAttribute('webkit-playsinline', '');
} catch {} } catch {}
// ——————————————————————— TitleBar on fullscreen —————————————————————— video.ready(() => {
video.ready(() => {
const metaTitle = document.querySelector('meta[name="title"]')?.content || ""; const metaTitle = document.querySelector('meta[name="title"]')?.content || "";
const metaDesc = document.querySelector('meta[name="twitter:description"]')?.content || ""; const metaDesc = document.querySelector('meta[name="twitter:description"]')?.content || "";
const metaAuthor = document.querySelector('meta[name="twitter:author"]')?.content || ""; const metaAuthor = document.querySelector('meta[name="twitter:author"]')?.content || "";
@ -87,8 +86,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
let intendedPlaying = false; let intendedPlaying = false;
// Only track explicit user intent, not programmatic flips // Only track explicit user intent, not programmatic flips
let userMutedVideo = false; let userMutedVideo = true; // start muted by default
let userMutedAudio = false;
let lastPlayKickTs = 0; let lastPlayKickTs = 0;
const STARTUP_GRACE_MS = 2200; const STARTUP_GRACE_MS = 2200;
@ -101,6 +99,10 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
let startupPhase = true; let startupPhase = true;
let firstPlayCommitted = false; let firstPlayCommitted = false;
// Start page muted: video UI shows muted, audio volume at 0
try { video.muted(true); } catch {}
try { audio.volume = 0; } catch {}
// Remove loop flags from DOM media // Remove loop flags from DOM media
try { videoEl.loop = false; videoEl.removeAttribute?.('loop'); } catch {} try { videoEl.loop = false; videoEl.removeAttribute?.('loop'); } catch {}
try { audio.loop = false; audio.removeAttribute?.('loop'); } catch {} try { audio.loop = false; audio.removeAttribute?.('loop'); } catch {}
@ -132,9 +134,8 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
let aligning = false; let aligning = false;
// —————————————————————————— Volume / Mute policy —————————————————————————— // —————————————————————————— Volume / Mute policy ——————————————————————————
// 1) Never set audio.muted except for the *temporary autoplay workaround*. // We never auto-unmute the video; user controls that.
// 2) Rely on volume ramps for fades and mirroring. // Audio is controlled purely by volume and mirrors the video volume/mute state.
// 3) If audio ever sits at volume===0 unintentionally, watchdog revives it.
let volAnim = null; let volAnim = null;
function setImmediateVolume(val) { function setImmediateVolume(val) {
@ -142,10 +143,9 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} }
function targetVolFromVideo() { function targetVolFromVideo() {
// base on the video element's user-facing volume & muted state only
const vVol = clamp01(typeof video.volume === 'function' ? video.volume() : (videoEl.volume ?? 1)); const vVol = clamp01(typeof video.volume === 'function' ? video.volume() : (videoEl.volume ?? 1));
const vMuted = !!(typeof video.muted === 'function' ? video.muted() : videoEl.muted); const vMuted = !!(typeof video.muted === 'function' ? video.muted() : videoEl.muted);
const hardMuted = vMuted || userMutedVideo; // user intent only; ignore userMutedAudio for target const hardMuted = vMuted || userMutedVideo;
return hardMuted ? 0 : vVol; return hardMuted ? 0 : vVol;
} }
@ -189,38 +189,10 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} }
} }
// ———————————————— Temporary mute guard for autoplay workaround ———————————————— async function ensureUnmutedIfNotUserMuted() {
let tempMuteDepth = 0; if (startupPhase) { updateAudioGainImmediate(); return; }
let tempMuteReleaseTimer = null; // Only adjust audio volume to follow video; never touch video.muted here
await softUnmuteAudio(80);
function pushTemporaryMute() {
tempMuteDepth++;
squelchMuteEvents++;
try { video.muted(true); } catch {}
try { audio.muted = true; } catch {}
suppressMirrorUntil = performance.now() + MUTE_SQUELCH_MS;
queueMicrotask(() => { squelchMuteEvents = Math.max(0, squelchMuteEvents - 1); });
}
function popTemporaryMute() {
if (tempMuteDepth > 0) tempMuteDepth--;
if (tempMuteDepth === 0) {
squelchMuteEvents++;
try {
if (!userMutedVideo) video.muted(false);
audio.muted = false;
} catch {}
queueMicrotask(() => { squelchMuteEvents = Math.max(0, squelchMuteEvents - 1); });
// After unmuting flags, restore volume smoothly to user target
softUnmuteAudio(120);
}
}
function clearTemporaryMuteTimer() {
if (tempMuteReleaseTimer) {
clearTimeout(tempMuteReleaseTimer);
tempMuteReleaseTimer = null;
}
} }
// ———————————————————————————— Utilities ———————————————————————————— // ———————————————————————————— Utilities ————————————————————————————
@ -235,6 +207,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} catch {} } catch {}
return false; return false;
} }
function canPlayAt(media, t) { function canPlayAt(media, t) {
try { try {
const rs = Number(media.readyState || 0); const rs = Number(media.readyState || 0);
@ -243,7 +216,9 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
return timeInBuffered(media, t); return timeInBuffered(media, t);
} catch { return false; } } catch { return false; }
} }
function bothPlayableAt(t) { return canPlayAt(videoEl, t) && canPlayAt(audio, t); } function bothPlayableAt(t) { return canPlayAt(videoEl, t) && canPlayAt(audio, t); }
function safeSetCT(media, t) { try { if (isFinite(t) && t >= 0) media.currentTime = t; } catch {} } function safeSetCT(media, t) { try { if (isFinite(t) && t >= 0) media.currentTime = t; } catch {} }
function clearSyncLoop() { function clearSyncLoop() {
@ -255,17 +230,31 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} }
} }
function setVideoMuted(val) { // ———————————————————— Play/Pause squelch (avoid loops) ————————————————————
squelchMuteEvents++; let squelchPlayPause = 0;
try { video.muted(val); } catch {}
queueMicrotask(() => { squelchMuteEvents = Math.max(0, squelchMuteEvents - 1); }); function beginSquelchPlayPause() { squelchPlayPause++; }
function endSquelchPlayPauseAsync() {
queueMicrotask(() => { squelchPlayPause = Math.max(0, squelchPlayPause - 1); });
} }
async function ensureUnmutedIfNotUserMuted() { async function safePlayVideo() {
if (startupPhase) { updateAudioGainImmediate(); return; } beginSquelchPlayPause();
if (!userMutedVideo) setVideoMuted(false); try {
// Do not flip audio.muted here; volume ramps handle audibility. const p = video.play();
await softUnmuteAudio(80); if (p && p.then) await p;
return true;
} catch {
return false;
} finally {
endSquelchPlayPauseAsync();
}
}
function safePauseVideo() {
beginSquelchPlayPause();
try { video.pause(); } catch {}
endSquelchPlayPauseAsync();
} }
// ———————————————————————— Sync loops ————————————————————————— // ———————————————————————— Sync loops —————————————————————————
@ -275,7 +264,10 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
function startFrameSyncLoop() { function startFrameSyncLoop() {
if (!useRVFC) return; if (!useRVFC) return;
const step = (_now, meta) => { const step = (_now, meta) => {
if (!intendedPlaying) { rvfcHandle = videoEl.requestVideoFrameCallback(step); return; } if (!intendedPlaying) {
rvfcHandle = videoEl.requestVideoFrameCallback(step);
return;
}
const vt = Number(video.currentTime()); const vt = Number(video.currentTime());
const at = Number(audio.currentTime); const at = Number(audio.currentTime);
if (isFinite(vt) && isFinite(at)) { if (isFinite(vt) && isFinite(at)) {
@ -303,19 +295,23 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
// Keep both elements' play/pause states aligned with intention // Keep both elements' play/pause states aligned with intention
if (intendedPlaying) { if (intendedPlaying) {
if (video.paused() && !audio.paused) { try { video.play(); } catch {} } if (video.paused() && !audio.paused) { safePlayVideo(); }
if (!video.paused() && audio.paused) { try { audio.play().catch(()=>{}); } catch {} } if (!video.paused() && audio.paused) {
try { audio.play().catch(() => {}); } catch {}
}
} else { } else {
if (!video.paused()) { try { video.pause(); } catch {} } if (!video.paused()) { safePauseVideo(); }
if (!audio.paused) { try { audio.pause(); } catch {} } if (!audio.paused) { try { audio.pause(); } catch {} }
} }
const delta = vt - at; const delta = vt - at;
if (Math.abs(delta) > RESYNC_DRIFT_LIMIT) { if (intendedPlaying && Math.abs(delta) > RESYNC_DRIFT_LIMIT) {
intendedPlaying = true;
pauseHard(); pauseHard();
setTimeout(() => playTogether({ allowMutedRetry: true }), 120); setTimeout(() => {
if (!intendedPlaying) return;
playTogether();
}, 120);
return; return;
} }
@ -347,7 +343,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} }
// Watchdog: revive if audio ended up silent unintentionally // Watchdog: revive if audio ended up silent unintentionally
if (intendedPlaying && !audio.paused && !audio.muted && !userMutedVideo && !userMutedAudio) { if (intendedPlaying && !audio.paused && !audio.muted && !userMutedVideo) {
if (audio.volume <= 0.001 && (performance.now() - suppressMirrorUntil) > 400) { if (audio.volume <= 0.001 && (performance.now() - suppressMirrorUntil) > 400) {
softUnmuteAudio(140); softUnmuteAudio(140);
} }
@ -376,17 +372,10 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} catch { return false; } } catch { return false; }
} }
function shouldAttemptLoudAutoplay() { // ———————————————————— playTogether: synced start, cancelable ————————————————————
// keep conservative async function playTogether() {
if (!('mediaSession' in navigator)) return false; if (syncing || restarting || !intendedPlaying) return;
return document.visibilityState === 'visible';
}
// ———————————————————— FIXED: make playTogether cancelable ————————————————————
async function playTogether({ allowMutedRetry = true } = {}) {
if (syncing || restarting) return;
syncing = true; syncing = true;
intendedPlaying = true;
lastPlayKickTs = performance.now(); lastPlayKickTs = performance.now();
const cancelled = () => !intendedPlaying; const cancelled = () => !intendedPlaying;
@ -402,69 +391,31 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
if (cancelled()) return; if (cancelled()) return;
setImmediateVolume(0); // start silent, bring up after both are running setImmediateVolume(0); // audio fades in to the target volume
let vOk = true, aOk = true; const vOk = await safePlayVideo();
if (cancelled() || !vOk) return;
try { const aOk = await tryPlay(audio);
const p = video.play(); if (cancelled() || !aOk) return;
if (p && p.then) await p;
if (cancelled()) return;
} catch {
vOk = false;
}
if (cancelled()) return; if (!syncInterval && intendedPlaying) startSyncLoop();
aOk = await tryPlay(audio); if (!firstPlayCommitted && intendedPlaying) {
if (cancelled()) return;
if ((allowMutedRetry && (!vOk || !aOk)) || !shouldAttemptLoudAutoplay()) {
if (cancelled()) return;
// ——— Scoped temporary mute for autoplay workaround ———
pushTemporaryMute();
await softMuteAudio(40);
if (cancelled()) { popTemporaryMute(); return; }
try {
const p2 = video.play();
if (p2 && p2.then) await p2;
} catch {}
if (cancelled()) { popTemporaryMute(); return; }
await tryPlay(audio);
if (cancelled()) { popTemporaryMute(); return; }
clearTemporaryMuteTimer();
tempMuteReleaseTimer = setTimeout(() => {
popTemporaryMute(); // restores flags & ramps volume back
}, 180);
} else {
if (cancelled()) return;
await softUnmuteAudio(140);
}
if (cancelled()) return;
if (!syncInterval) startSyncLoop();
if (!firstPlayCommitted) {
firstPlayCommitted = true; firstPlayCommitted = true;
setTimeout(() => { startupPhase = false; }, 800); setTimeout(() => { startupPhase = false; }, 800);
} }
if (intendedPlaying) await softUnmuteAudio(140);
} finally { } finally {
syncing = false; syncing = false;
} }
} }
function pauseHard() { function pauseHard() {
try { video.pause(); } catch {} safePauseVideo();
try { audio.pause(); } catch {} try { audio.pause(); } catch {}
clearSyncLoop(); clearSyncLoop();
clearTemporaryMuteTimer();
// Do not toggle muted flags here; leave user intent intact
} }
function pauseTogether() { function pauseTogether() {
@ -493,8 +444,14 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
}); });
} catch {} } catch {}
try { try {
navigator.mediaSession.setActionHandler('play', () => playTogether()); navigator.mediaSession.setActionHandler('play', () => {
navigator.mediaSession.setActionHandler('pause', () => pauseTogether()); intendedPlaying = true;
playTogether();
});
navigator.mediaSession.setActionHandler('pause', () => {
intendedPlaying = false;
pauseTogether();
});
navigator.mediaSession.setActionHandler('seekforward', (d) => { navigator.mediaSession.setActionHandler('seekforward', (d) => {
const inc = Number(d?.seekOffset) || 10; const inc = Number(d?.seekOffset) || 10;
video.currentTime(Math.min((video.currentTime() || 0) + inc, Number(video.duration()) || 0)); video.currentTime(Math.min((video.currentTime() || 0) + inc, Number(video.duration()) || 0));
@ -523,6 +480,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
} }
} catch {} } catch {}
} }
function saveProgressThrottled() { function saveProgressThrottled() {
try { try {
const t = Math.floor(Number(video.currentTime()) || 0); const t = Math.floor(Number(video.currentTime()) || 0);
@ -551,7 +509,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
const t = Number(video.currentTime()); const t = Number(video.currentTime());
if (bothPlayableAt(t)) { if (bothPlayableAt(t)) {
await ensureUnmutedIfNotUserMuted(); await ensureUnmutedIfNotUserMuted();
playTogether({ allowMutedRetry: true }); playTogether();
} }
}; };
el.addEventListener('canplay', tryResume); el.addEventListener('canplay', tryResume);
@ -592,7 +550,6 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
rampVolumeTo(targetVolFromVideo(), 80); rampVolumeTo(targetVolFromVideo(), 80);
return; return;
} }
// Use volume ramp; do not touch audio.muted here
rampVolumeTo(targetVolFromVideo(), 120); rampVolumeTo(targetVolFromVideo(), 120);
userMutedVideo = !!video.muted(); userMutedVideo = !!video.muted();
}); });
@ -604,20 +561,22 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
rampVolumeTo(targetVolFromVideo(), 120); rampVolumeTo(targetVolFromVideo(), 120);
}); });
// Track manual mutes on the hidden audio element (rare, but safe)
audio.addEventListener('volumechange', () => {
if (squelchMuteEvents) return;
userMutedAudio = !!audio.muted; // informational only; we don't mirror this back
});
video.on('ratechange', () => { try { audio.playbackRate = video.playbackRate(); } catch {} }); video.on('ratechange', () => { try { audio.playbackRate = video.playbackRate(); } catch {} });
// Only treat real user/OS play as intent
video.on('play', () => { video.on('play', () => {
if (squelchPlayPause) return;
intendedPlaying = true; intendedPlaying = true;
ensureUnmutedIfNotUserMuted().then(() => playTogether()); ensureUnmutedIfNotUserMuted().then(() => playTogether());
}); });
video.on('pause', () => { if (!restarting) pauseTogether(); }); video.on('pause', () => {
if (squelchPlayPause) return;
if (!restarting) {
intendedPlaying = false;
pauseTogether();
}
});
let wasPlayingBeforeSeek = false; let wasPlayingBeforeSeek = false;
let seekStartTime = 0; let seekStartTime = 0;
@ -654,12 +613,12 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
if (diff <= small) { if (diff <= small) {
if (wasPlayingBeforeSeek && bothPlayableAt(newTime)) { if (wasPlayingBeforeSeek && bothPlayableAt(newTime)) {
intendedPlaying = true; intendedPlaying = true;
playTogether({ allowMutedRetry: true }); playTogether();
} }
} else if (diff <= large) { } else if (diff <= large) {
if (wasPlayingBeforeSeek) { if (wasPlayingBeforeSeek) {
intendedPlaying = true; intendedPlaying = true;
if (bothPlayableAt(newTime)) playTogether({ allowMutedRetry: true }); if (bothPlayableAt(newTime)) playTogether();
} else { } else {
intendedPlaying = false; intendedPlaying = false;
pauseHard(); pauseHard();
@ -674,7 +633,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
if (wasPlayingBeforeSeek) { if (wasPlayingBeforeSeek) {
intendedPlaying = true; intendedPlaying = true;
await ensureUnmutedIfNotUserMuted(); await ensureUnmutedIfNotUserMuted();
playTogether({ allowMutedRetry: true }); playTogether();
} }
}, 160); }, 160);
} }
@ -717,7 +676,10 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
const tryAutoResume = async () => { const tryAutoResume = async () => {
if (!intendedPlaying) return; if (!intendedPlaying) return;
const t = Number(video.currentTime()); const t = Number(video.currentTime());
if (bothPlayableAt(t)) { await ensureUnmutedIfNotUserMuted(); playTogether({ allowMutedRetry: true }); } if (bothPlayableAt(t)) {
await ensureUnmutedIfNotUserMuted();
playTogether();
}
}; };
videoEl.addEventListener('canplay', tryAutoResume); videoEl.addEventListener('canplay', tryAutoResume);
audio.addEventListener('canplay', tryAutoResume); audio.addEventListener('canplay', tryAutoResume);