Merge improvement/player-drop-hd-experiment: drop HD flash, clean notes (v0.36.4)

This commit is contained in:
npeter83 2026-07-10 18:14:32 +02:00
commit f0bab315ad
3 changed files with 12 additions and 81 deletions

View file

@ -1 +1 @@
0.36.3
0.36.4

View file

@ -20,14 +20,6 @@ import { useBackToClose } from "../lib/history";
// How close to the end (seconds) counts as "finished" → auto-mark watched.
const FINISH_MARGIN = 10;
// One-time HD-unlock (see attemptHdUnlock). YouTube hard-caps a windowed embed to ~360p and only a
// real fullscreen lifts the cap; the lift then persists for the whole page session. So on the first
// video opened we briefly flash the player to fullscreen and back to coax YouTube past the cap once.
// Cap the flash at this many ms, but exit as soon as we see the quality actually rise.
const HD_UNLOCK_MAX_MS = 2500;
// Module-scoped so the flash happens only ONCE per page session (the unlock persists afterwards).
let hdUnlockDone = false;
// Persistent playback settings (stored in users.preferences). Auto-advance = what plays when a
// video ends; loop = whether it repeats the current video ("one"), wraps the list at its ends
// ("all"), or neither ("off"). Both apply to any queued player (feed or playlist).
@ -132,9 +124,6 @@ export default function PlayerModal({
// modal adjusts volume (not just the small player area).
const dialogRef = useRef<HTMLDivElement | null>(null);
const volTimerRef = useRef<number | undefined>(undefined);
// HD-unlock flash bookkeeping (see attemptHdUnlock / finishHdUnlock).
const unlockTimerRef = useRef<number | undefined>(undefined);
const unlockingRef = useRef(false);
// Volume level to flash in the on-player overlay (null = hidden). Auto-fades after a moment.
const [volumeUi, setVolumeUi] = useState<number | null>(null);
// When the user interacts with YouTube's own controls (gear/seek/CC), focus moves into the
@ -157,32 +146,6 @@ export default function PlayerModal({
if (document.fullscreenElement) document.exitFullscreen?.();
else el.requestFullscreen?.();
};
// End the HD-unlock flash: exit fullscreen (if we're still in it) and mark the session unlocked so
// it never flashes again. Called either when YouTube's quality is seen to rise or on the max timeout.
const finishHdUnlock = () => {
if (!unlockingRef.current) return;
unlockingRef.current = false;
window.clearTimeout(unlockTimerRef.current);
hdUnlockDone = true;
if (document.fullscreenElement) document.exitFullscreen?.().catch(() => {});
};
// Coax YouTube past its windowed 360p cap: on the first opened video, briefly enter fullscreen so
// YouTube lifts the cap (which then persists for the session), then exit back to the small player.
// Needs live user activation (the modal-open click) — silently retried on the next click if it's
// already expired, and a no-op once the session is unlocked. Best-effort; can't be forced further.
const attemptHdUnlock = () => {
if (hdUnlockDone || unlockingRef.current) return;
const el = stageRef.current;
if (!el || !el.requestFullscreen || document.fullscreenElement) return;
el.requestFullscreen()
.then(() => {
unlockingRef.current = true;
unlockTimerRef.current = window.setTimeout(finishHdUnlock, HD_UNLOCK_MAX_MS);
})
.catch(() => {
/* no user activation yet / fullscreen blocked — leave hdUnlockDone false to retry on a click */
});
};
const flashVolume = (level: number) => {
setVolumeUi(level);
window.clearTimeout(volTimerRef.current);
@ -525,24 +488,18 @@ export default function PlayerModal({
playsinline: 1,
// Best-effort HD hint. The IFrame API's setPlaybackQuality/suggestedQuality are hard no-ops
// now (YouTube removed them), and the undocumented `vq` URL param is only occasionally
// honoured — kept because it's harmless. YouTube hard-caps an embed's max quality by the
// player's on-screen size; a windowed embed is stuck ~360p and only true fullscreen lifts
// the cap (then it persists for the session). A CSS transform-scale trick does NOT fool it;
// we instead do a one-time fullscreen flash on open (see attemptHdUnlock) to lift the cap.
// honoured — kept because it's harmless. NOTE: YouTube hard-caps an embed's max quality by
// the player's on-screen size; a windowed embed is stuck ~360p and only true fullscreen
// lifts the cap (after which the higher pick persists for the session). We can't beat that
// programmatically — a CSS transform-scale trick was tried and does NOT fool the cap.
vq: "hd1080",
},
events: {
// Keep keyboard focus on the modal, not the iframe, so shortcuts work right away, and try
// the one-time HD-unlock flash while the modal-open click is still a live user activation.
onReady: () => {
focusModal();
attemptHdUnlock();
},
// Keep keyboard focus on the modal, not the iframe, so shortcuts work right away.
onReady: () => focusModal(),
onStateChange: (e: any) => {
// 1 === playing → sync the navigated-to video's title/author for display, and retry the
// HD-unlock (a second chance at the flash right as playback begins).
// 1 === playing → sync the navigated-to video's title/author for display.
if (e?.data === 1) {
attemptHdUnlock();
const p = playerRef.current;
const d = p && typeof p.getVideoData === "function" ? p.getVideoData() : null;
if (d) setLiveData({ title: d.title, author: d.author });
@ -556,15 +513,6 @@ export default function PlayerModal({
advanceOnEnd();
}
},
// While the HD-unlock flash is running, exit fullscreen as soon as YouTube's quality is
// seen to rise (no need to hold the flash the full timeout). onPlaybackQualityChange is one
// of the few still-live quality signals; e.data is a label ("hd1080"/"hd720"/"large"/…).
onPlaybackQualityChange: (e: any) => {
const q = String(e?.data || "");
if (unlockingRef.current && (q === "hd1080" || q === "hd720" || q === "large" || q === "highres")) {
finishHdUnlock();
}
},
// The embed couldn't play this video (embedding disabled, removed, private…).
// Surface our own message + an "Open on YouTube" escape hatch.
onError: (e: any) => setPlayerError(typeof e?.data === "number" ? e.data : -1),
@ -578,12 +526,6 @@ export default function PlayerModal({
return () => {
cancelled = true;
window.clearInterval(timer);
// If the modal closes / advances mid-flash, don't leave the player stuck in fullscreen.
window.clearTimeout(unlockTimerRef.current);
if (unlockingRef.current) {
unlockingRef.current = false;
if (document.fullscreenElement) document.exitFullscreen?.().catch(() => {});
}
// Final flush, then refresh the feed + playlists so resume bars reflect this session.
Promise.resolve(persist()).finally(() => {
qc.invalidateQueries({ queryKey: ["feed"] });
@ -651,7 +593,6 @@ export default function PlayerModal({
<div
onClick={() => {
focusModal();
attemptHdUnlock(); // fallback: a real click is a fresh user activation for the flash
togglePlay();
}}
title={t("player.shortcutsHint")}

View file

@ -15,19 +15,10 @@ export interface ReleaseEntry {
export const RELEASE_NOTES: ReleaseEntry[] = [
{
version: "0.36.3",
version: "0.36.4",
date: "2026-07-10",
summary: "Experimental: the player tries to unlock higher video quality automatically.",
features: [
"The in-app player now tries to lift YouTube's windowed quality limit for you. YouTube caps a small embedded player to a low resolution and only real fullscreen unlocks HD — so on the first video you open in a session, the player briefly flashes to fullscreen and back to trigger that unlock, which then sticks for the rest of the session. You may notice a short flash on that first video; later videos won't flash. (It's still bandwidth-dependent, and can be reverted if it doesn't help on your setup.)",
],
},
{
version: "0.36.2",
date: "2026-07-10",
summary: "Player's YouTube controls back to normal size (a quality experiment was reverted).",
fixes: [
"Reverted an experiment that tried to raise video quality in the small (windowed) player: it only shrank YouTube's on-screen controls without actually improving quality, because YouTube caps an embedded player's quality by its on-screen size. Tip for the sharpest picture: switch the player to fullscreen once — YouTube then keeps the higher quality even after you return to the small player.",
chores: [
"Internal in-app player cleanup.",
],
},
{
@ -41,9 +32,8 @@ export const RELEASE_NOTES: ReleaseEntry[] = [
{
version: "0.36.0",
date: "2026-07-10",
summary: "Sharper in-app playback and full access to YouTube's own player menu.",
summary: "Full access to YouTube's own player menu in the in-app player.",
features: [
"The in-app player is now larger and asks YouTube for higher quality, so videos look sharper than the old low-resolution default. (For the maximum your connection allows, go fullscreen.)",
"You can now open and navigate YouTube's own player menu — Quality, Speed, Captions and More options — all the way down. When you use YouTube's built-in controls, the app's click-to-play and scroll-for-volume shortcuts briefly step aside (a small badge shows this), and they return the moment you move the pointer off the video.",
],
},