import { useEffect, useRef, useState } from "react"; import { mediaSrc } from "../lib/api"; type Props = { videoId: string; title: string; watchUrl: string; poster?: string; }; declare global { interface Window { Vimeo?: { Player: new ( el: HTMLIFrameElement | HTMLElement, opts?: { id?: string | number; url?: string } ) => { on: (event: string, cb: (data?: unknown) => void) => void; destroy: () => void; }; }; } } let playerSdkPromise: Promise | null = null; function loadVimeoSdk(): Promise { if (window.Vimeo?.Player) return Promise.resolve(); if (playerSdkPromise) return playerSdkPromise; playerSdkPromise = new Promise((resolve, reject) => { const existing = document.querySelector( 'script[src="https://player.vimeo.com/api/player.js"]' ); if (existing) { existing.addEventListener("load", () => resolve()); existing.addEventListener("error", () => reject(new Error("Vimeo SDK failed"))); if (window.Vimeo?.Player) resolve(); return; } const s = document.createElement("script"); s.src = "https://player.vimeo.com/api/player.js"; s.async = true; s.onload = () => resolve(); s.onerror = () => reject(new Error("Vimeo SDK failed")); document.body.appendChild(s); }); return playerSdkPromise; } /** * Always embeds first. Only if Vimeo reports a privacy/error does the UI * fall back to opening the video on vimeo.com (last resort). */ export function VimeoEmbed({ videoId, title, watchUrl, poster }: Props) { const iframeRef = useRef(null); const [blocked, setBlocked] = useState(false); const embedSrc = `https://player.vimeo.com/video/${videoId}?title=0&byline=0&portrait=0`; useEffect(() => { let player: { destroy: () => void } | null = null; let cancelled = false; setBlocked(false); loadVimeoSdk() .then(() => { if (cancelled || !iframeRef.current || !window.Vimeo?.Player) return; player = new window.Vimeo.Player(iframeRef.current); player.on("error", (data: unknown) => { // Privacy / password / unavailable — leave site only as last resort console.warn("Vimeo player error", data); if (!cancelled) setBlocked(true); }); }) .catch(() => { // SDK load failure still keep iframe; browser may still play }); return () => { cancelled = true; try { player?.destroy(); } catch { /* ignore */ } }; }, [videoId]); if (blocked) { return ( {poster ? ( ) : (
)} Watch on Vimeo ↗ This video can’t be embedded here (Vimeo privacy). Opens on Vimeo as a last resort. ); } return (