From 05746e13e63fbb0b0b9b370f8ce5df72a42218da Mon Sep 17 00:00:00 2001 From: Marco Sadjadi Date: Thu, 28 May 2026 03:26:56 +0200 Subject: [PATCH] fix(video): drop WebM source + load()-before-play() + open-in-tab fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner: "wird nicht richtig gestream hab browser daten gelöscht aber kann [nicht]" — clearing the cache didn't help. Three things changed: 1. **Single MP4 source.** Chrome listed the WebM source first because we offered it first; on the owner's setup the VP9 decode appears to stall silently and Chrome does NOT fall back to MP4 — it parks the element at networkState=2/readyState=0 forever. Removing the WebM source forces Chrome onto the MP4 (Main profile / yuv420p / TV-range / faststart, 2.6 MB) which we've already verified plays correctly. 2. **.load() before .play() in togglePlay.** When the original autoplay was blocked before the source ever fetched, some Chrome builds leave the element in a "stuck unloaded" state where subsequent .play() calls inside a user gesture also no-op. Calling .load() first resets the resource-selection algorithm, then .play() fetches and plays. 3. **playFailed escape hatch.** If .play() still rejects even after .load() + user gesture (extension sandbox, hardware decoder failure), surface a small "your browser blocked playback — open the video directly" link to the raw MP4. The visitor isn't trapped staring at a poster. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/components/hero-video.tsx | 118 ++++++++++++++++++----------- 1 file changed, 74 insertions(+), 44 deletions(-) diff --git a/apps/web/components/hero-video.tsx b/apps/web/components/hero-video.tsx index 8649050..4b34ee2 100644 --- a/apps/web/components/hero-video.tsx +++ b/apps/web/components/hero-video.tsx @@ -1,72 +1,84 @@ 'use client'; -import { Play, Volume2, VolumeX } from 'lucide-react'; +import { ExternalLink, Play, Volume2, VolumeX } from 'lucide-react'; import { useCallback, useEffect, useRef, useState } from 'react'; /** - * Hero video player with two controls: + * Hero video player tuned for the stubborn-autoplay cases we hit in + * the field (Chrome with prefers-reduced-motion, data saver, etc.). * - * 1. Big PLAY overlay shown while the video is paused. Browsers - * block autoplay for users with `prefers-reduced-motion`, on data - * saver, or with strict autoplay policies — so the poster sits - * there forever and the visitor thinks the page is broken. The - * overlay gives them an explicit, unmissable affordance to start - * it. We also wire `onClick` on the - {/* Centre PLAY button — shown only while paused. Covers the - whole video so clicking anywhere starts playback; the inner - circle is the visual affordance. */} + {/* PLAY overlay — visible while paused. Full-frame so clicking + anywhere starts playback (YouTube / Vimeo pattern). */} {!playing && ( )} - {/* Mute toggle — always visible, top of z-stack so it stays - clickable even when the play overlay is up. */} + {/* Fallback escape hatch — surfaces only if .play() rejects even + after a user gesture (extension sandbox, hardware-decoder + failures, etc.). One-line link to the raw MP4 so the visitor + isn't trapped staring at a poster forever. */} + {playFailed && !playing && ( + + your browser blocked playback — open the video directly + + + )} + + {/* Mute toggle — always visible, top of z-stack. */}