"use client"; import { forwardRef, useImperativeHandle, useState } from "react"; export type TwitchPlayerHandle = { seekTo: (seconds: number, unit?: "seconds") => void; }; type Props = { videoId: string; startSeconds?: number; }; // Twitch's `time` param wants an `XhYmZs` string, not raw seconds. function toTwitchTime(totalSeconds: number): string { const s = Math.max(0, Math.floor(totalSeconds)); const h = Math.floor(s / 3600); const m = Math.floor((s % 3600) / 60); return `${h}h${m}m${s % 60}s`; } // Twitch's iframe embed — like Rumble/Odysee there's no outside JS API, so we // seek by reloading with a new `time` query param. // // Twitch requires a `parent=` param naming every domain the player is embedded // under. We read `window.location.hostname` at runtime (this is a client-only // dynamic import, ssr:false), which covers the normal case. Caveat: Twitch // rejects bare IP-address parents and some reverse-proxy hosts — on those the // embed will refuse to load. const TwitchPlayer = forwardRef(function TwitchPlayer( { videoId, startSeconds = 0 }, ref, ) { const [start, setStart] = useState(() => Math.max(0, Math.floor(startSeconds)), ); useImperativeHandle( ref, () => ({ seekTo(seconds: number) { setStart(Math.max(0, Math.floor(seconds))); }, }), [], ); const parent = typeof window !== "undefined" ? window.location.hostname : "localhost"; const src = `https://player.twitch.tv/?video=${encodeURIComponent( videoId, )}&parent=${encodeURIComponent(parent)}&time=${toTwitchTime(start)}&autoplay=true`; return (