itube-modern-player 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +55 -50
- package/dist/core.js.map +1 -1
- package/dist/{gesture-BLWiPHlW.cjs → gesture-Dc6eb_46.cjs} +2 -2
- package/dist/{gesture-BLWiPHlW.cjs.map → gesture-Dc6eb_46.cjs.map} +1 -1
- package/dist/{gesture-BYJrYanO.js → gesture-hoL6gIex.js} +2 -2
- package/dist/{gesture-BYJrYanO.js.map → gesture-hoL6gIex.js.map} +1 -1
- package/dist/itube-modern-player.iife.js +1 -1
- package/dist/itube-modern-player.iife.js.map +1 -1
- package/dist/lazy.cjs +1 -1
- package/dist/lazy.js +1 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.js +1 -1
- package/package.json +1 -1
package/dist/core.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.cjs","sources":["../src/core/dom.ts","../src/core/events.ts","../src/core/icons.ts","../src/core/localeRegistry.ts","../src/features/heatmap.ts","../src/features/ads/vast.ts","../src/features/ads/manager.ts","../src/features/chapters.ts","../src/features/hls.ts","../src/features/thumbnails.ts","../src/ui/menu.ts","../src/ui/progress.ts","../src/ui/controls.ts","../src/ui/overlays.ts","../src/ui/playlistPanel.ts","../src/ui/scenesPanel.ts","../src/player.ts"],"sourcesContent":["/** DOM helpers. All player markup is built through these — no innerHTML templates. */\n\nimport type { IconSource, PreviewMetaItem } from '../types'\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string,\n attrs?: Record<string, string>,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className) node.className = className\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, value)\n }\n return node\n}\n\n/**\n * Render an icon into `target`, honoring its kind: raw SVG (set as innerHTML)\n * or an image URL (an `<img class=\"imp-icon-img\">`). A bare string is raw SVG\n * when it starts with `<`, otherwise an image URL.\n */\nexport function applyIcon(target: HTMLElement, icon: IconSource): void {\n target.textContent = ''\n if (typeof icon === 'object') {\n if ('url' in icon) target.append(el('img', 'imp-icon-img', { src: icon.url, alt: '' }))\n else target.innerHTML = icon.svg\n return\n }\n if (/^\\s*</.test(icon)) target.innerHTML = icon\n else target.append(el('img', 'imp-icon-img', { src: icon, alt: '' }))\n}\n\n/**\n * Real `<img>` filling its (positioned, overflow-hidden) container like\n * `background-size: cover`. Used for every poster/preview surface — a real\n * element (not a CSS background) is lazy-loadable, LCP-eligible and keeps\n * the markup identical between the lazy placeholder and the live player.\n */\nexport function coverImg(src: string, alt = ''): HTMLImageElement {\n const img = el('img', 'imp-cover-img', { alt, decoding: 'async' }) as HTMLImageElement\n img.src = src\n return img\n}\n\n/** Icon-only button with an accessible label. */\nexport function iconButton(className: string, label: string, icon: IconSource): HTMLButtonElement {\n const btn = el('button', `imp-btn ${className}`, { type: 'button', 'aria-label': label, title: label })\n applyIcon(btn, icon)\n return btn\n}\n\n/**\n * Build the preview-meta row (used by the next-video hover card and the end\n * overlay). Each chunk renders as a `<span>`; a chunk with an `icon` gets an\n * inline leading glyph (e.g. a user icon before \"Uploaded by user\"). Chunks are\n * separated by a `·` via CSS. `base` is the BEM block, e.g. `\"imp-upnext\"`.\n */\nexport function previewMetaEl(items: PreviewMetaItem[], base: string): HTMLElement {\n const row = el('div', `${base}__meta`)\n items.forEach((item, i) => {\n // A real space between chunks is the only line-break opportunity at the\n // separator: the `·` itself is glued to the end of the previous chunk (CSS\n // ::after), so it never orphans at the start of a wrapped line. Text inside\n // a chunk still wraps word-by-word at its own spaces.\n if (i > 0) row.append(document.createTextNode(' '))\n const text = typeof item === 'string' ? item : item.text\n const icon = typeof item === 'string' ? undefined : item.icon\n const chunk = el('span', `${base}__meta-chunk`)\n if (icon) {\n const ic = el('span', `${base}__meta-icon`)\n applyIcon(ic, icon)\n chunk.append(ic)\n }\n chunk.append(document.createTextNode(text))\n row.append(chunk)\n })\n return row\n}\n\nexport function setIcon(btn: HTMLButtonElement, icon: IconSource, label?: string): void {\n applyIcon(btn, icon)\n if (label !== undefined) {\n btn.setAttribute('aria-label', label)\n btn.setAttribute('title', label)\n }\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/** `73` → `\"1:13\"`, `3673` → `\"1:01:13\"`. Infinity/NaN → `\"0:00\"`. */\nexport function formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00'\n const s = Math.floor(seconds % 60)\n const m = Math.floor((seconds / 60) % 60)\n const h = Math.floor(seconds / 3600)\n const mm = h > 0 ? String(m).padStart(2, '0') : String(m)\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`\n}\n\n/** Parse `\"00:01:02.500\"` / `\"01:02.500\"` VTT timestamps into seconds. */\nexport function parseVttTime(raw: string): number | null {\n const m = raw.trim().match(/^(?:(\\d+):)?(\\d{1,2}):(\\d{2})(?:[.,](\\d{1,3}))?$/)\n if (!m) return null\n const h = m[1] ? Number(m[1]) : 0\n const min = Number(m[2])\n const s = Number(m[3])\n const ms = m[4] ? Number(m[4].padEnd(3, '0')) : 0\n return h * 3600 + min * 60 + s + ms / 1000\n}\n\n/** Tiny WebVTT cue parser — enough for chapters and thumbnail tracks. */\nexport function parseVttCues(text: string): Array<{ start: number; end: number; text: string }> {\n const cues: Array<{ start: number; end: number; text: string }> = []\n // Normalize newlines, drop the WEBVTT header block, split into cue blocks.\n const blocks = text.replace(/\\r\\n?/g, '\\n').split(/\\n\\n+/)\n for (const block of blocks) {\n const lines = block.split('\\n').filter((l) => l.trim() !== '')\n if (lines.length === 0) continue\n let timingIndex = lines.findIndex((l) => l.includes('-->'))\n if (timingIndex === -1) continue\n const [rawStart, rawEnd] = lines[timingIndex].split('-->')\n const start = parseVttTime(rawStart)\n const end = parseVttTime((rawEnd ?? '').split(' ')[1] ?? rawEnd ?? '')\n ?? parseVttTime(rawEnd ?? '')\n if (start === null || end === null) continue\n const cueText = lines.slice(timingIndex + 1).join('\\n').trim()\n if (cueText) cues.push({ start, end, text: cueText })\n }\n return cues\n}\n\n/** Resolve `url` against the location of the VTT file it came from. */\nexport function resolveUrl(url: string, base: string): string {\n try {\n return new URL(url, new URL(base, window.location.href)).toString()\n } catch {\n return url\n }\n}\n","/** Minimal strictly-typed event emitter. No globals, no wildcard magic. */\nexport class Emitter<M> {\n private listeners = new Map<keyof M, Set<(payload: never) => void>>()\n\n /** Subscribe. Returns an unsubscribe function. */\n on<K extends keyof M>(event: K, fn: (payload: M[K]) => void): () => void {\n let set = this.listeners.get(event)\n if (!set) {\n set = new Set()\n this.listeners.set(event, set)\n }\n set.add(fn as (payload: never) => void)\n return () => this.off(event, fn)\n }\n\n once<K extends keyof M>(event: K, fn: (payload: M[K]) => void): () => void {\n const off = this.on(event, (payload) => {\n off()\n fn(payload)\n })\n return off\n }\n\n off<K extends keyof M>(event: K, fn: (payload: M[K]) => void): void {\n this.listeners.get(event)?.delete(fn as (payload: never) => void)\n }\n\n emit<K extends keyof M>(event: K, payload: M[K]): void {\n const set = this.listeners.get(event)\n if (!set) return\n for (const fn of [...set]) {\n try {\n ;(fn as (payload: M[K]) => void)(payload)\n } catch (err) {\n // A broken consumer listener must not break the player.\n console.error('[itube-player] listener error', err)\n }\n }\n }\n\n removeAll(): void {\n this.listeners.clear()\n }\n}\n","import type { IconName } from '../types'\n\nconst svg = (body: string, viewBox = '0 0 24 24') =>\n `<svg viewBox=\"${viewBox}\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">${body}</svg>`\n\n/** Default icon set. Every icon can be replaced via `options.icons`. */\nexport const defaultIcons: Record<IconName, string> = {\n play: svg('<path d=\"M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z\"/>'),\n pause: svg('<rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\"/><rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\"/>'),\n replay: svg('<path d=\"M12 5V2.5L7.5 6 12 9.5V7a5 5 0 1 1-5 5H5a7 7 0 1 0 7-7Z\"/>'),\n bigPlay: svg('<path d=\"M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z\"/>'),\n volumeHigh: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z\"/><path d=\"M14.5 5.2a8 8 0 0 1 0 13.6v-2a6 6 0 0 0 0-9.6v-2Z\"/>'),\n volumeLow: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z\"/>'),\n volumeMute: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"m15.3 9.3 1.4 1.4 1.4-1.4 1.4 1.4-1.4 1.4 1.4 1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4Z\"/>'),\n fullscreen: svg('<path d=\"M5 5h5v2H7v3H5V5Zm9 0h5v5h-2V7h-3V5ZM5 14h2v3h3v2H5v-5Zm12 0h2v5h-5v-2h3v-3Z\"/>'),\n fullscreenExit: svg('<path d=\"M10 10H5V8h3V5h2v5Zm4 0V5h2v3h3v2h-5Zm-4 4v5H8v-3H5v-2h5Zm4 0h5v2h-3v3h-2v-5Z\"/>'),\n pip: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h14V7H5Z\"/><rect x=\"12\" y=\"11\" width=\"6\" height=\"4\"/>'),\n settings: svg('<path d=\"M12 8.5A3.5 3.5 0 1 0 12 15.5 3.5 3.5 0 0 0 12 8.5Zm0 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z\"/><path d=\"M10.3 2.8 9.9 5.1a7 7 0 0 0-1.5.86l-2.2-.8-1.7 3 1.8 1.5a7 7 0 0 0 0 1.7l-1.8 1.5 1.7 3 2.2-.8c.46.36.97.65 1.5.86l.4 2.3h3.4l.4-2.3a7 7 0 0 0 1.5-.86l2.2.8 1.7-3-1.8-1.5a7 7 0 0 0 0-1.7l1.8-1.5-1.7-3-2.2.8a7 7 0 0 0-1.5-.86l-.4-2.3h-3.4Zm1.7 5.7a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Z\"/>'),\n speed: svg('<path d=\"M12 4a9 9 0 0 0-9 9 8.96 8.96 0 0 0 1.62 5.16l1.64-1.15A7 7 0 0 1 5 13a7 7 0 1 1 12.74 4.01l1.64 1.15A8.96 8.96 0 0 0 21 13a9 9 0 0 0-9-9Z\"/><path d=\"m15.6 8.3-3.95 3.13a1.5 1.5 0 1 0 1.92 1.92L16.7 9.4a.78.78 0 0 0-1.1-1.1Z\"/>'),\n scenes: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h3V7H5Zm5 0v10h9V7h-9Zm-5 3h3v1H5v-1Zm0 3h3v1H5v-1Z\"/>'),\n shuffle: svg('<path d=\"M4 6h2.6c1.5 0 2.9.7 3.8 1.9l4.1 5.4a2.75 2.75 0 0 0 2.2 1.1H19l-1.8-1.8 1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 16.4h-2.3a4.75 4.75 0 0 1-3.8-1.9L8.8 9.1A2.75 2.75 0 0 0 6.6 8H4V6Z\"/><path d=\"M19 7.6h-2.3c-.86 0-1.67.41-2.18 1.1l-.93 1.23-1.25-1.65.58-.77A4.75 4.75 0 0 1 16.7 5.6H19L17.2 3.8l1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 7.6Z\" transform=\"translate(0 1.2)\"/>'),\n repeat: svg('<path d=\"M7 7h10v2.5L21 6l-4-3.5V5H5v6h2V7Zm10 10H7v-2.5L3 18l4 3.5V19h12v-6h-2v4Z\"/>'),\n subtitles: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h14V7H5Zm2 3h6v2H7v-2Zm8 0h2v2h-2v-2ZM7 14h2v2H7v-2Zm4 0h6v2h-6v-2Z\"/>'),\n list: svg('<path d=\"M4 6h2v2H4V6Zm4 0h12v2H8V6ZM4 11h2v2H4v-2Zm4 0h12v2H8v-2ZM4 16h2v2H4v-2Zm4 0h12v2H8v-2Z\"/>'),\n next: svg('<path d=\"M6 5.5v13c0 .77.84 1.25 1.5.85l9-6.5a1 1 0 0 0 0-1.7l-9-6.5c-.66-.4-1.5.08-1.5.85Z\"/><rect x=\"17\" y=\"5\" width=\"2.5\" height=\"14\" rx=\"1\"/>'),\n previous: svg('<path d=\"M18 5.5v13c0 .77-.84 1.25-1.5.85l-9-6.5a1 1 0 0 1 0-1.7l9-6.5c.66-.4 1.5.08 1.5.85Z\"/><rect x=\"4.5\" y=\"5\" width=\"2.5\" height=\"14\" rx=\"1\"/>'),\n // Thin circular-arrow (\"rotate\") icons — open center so the step number reads\n // clearly inside the ring (matches the reference skip ±N look).\n seekForward: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><polyline points=\"23 4 23 10 17 10\"/><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\"/></svg>',\n seekBack: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>',\n like: svg('<path d=\"M9 21H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h4v11Zm2 0h7.1a2 2 0 0 0 2-1.6l1.3-7a2 2 0 0 0-2-2.4H14V5.5A2.5 2.5 0 0 0 11.5 3l-.5.1V10h-.9L11 21Z\"/>'),\n dislike: svg('<path d=\"M15 3h4a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-4V3Zm-2 0H5.9a2 2 0 0 0-2 1.6l-1.3 7a2 2 0 0 0 2 2.4H10v4.5A2.5 2.5 0 0 0 12.5 21l.5-.1V14h.9L13 3Z\"/>'),\n addTo: svg('<path d=\"M4 6h12v2H4V6Zm0 4h12v2H4v-2Zm0 4h8v2H4v-2Zm14 0v-4h2v4h4v2h-4v4h-2v-4h-4v-2h4Z\"/>'),\n share: svg('<path d=\"M18 8a3 3 0 1 0-2.83-4H15a3 3 0 0 0 .14 1.06L8.4 8.94a3 3 0 1 0 0 6.12l6.74 3.88A3 3 0 1 0 16 16.6l-6.74-3.88a3.03 3.03 0 0 0 0-1.44L16 7.4c.55.38 1.24.6 2 .6Z\"/>'),\n report: svg('<path d=\"M5 3h2v18H5V3Zm4 1h10l-2.5 4L19 12H9V4Z\"/>'),\n more: svg('<circle cx=\"12\" cy=\"5\" r=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/><circle cx=\"12\" cy=\"19\" r=\"2\"/>'),\n close: svg('<path d=\"m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6 10.6 12 5 6.4 6.4 5Z\"/>'),\n // Generic \"uploaded by a user\" glyph (head + shoulders) for preview meta.\n user: svg('<path d=\"M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm0 2c-3.6 0-8 1.8-8 4.4V21h16v-2.6c0-2.6-4.4-4.4-8-4.4Z\"/>'),\n // Generic \"channel\" glyph (play badge) for preview meta when a channel is set.\n channel: svg('<path d=\"M3 5h18a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm7 3.5v7l6-3.5-6-3.5Z\"/>'),\n}\n","import type { PlayerLabels } from '../types'\nimport { defaultLabels } from './labels'\n\n/**\n * Locale registry. The core only ships English; the main package entry\n * registers all built-in dictionaries, while `itube-modern-player/core`\n * leaves them out so size-conscious builds can register just one:\n *\n * ```ts\n * import { Player, registerLocale } from 'itube-modern-player/core'\n * import { locales } from 'itube-modern-player/locales'\n * registerLocale('ru', locales.ru)\n * ```\n */\nconst registry: Partial<Record<string, PlayerLabels>> = { en: defaultLabels }\n\nexport function registerLocale(code: string, labels: PlayerLabels): void {\n registry[code] = labels\n}\n\nexport function registerLocales(map: Partial<Record<string, PlayerLabels>>): void {\n for (const [code, labels] of Object.entries(map)) {\n if (labels) registry[code] = labels\n }\n}\n\n/** Resolve a registered locale with graceful fallback to English. */\nexport function getLocale(code: string | undefined): PlayerLabels {\n return (code && registry[code]) || defaultLabels\n}\n\n/** Codes currently registered (built-ins and custom). */\nexport function registeredLanguages(): string[] {\n return Object.keys(registry)\n}\n","import type { HeatmapPoint } from '../types'\n\n/** Gaussian-ish ±2 smoothing; edges clamp to the nearest sample (no end dip). */\nfunction smooth(raw: number[]): number[] {\n const kernel = [0.06, 0.24, 0.4, 0.24, 0.06]\n const n = raw.length\n return raw.map((_, i) =>\n kernel.reduce((acc, k, j) => {\n const idx = Math.min(n - 1, Math.max(0, i + j - 2))\n return acc + k * raw[idx]\n }, 0),\n )\n}\n\n/** Value at percentile `p` (0..1) of an already-ascending-sorted array. */\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0\n const idx = Math.min(sorted.length - 1, Math.max(0, Math.round((sorted.length - 1) * p)))\n return sorted[idx]\n}\n\n/**\n * Smooth + normalize raw bucket values into a 0..1 curve.\n * - `\"peaks\"`: divide by the max (replayed-spikes look);\n * - `\"retention\"`: divide by the 95th percentile so a lone start spike can't\n * crush the rest of the curve to a flat line.\n * A small floor keeps flat areas reading as \"watched\", not \"missing data\".\n */\nfunction normalize(raw: number[], mode: 'retention' | 'peaks'): number[] {\n if (raw.length === 0 || raw.every((v) => v <= 0)) return []\n const sm = smooth(raw)\n const max = mode === 'retention' ? percentile([...sm].sort((a, b) => a - b), 0.95) : Math.max(...sm)\n if (max <= 0) return []\n const FLOOR = 0.08\n return sm.map((v) => FLOOR + (1 - FLOOR) * Math.min(1, Math.max(0, v / max)))\n}\n\n/**\n * Sparse samples → normalized curve. Buckets the duration into `buckets` slots,\n * sums samples into them, then smooth+normalize as \"peaks\".\n */\nexport function buildHeatmapValues(points: HeatmapPoint[], duration: number, buckets = 100): number[] {\n if (points.length === 0 || !Number.isFinite(duration) || duration <= 0) return []\n const raw = new Array<number>(buckets).fill(0)\n let hasData = false\n for (const point of points) {\n if (!Number.isFinite(point.time) || point.time < 0 || point.time > duration) continue\n const value = Math.max(0, point.value)\n if (value === 0) continue\n raw[Math.min(buckets - 1, Math.floor((point.time / duration) * buckets))] += value\n hasData = true\n }\n if (!hasData) return []\n return normalize(raw, 'peaks')\n}\n\n/**\n * Dense per-bucket series (partner format) → normalized curve. Each value is one\n * equal time slot; no time field needed. `mode` picks the normalization.\n * Long feeds (2h video ≈ 1400 five-second buckets) are resampled down to\n * `maxBuckets` by averaging — the SVG spline stays light and smooth.\n */\nexport function buildHeatmapValuesDense(values: number[], mode: 'retention' | 'peaks' = 'peaks', maxBuckets = 200): number[] {\n let raw = values.map((v) => (Number.isFinite(v) ? Math.max(0, v) : 0))\n if (raw.length > maxBuckets) {\n const out = new Array<number>(maxBuckets).fill(0)\n const counts = new Array<number>(maxBuckets).fill(0)\n for (let i = 0; i < raw.length; i++) {\n const j = Math.min(maxBuckets - 1, Math.floor((i / raw.length) * maxBuckets))\n out[j] += raw[i]\n counts[j]++\n }\n raw = out.map((sum, j) => (counts[j] > 0 ? sum / counts[j] : 0))\n }\n return normalize(raw, mode)\n}\n\n/**\n * Build a SMOOTH SVG area path (Catmull-Rom → cubic Bézier) for normalized\n * values (0..1) in a `width`×`height` viewBox. Rendered with\n * `preserveAspectRatio=\"none\"`, so the numbers are arbitrary units, not pixels.\n */\nexport function heatmapPath(values: number[], smoothing = 1, width = 1000, height = 100): string {\n const n = values.length\n if (n === 0) return ''\n const step = width / (n - 1 || 1)\n // Catmull-Rom tangent length. `smoothing` 1 → the classic 1/6 (roundest);\n // 0 → straight segments. Clamped at the low end so it never goes negative.\n const k = Math.max(0, smoothing) / 6\n const at = (i: number): [number, number] => {\n const c = Math.min(n - 1, Math.max(0, i))\n return [c * step, height - values[c] * height]\n }\n\n const [, y0] = at(0)\n let d = `M 0 ${height} L 0 ${y0.toFixed(1)}`\n for (let i = 0; i < n - 1; i++) {\n const [x0p, y0p] = at(i - 1)\n const [x1, y1] = at(i)\n const [x2, y2] = at(i + 1)\n const [x3p, y3p] = at(i + 2)\n const cp1x = x1 + (x2 - x0p) * k\n // Clamp control-point y into the viewBox so the spline never overshoots\n // above the top edge (or below the baseline) on sharp value changes.\n const cp1y = Math.min(height, Math.max(0, y1 + (y2 - y0p) * k))\n const cp2x = x2 - (x3p - x1) * k\n const cp2y = Math.min(height, Math.max(0, y2 - (y3p - y1) * k))\n d += ` C ${cp1x.toFixed(1)} ${cp1y.toFixed(1)} ${cp2x.toFixed(1)} ${cp2y.toFixed(1)} ${x2.toFixed(1)} ${y2.toFixed(1)}`\n }\n d += ` L ${width} ${height} Z`\n return d\n}\n","import type { AdRoll, AdsOptions, ResolvedAd } from '../../types'\n\n/**\n * Minimal VAST 2/3/4 resolver — the same subset fluid-player consumes:\n * InLine Linear creatives, Wrapper redirects, MediaFiles, ClickThrough,\n * Impression and quartile tracking, skipoffset.\n */\nexport async function resolveVast(roll: AdRoll, opts: Required<Pick<AdsOptions, 'maxWrapperDepth' | 'requestTimeout'>>): Promise<ResolvedAd> {\n if (roll.src) {\n // Direct media file — no VAST round-trip needed.\n return {\n roll: roll.roll,\n mediaUrl: roll.src,\n clickThrough: roll.clickUrl,\n impressions: [],\n tracking: {},\n }\n }\n if (!roll.vastTag) throw new Error('Ad roll has neither \"vastTag\" nor \"src\"')\n return fetchAndParse(roll, roll.vastTag, opts, 0, { impressions: [], tracking: {} })\n}\n\ninterface Accumulated {\n impressions: string[]\n tracking: ResolvedAd['tracking']\n}\n\nasync function fetchAndParse(\n roll: AdRoll,\n tagUrl: string,\n opts: { maxWrapperDepth: number; requestTimeout: number },\n depth: number,\n acc: Accumulated,\n): Promise<ResolvedAd> {\n if (depth > opts.maxWrapperDepth) throw new Error('VAST wrapper depth limit exceeded')\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), opts.requestTimeout)\n let xmlText: string\n try {\n const res = await fetch(tagUrl, { signal: controller.signal, credentials: 'omit' })\n if (!res.ok) throw new Error(`VAST request failed (${res.status})`)\n xmlText = await res.text()\n } finally {\n clearTimeout(timer)\n }\n\n const doc = new DOMParser().parseFromString(xmlText, 'text/xml')\n if (doc.querySelector('parsererror')) throw new Error('VAST response is not valid XML')\n\n const ad = doc.querySelector('VAST > Ad')\n if (!ad) throw new Error('VAST response contains no ads')\n\n collectTracking(ad, acc)\n\n const wrapper = ad.querySelector(':scope > Wrapper')\n if (wrapper) {\n const nextTag = textOf(wrapper.querySelector('VASTAdTagURI'))\n if (!nextTag) throw new Error('VAST wrapper without VASTAdTagURI')\n return fetchAndParse(roll, nextTag, opts, depth + 1, acc)\n }\n\n const inline = ad.querySelector(':scope > InLine')\n if (!inline) throw new Error('VAST ad has neither InLine nor Wrapper')\n\n const linear = inline.querySelector('Creatives > Creative > Linear')\n if (!linear) throw new Error('VAST ad has no Linear creative')\n\n const media = pickMediaFile(linear)\n if (!media) throw new Error('VAST ad has no playable MediaFile')\n\n return {\n roll: roll.roll,\n mediaUrl: media.url,\n mediaType: media.type,\n clickThrough: textOf(linear.querySelector('VideoClicks > ClickThrough')) ?? roll.clickUrl,\n duration: parseDuration(textOf(linear.querySelector(':scope > Duration'))),\n skipOffset: parseSkipOffset(\n linear.getAttribute('skipoffset'),\n parseDuration(textOf(linear.querySelector(':scope > Duration'))),\n ),\n impressions: acc.impressions,\n tracking: acc.tracking,\n adTitle: textOf(inline.querySelector(':scope > AdTitle')) ?? undefined,\n }\n}\n\n/** Impressions, quartile tracking and click tracking accumulate across wrappers. */\nfunction collectTracking(ad: Element, acc: Accumulated): void {\n for (const node of ad.querySelectorAll('Impression')) {\n const url = textOf(node)\n if (url) acc.impressions.push(url)\n }\n for (const node of ad.querySelectorAll('Linear > TrackingEvents > Tracking')) {\n const event = node.getAttribute('event') as keyof ResolvedAd['tracking'] | null\n const url = textOf(node)\n if (!event || !url) continue\n if (!['start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'skip', 'pause', 'resume'].includes(event)) continue\n ;(acc.tracking[event] ??= []).push(url)\n }\n for (const node of ad.querySelectorAll('Linear > VideoClicks > ClickTracking')) {\n const url = textOf(node)\n if (url) (acc.tracking.click ??= []).push(url)\n }\n}\n\nfunction pickMediaFile(linear: Element): { url: string; type?: string } | null {\n const files = [...linear.querySelectorAll('MediaFiles > MediaFile')]\n .map((node) => ({\n url: textOf(node) ?? '',\n type: node.getAttribute('type') ?? undefined,\n delivery: node.getAttribute('delivery') ?? '',\n bitrate: Number(node.getAttribute('bitrate') ?? 0),\n apiFramework: node.getAttribute('apiFramework') ?? '',\n }))\n // VPAID needs a JS runtime we deliberately do not ship.\n .filter((f) => f.url && f.apiFramework.toUpperCase() !== 'VPAID')\n\n const playable = files.filter(\n (f) => !f.type || /(video\\/(mp4|webm|ogg)|application\\/(x-mpegurl|vnd\\.apple\\.mpegurl))/i.test(f.type),\n )\n if (playable.length === 0) return null\n // Mid-bitrate progressive file is the safest default.\n playable.sort((a, b) => a.bitrate - b.bitrate)\n const progressive = playable.filter((f) => f.delivery !== 'streaming')\n const pool = progressive.length > 0 ? progressive : playable\n const pick = pool[Math.floor(pool.length / 2)]\n return { url: pick.url, type: pick.type }\n}\n\n/** `\"00:00:15\"` / `\"00:15.250\"` → seconds. */\nfunction parseDuration(raw: string | null): number | undefined {\n if (!raw) return undefined\n const m = raw.trim().match(/^(?:(\\d+):)?(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?$/)\n if (!m) return undefined\n return (m[1] ? Number(m[1]) * 3600 : 0) + Number(m[2]) * 60 + Number(m[3]) + (m[4] ? Number(m[4].padEnd(3, '0')) / 1000 : 0)\n}\n\n/** skipoffset is either a timestamp or a percentage of the ad duration. */\nfunction parseSkipOffset(raw: string | null, duration: number | undefined): number | undefined {\n if (!raw) return undefined\n const pct = raw.trim().match(/^(\\d+(?:\\.\\d+)?)%$/)\n if (pct) return duration !== undefined ? (Number(pct[1]) / 100) * duration : undefined\n return parseDuration(raw)\n}\n\n/** Fire a tracking pixel without caring about the response. */\nexport function firePixels(urls: string[] | undefined): void {\n if (!urls) return\n for (const url of urls) {\n try {\n new Image().src = url\n } catch {\n /* tracking must never throw */\n }\n }\n}\n\nfunction textOf(node: Element | null): string | null {\n const text = node?.textContent?.trim().replace(/^<!\\[CDATA\\[|\\]\\]>$/g, '').trim()\n return text || null\n}\n","import { el, formatTime } from '../../core/dom'\nimport type { Emitter } from '../../core/events'\nimport type { AdRoll, AdsOptions, IconSource, PlayerEventMap, PlayerLabels, ResolvedAd } from '../../types'\nimport { firePixels, resolveVast } from './vast'\n\ninterface AdHost {\n container: HTMLElement\n contentVideo: HTMLVideoElement\n emitter: Emitter<PlayerEventMap>\n labels: PlayerLabels\n closeIcon: IconSource\n}\n\n/**\n * Plays pre/mid/post rolls in a dedicated <video> layered over the content.\n * The content element is never touched, so its position, HLS session and\n * text tracks survive every ad break.\n */\nexport class AdManager {\n private opts: Required<Pick<AdsOptions, 'skipDelay' | 'maxWrapperDepth' | 'requestTimeout' | 'mediaTimeout' | 'playOn'>> & AdsOptions\n private playedRolls = new Set<AdRoll>()\n private sourcesSeen = 0\n private activeBreak: Promise<void> | null = null\n private layer: HTMLElement | null = null\n private adVideo: HTMLVideoElement | null = null\n private destroyed = false\n\n /**\n * Create (once) and \"bless\" the ad <video> synchronously inside the user\n * gesture that started playback. A bare `load()` during the gesture lets\n * the later `play()` succeed even though VAST fetches happen in between —\n * the same trick every ad SDK uses for iOS/strict autoplay policies.\n */\n private ensureAdVideo(): HTMLVideoElement {\n if (!this.adVideo) {\n this.adVideo = el('video', 'imp-ad__video', { playsinline: '' })\n try {\n this.adVideo.load()\n } catch {\n /* some engines throw on load() without src — harmless */\n }\n }\n return this.adVideo\n }\n\n constructor(private host: AdHost, options: AdsOptions) {\n this.opts = {\n skipDelay: 5,\n maxWrapperDepth: 3,\n requestTimeout: 8000,\n mediaTimeout: 10000,\n playOn: 'every',\n ...options,\n }\n }\n\n get adPlaying(): boolean {\n return this.activeBreak !== null\n }\n\n /** True while a pre-roll is still due for the current source. */\n get hasPendingPreRoll(): boolean {\n return this.pending('preRoll').length > 0\n }\n\n /** Called on every source change. Honors the `playOn` frequency setting. */\n resetForNewSource(): void {\n this.sourcesSeen++\n if (this.opts.playOn === 'first' && this.sourcesSeen > 1) {\n // Ads ran (or had their chance) on the first video — mute them for the rest.\n for (const roll of this.opts.adList) this.playedRolls.add(roll)\n return\n }\n this.playedRolls.clear()\n }\n\n async playPreRoll(): Promise<void> {\n await this.playRolls(this.pending('preRoll'))\n }\n\n async playPostRoll(): Promise<void> {\n await this.playRolls(this.pending('postRoll'))\n }\n\n /** Called from timeupdate; fires due mid-rolls. */\n checkMidRolls(currentTime: number): void {\n if (this.adPlaying) return\n const due = this.pending('midRoll').filter((roll) => roll.timer !== undefined && currentTime >= roll.timer)\n if (due.length > 0) void this.playRolls(due)\n }\n\n private pending(kind: AdRoll['roll']): AdRoll[] {\n return this.opts.adList.filter((roll) => roll.roll === kind && !this.playedRolls.has(roll))\n }\n\n private async playRolls(rolls: AdRoll[]): Promise<void> {\n if (rolls.length === 0 || this.destroyed) return\n if (this.activeBreak) return this.activeBreak\n\n // Synchronously, while we may still be inside a user gesture.\n this.ensureAdVideo()\n\n const run = (async () => {\n const content = this.host.contentVideo\n const wasPlaying = !content.paused && !content.ended\n content.pause()\n for (const roll of rolls) {\n this.playedRolls.add(roll)\n try {\n const ad = await resolveVast(roll, this.opts)\n await this.playAd(ad)\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause))\n this.host.emitter.emit('aderror', { roll, error })\n }\n if (this.destroyed) return\n }\n if (wasPlaying || rolls[0].roll === 'preRoll') {\n void content.play().catch(() => {})\n }\n })()\n\n this.activeBreak = run.finally(() => {\n this.activeBreak = null\n })\n return this.activeBreak\n }\n\n private playAd(ad: ResolvedAd): Promise<void> {\n return new Promise<void>((resolve) => {\n const { labels } = this.host\n const layer = el('div', 'imp-ad')\n // One persistent (gesture-blessed) element, fresh listeners per ad.\n const video = this.ensureAdVideo()\n const ac = new AbortController()\n const signal = ac.signal\n video.src = ad.mediaUrl\n video.muted = this.host.contentVideo.muted\n video.volume = this.host.contentVideo.volume\n\n const spinner = el('div', 'imp-spinner imp-ad__spinner')\n\n const hud = el('div', 'imp-ad__hud')\n const badge = el('span', 'imp-ad__badge')\n badge.textContent = ad.adTitle ? `${labels.adLabel} · ${ad.adTitle}` : labels.adLabel\n const countdown = el('span', 'imp-ad__countdown')\n hud.append(badge, countdown)\n\n const actions = el('div', 'imp-ad__actions')\n let visitBtn: HTMLButtonElement | null = null\n if (ad.clickThrough) {\n visitBtn = el('button', 'imp-ad__visit', { type: 'button' })\n visitBtn.textContent = labels.visitAdvertiser\n actions.append(visitBtn)\n }\n const skipBtn = el('button', 'imp-ad__skip', { type: 'button' })\n skipBtn.hidden = true\n actions.append(skipBtn)\n\n layer.append(video, spinner, hud, actions)\n this.host.container.append(layer)\n this.layer = layer\n\n const skipAfter = ad.skipOffset ?? this.opts.skipDelay\n const fired = new Set<string>()\n const fireOnce = (event: keyof ResolvedAd['tracking']) => {\n if (fired.has(event)) return\n fired.add(event)\n firePixels(ad.tracking[event])\n }\n\n // Watchdog: a creative that never starts (dead-slow ad CDN) or stalls\n // mid-play must not hold the content hostage with a black screen.\n let lastProgress = Date.now()\n let lastTime = -1\n const watchdog = setInterval(() => {\n // A deliberately paused ad (click-through, tap-to-play) is not a stall.\n if (video.paused) {\n lastProgress = Date.now()\n return\n }\n if (video.currentTime !== lastTime) {\n lastTime = video.currentTime\n lastProgress = Date.now()\n return\n }\n if (Date.now() - lastProgress >= this.opts.mediaTimeout) {\n this.host.emitter.emit('aderror', {\n roll: { roll: ad.roll },\n error: new Error(`Ad media stalled for ${this.opts.mediaTimeout} ms — skipping`),\n })\n cleanup()\n }\n }, 500)\n\n const cleanup = () => {\n clearInterval(watchdog)\n ac.abort()\n video.pause()\n video.removeAttribute('src')\n video.load()\n layer.remove()\n this.layer = null\n resolve()\n }\n\n const finish = (skipped: boolean) => {\n if (skipped) {\n fireOnce('skip')\n this.host.emitter.emit('adskip', { ad })\n } else {\n fireOnce('complete')\n }\n this.host.emitter.emit('adend', { ad })\n cleanup()\n }\n\n video.addEventListener('playing', () => {\n firePixels(ad.impressions)\n fireOnce('start')\n this.host.emitter.emit('adstart', { ad })\n }, { once: true, signal })\n\n video.addEventListener('playing', () => {\n spinner.hidden = true\n }, { signal })\n video.addEventListener('waiting', () => {\n spinner.hidden = false\n }, { signal })\n\n video.addEventListener('timeupdate', () => {\n const t = video.currentTime\n const d = video.duration\n if (Number.isFinite(d) && d > 0) {\n countdown.textContent = formatTime(Math.max(0, d - t))\n if (t / d >= 0.25) fireOnce('firstQuartile')\n if (t / d >= 0.5) fireOnce('midpoint')\n if (t / d >= 0.75) fireOnce('thirdQuartile')\n }\n if (skipAfter >= 0) {\n const remaining = Math.ceil(skipAfter - t)\n if (remaining > 0) {\n skipBtn.hidden = false\n skipBtn.disabled = true\n skipBtn.textContent = `${labels.skipAdIn} ${remaining}`\n } else {\n skipBtn.hidden = false\n skipBtn.disabled = false\n skipBtn.textContent = labels.skipAd\n }\n }\n }, { signal })\n\n video.addEventListener('ended', () => finish(false), { signal })\n video.addEventListener('error', () => {\n this.host.emitter.emit('aderror', {\n roll: { roll: ad.roll },\n error: new Error('Ad media failed to play'),\n })\n cleanup()\n }, { signal })\n\n skipBtn.addEventListener('click', () => finish(true))\n\n const onAdClick = () => {\n if (!ad.clickThrough) return\n fireOnce('click')\n this.host.emitter.emit('adclick', { ad })\n window.open(ad.clickThrough, '_blank', 'noopener')\n video.pause()\n }\n video.addEventListener('click', onAdClick, { signal })\n visitBtn?.addEventListener('click', onAdClick)\n\n // pause / resume: player events + VAST tracking pixels. Only after the\n // creative actually started, and never for the teardown pause.\n let adStarted = false\n let pauseEmitted = false\n video.addEventListener('playing', () => {\n adStarted = true\n }, { once: true, signal })\n video.addEventListener('pause', () => {\n if (video.ended || !layer.isConnected) return\n layer.classList.add('imp-ad--paused')\n if (adStarted && !pauseEmitted) {\n pauseEmitted = true\n firePixels(ad.tracking.pause)\n this.host.emitter.emit('adpause', { ad })\n }\n }, { signal })\n video.addEventListener('play', () => {\n layer.classList.remove('imp-ad--paused')\n if (pauseEmitted) {\n pauseEmitted = false\n firePixels(ad.tracking.resume)\n this.host.emitter.emit('adresume', { ad })\n }\n }, { signal })\n layer.addEventListener('click', (e) => {\n if (e.target === layer || layer.classList.contains('imp-ad--paused')) {\n if (video.paused) void video.play().catch(() => {})\n }\n })\n\n void video.play().catch(() => {\n // Autoplay rejected (no gesture) — surface the ad with a tap-to-play state.\n layer.classList.add('imp-ad--paused')\n })\n })\n }\n\n /** Skip-button icon access for potential theming. */\n get closeIcon(): IconSource {\n return this.host.closeIcon\n }\n\n destroy(): void {\n this.destroyed = true\n this.adVideo?.pause()\n this.layer?.remove()\n this.layer = null\n this.adVideo = null\n }\n}\n","import { parseVttCues } from '../core/dom'\nimport type { Chapter } from '../types'\n\n/** Chapter with a guaranteed `end`, ready for rendering. */\nexport interface NormalizedChapter extends Chapter {\n end: number\n}\n\n/**\n * Sort chapters, fill missing `end` values from the next chapter (or video duration).\n * Out-of-range chapters are clamped, empty ones dropped.\n */\nexport function normalizeChapters(chapters: Chapter[], duration: number): NormalizedChapter[] {\n const sorted = [...chapters].sort((a, b) => a.start - b.start)\n const result: NormalizedChapter[] = []\n for (let i = 0; i < sorted.length; i++) {\n const start = Math.max(0, sorted[i].start)\n if (Number.isFinite(duration) && start >= duration) continue\n let end = sorted[i].end ?? sorted[i + 1]?.start ?? duration\n if (Number.isFinite(duration)) end = Math.min(end, duration)\n // NaN end (no explicit end AND no known duration yet) would slip through\n // the <= comparison and render a broken segment — drop it instead.\n if (!Number.isFinite(end) || end <= start) continue\n result.push({ start, end, title: sorted[i].title })\n }\n return result\n}\n\nexport async function loadChaptersVtt(url: string): Promise<Chapter[]> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`Failed to load chapters VTT (${res.status})`)\n const text = await res.text()\n return parseVttCues(text).map((cue) => ({ start: cue.start, end: cue.end, title: cue.text }))\n}\n\nexport function chapterAt(chapters: NormalizedChapter[], time: number): NormalizedChapter | null {\n for (const chapter of chapters) {\n if (time >= chapter.start && time < chapter.end) return chapter\n }\n return null\n}\n","import type Hls from 'hls.js'\n\n/** A selectable rendition (HLS level or progressive quality). */\nexport interface Level {\n /** Index understood by `setLevel`. `-1` is reserved for \"auto\". */\n index: number\n label: string\n}\n\n/** Wraps whatever is feeding the <video> so the player can switch quality and tear down. */\nexport interface SourceController {\n kind: 'hls' | 'native'\n levels: Level[]\n /** Currently *selected* level (-1 = auto). */\n selected: number\n setLevel(index: number): void\n destroy(): void\n}\n\nexport function isHlsSource(src: string, type?: string): boolean {\n if (type) return /application\\/(x-mpegurl|vnd\\.apple\\.mpegurl)/i.test(type)\n return /\\.m3u8(\\?|#|$)/i.test(src)\n}\n\nlet hlsModule: typeof Hls | null | undefined\n\nasync function loadHls(): Promise<typeof Hls | null> {\n if (hlsModule !== undefined) return hlsModule\n // Script-tag setups (IIFE bundle) provide hls.js as a global instead.\n const globalHls = (globalThis as { Hls?: typeof Hls }).Hls\n if (globalHls) {\n hlsModule = globalHls\n return hlsModule\n }\n try {\n // The \"light\" build drops rarely-needed features (alt-audio, subtitles,\n // EME, low-latency) — far smaller than the full build for the common case\n // of plain VOD m3u8. Consumers who need them can provide window.Hls\n // (full build) which is picked up above.\n const mod = await import('hls.js/light')\n hlsModule = mod.default\n return hlsModule\n } catch {\n // Do NOT cache the failure: a transient network hiccup on the chunk would\n // otherwise permanently break HLS for the rest of the page's lifetime.\n return null\n }\n}\n\nexport interface AttachCallbacks {\n onLevels(levels: Level[]): void\n onLevelSwitch(label: string): void\n onError(message: string, cause?: unknown): void\n}\n\n/**\n * Point the <video> at `src`. m3u8 goes through hls.js (or native HLS on Safari),\n * everything else is plain `video.src`.\n */\nexport async function attachSource(\n video: HTMLVideoElement,\n src: string,\n type: string | undefined,\n cb: AttachCallbacks,\n): Promise<SourceController> {\n if (isHlsSource(src, type)) {\n const native = video.canPlayType('application/vnd.apple.mpegurl')\n const HlsCtor = native ? null : await loadHls()\n if (HlsCtor && HlsCtor.isSupported()) {\n return attachHls(HlsCtor, video, src, cb)\n }\n if (native) {\n video.src = src\n return nativeController(video)\n }\n cb.onError('HLS is not supported in this browser and hls.js could not be loaded.')\n return nativeController(video)\n }\n video.src = src\n return nativeController(video)\n}\n\nfunction nativeController(video: HTMLVideoElement): SourceController {\n return {\n kind: 'native',\n levels: [],\n selected: -1,\n setLevel() {},\n destroy() {\n video.removeAttribute('src')\n video.load()\n },\n }\n}\n\nfunction attachHls(\n HlsCtor: typeof Hls,\n video: HTMLVideoElement,\n src: string,\n cb: AttachCallbacks,\n): SourceController {\n const hls = new HlsCtor({ enableWorker: true })\n const controller: SourceController = {\n kind: 'hls',\n levels: [],\n selected: -1,\n setLevel(index: number) {\n controller.selected = index\n hls.currentLevel = index\n },\n destroy() {\n hls.destroy()\n video.removeAttribute('src')\n video.load()\n },\n }\n\n hls.on(HlsCtor.Events.MANIFEST_PARSED, () => {\n controller.levels = hls.levels\n .map((level, index) => ({\n index,\n label: level.height ? `${level.height}p` : `${Math.round(level.bitrate / 1000)} kbps`,\n }))\n .reverse()\n cb.onLevels(controller.levels)\n })\n\n hls.on(HlsCtor.Events.LEVEL_SWITCHED, (_e, data) => {\n const level = hls.levels[data.level]\n if (level) cb.onLevelSwitch(level.height ? `${level.height}p` : `${Math.round(level.bitrate / 1000)} kbps`)\n })\n\n // Bounded recovery so a persistently broken stream can't loop forever, but\n // transient fatals (e.g. a layout/visibility change toggling buffering) get\n // a real chance to self-heal before we surface an error.\n let mediaRecoveries = 0\n let networkRecoveries = 0\n let otherRecovered = false\n\n // Any successfully buffered fragment = the network is alive again; give\n // future hiccups a fresh retry budget.\n hls.on(HlsCtor.Events.FRAG_BUFFERED, () => {\n networkRecoveries = 0\n })\n\n hls.on(HlsCtor.Events.ERROR, (_e, data) => {\n if (!data.fatal) return\n switch (data.type) {\n case HlsCtor.ErrorTypes.NETWORK_ERROR:\n // Bounded: a dead manifest (404/DNS) must SURFACE, not spin forever —\n // unbounded startLoad() kept the spinner up and the player's fatal\n // error overlay never appeared for missing HLS streams.\n if (networkRecoveries < 3) {\n networkRecoveries++\n hls.startLoad()\n } else {\n cb.onError(`HLS network error: ${data.details}`, data)\n hls.destroy()\n }\n break\n case HlsCtor.ErrorTypes.MEDIA_ERROR:\n if (mediaRecoveries < 3) {\n mediaRecoveries++\n hls.recoverMediaError()\n } else {\n cb.onError(`HLS media error: ${data.details}`, data)\n hls.destroy()\n }\n break\n default:\n // Try a single full reload before declaring the stream dead.\n if (!otherRecovered) {\n otherRecovered = true\n try {\n hls.loadSource(src)\n hls.startLoad()\n } catch {\n cb.onError(`HLS fatal error: ${data.details}`, data)\n hls.destroy()\n }\n } else {\n cb.onError(`HLS fatal error: ${data.details}`, data)\n hls.destroy()\n }\n }\n })\n\n hls.loadSource(src)\n hls.attachMedia(video)\n return controller\n}\n","import { parseVttCues, resolveUrl } from '../core/dom'\nimport type { ThumbnailCue } from '../types'\n\n/**\n * Preview thumbnails described by a WebVTT file. Supports both sprite sheets\n * (`sprite.jpg#xywh=160,0,160,90`) and one-image-per-cue tracks.\n */\nexport class ThumbnailTrack {\n private constructor(private cues: ThumbnailCue[]) {}\n\n static async load(vttUrl: string): Promise<ThumbnailTrack> {\n const res = await fetch(vttUrl)\n if (!res.ok) throw new Error(`Failed to load thumbnails VTT (${res.status})`)\n const text = await res.text()\n const cues: ThumbnailCue[] = []\n for (const cue of parseVttCues(text)) {\n const [rawSrc, fragment] = cue.text.trim().split('#')\n if (!rawSrc) continue\n const entry: ThumbnailCue = {\n start: cue.start,\n end: cue.end,\n src: resolveUrl(rawSrc, vttUrl),\n }\n const m = fragment?.match(/xywh=(\\d+),(\\d+),(\\d+),(\\d+)/)\n if (m) entry.xywh = { x: Number(m[1]), y: Number(m[2]), w: Number(m[3]), h: Number(m[4]) }\n cues.push(entry)\n }\n cues.sort((a, b) => a.start - b.start)\n return new ThumbnailTrack(cues)\n }\n\n cueAt(time: number): ThumbnailCue | null {\n // Binary search — thumbnail tracks for long videos can hold thousands of cues.\n let lo = 0\n let hi = this.cues.length - 1\n while (lo <= hi) {\n const mid = (lo + hi) >> 1\n const cue = this.cues[mid]\n if (time < cue.start) hi = mid - 1\n else if (time >= cue.end) lo = mid + 1\n else return cue\n }\n return null\n }\n}\n","import { applyIcon, el } from '../core/dom'\nimport type { IconSource } from '../types'\n\nexport interface MenuItem {\n label: string\n value: string\n active: boolean\n /** Optional icon — raw SVG (`{ svg }`) or image URL (`{ url }`). */\n icon?: IconSource\n}\n\nexport interface MenuSection {\n title: string\n items: MenuItem[]\n onSelect(value: string): void\n}\n\n/**\n * One row in the settings (gear) drill-down. Either a drill row (label + current\n * value → options) or a toggle row (label + on/off switch).\n */\nexport interface SettingEntry {\n key: string\n label: string\n /** Current value shown on a drill row, e.g. \"Auto\", \"1×\", \"Off\". */\n value?: string\n /** The options panel to drill into when a drill row is clicked. */\n section?(): MenuSection\n /** Present → render a toggle switch instead of a drill row. */\n toggle?: {\n value: boolean\n onChange(next: boolean): void\n }\n}\n\nconst CHEVRON_RIGHT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 6 6 6-6 6\"/></svg>'\nconst CHEVRON_LEFT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m15 6-6 6 6 6\"/></svg>'\n\n/**\n * Popup menu anchored above a control button. One instance per button;\n * rebuilt from data on every open, closed on outside click / Escape.\n */\nexport class Menu {\n readonly root: HTMLElement\n /** Dim layer behind the mobile bottom-sheet; tap to dismiss. Hidden on desktop. */\n private backdrop: HTMLElement\n private outsideListener: ((e: Event) => void) | null = null\n private escListener: ((e: KeyboardEvent) => void) | null = null\n\n constructor(private anchor: HTMLElement) {\n this.root = el('div', 'imp-menu')\n this.root.hidden = true\n this.backdrop = el('div', 'imp-menu__backdrop')\n this.backdrop.hidden = true\n this.backdrop.addEventListener('pointerdown', (e) => {\n e.preventDefault()\n this.close()\n })\n }\n\n get open(): boolean {\n return !this.root.hidden\n }\n\n toggle(sections: MenuSection[]): void {\n if (this.open) this.close()\n else this.show(sections)\n }\n\n show(sections: MenuSection[]): void {\n this.root.textContent = ''\n for (const section of sections) {\n if (section.items.length === 0) continue\n this.root.append(this.renderSection(section))\n }\n this.reveal()\n }\n\n /** Settings (gear) drill-down: a root list of rows, each opening its options. */\n toggleSettings(title: string, entries: SettingEntry[]): void {\n if (this.open) this.close()\n else this.showSettings(title, entries)\n }\n\n showSettings(_title: string, entries: SettingEntry[]): void {\n this.root.classList.add('imp-menu--wide')\n this.renderSettingsRoot(entries)\n this.reveal()\n }\n\n private renderSettingsRoot(entries: SettingEntry[]): void {\n this.root.textContent = ''\n const block = el('div', 'imp-menu__section')\n for (const entry of entries) {\n if (entry.toggle) {\n block.append(this.buildToggleRow(entry, entry.toggle))\n continue\n }\n const row = el('button', 'imp-menu__item imp-menu__row', { type: 'button' })\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n const value = el('span', 'imp-menu__value')\n value.textContent = entry.value ?? ''\n const chevron = el('span', 'imp-menu__chevron')\n chevron.innerHTML = CHEVRON_RIGHT\n row.append(label, value, chevron)\n row.addEventListener('click', () => this.renderSettingsDetail(entries, entry))\n block.append(row)\n }\n this.root.append(block)\n }\n\n /** Toggle row: label + an on/off switch. Stays in the menu (no close). */\n private buildToggleRow(entry: SettingEntry, toggle: NonNullable<SettingEntry['toggle']>): HTMLElement {\n const row = el('button', 'imp-menu__item imp-menu__row imp-menu__toggle', {\n type: 'button', role: 'switch', 'aria-checked': String(toggle.value),\n })\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n const sw = el('span', 'imp-switch')\n sw.append(el('span', 'imp-switch__knob'))\n row.classList.toggle('imp-menu__toggle--on', toggle.value)\n row.append(label, sw)\n row.addEventListener('click', () => {\n const next = row.getAttribute('aria-checked') !== 'true'\n row.setAttribute('aria-checked', String(next))\n row.classList.toggle('imp-menu__toggle--on', next)\n toggle.onChange(next)\n })\n return row\n }\n\n private renderSettingsDetail(entries: SettingEntry[], entry: SettingEntry): void {\n if (!entry.section) return // toggle rows have no drill-down panel\n this.root.textContent = ''\n const back = el('button', 'imp-menu__item imp-menu__back', { type: 'button' })\n const chevron = el('span', 'imp-menu__chevron')\n chevron.innerHTML = CHEVRON_LEFT\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n back.append(chevron, label)\n back.addEventListener('click', () => this.renderSettingsRoot(entries))\n this.root.append(back)\n this.root.append(this.renderSection(entry.section()))\n this.root.scrollTop = 0\n }\n\n /** Build one option block (radio items) from a section. */\n private renderSection(section: MenuSection): HTMLElement {\n const block = el('div', 'imp-menu__section')\n if (section.title) {\n const title = el('div', 'imp-menu__title')\n title.textContent = section.title\n block.append(title)\n }\n for (const item of section.items) {\n const btn = el('button', 'imp-menu__item', { type: 'button', role: 'menuitemradio' })\n btn.setAttribute('aria-checked', String(item.active))\n if (item.active) btn.classList.add('imp-menu__item--active')\n if (item.icon) {\n const iconBox = el('span', 'imp-menu__icon')\n applyIcon(iconBox, item.icon)\n btn.append(iconBox)\n }\n const text = el('span', 'imp-menu__label')\n text.textContent = item.label\n btn.append(text)\n btn.addEventListener('click', () => {\n section.onSelect(item.value)\n this.close()\n })\n block.append(btn)\n }\n return block\n }\n\n /** Reveal the populated menu + backdrop and arm outside-click / Escape. */\n private reveal(): void {\n // The backdrop lives NEXT to the menu root (not inside the anchor button —\n // a div inside <button> is invalid HTML and a backdrop tap would re-trigger\n // the button, closing and instantly reopening the menu).\n const host = this.root.parentElement\n if (host && this.backdrop.parentElement !== host) host.insertBefore(this.backdrop, this.root)\n this.root.hidden = false\n this.backdrop.hidden = false\n this.outsideListener = (e: Event) => {\n const target = e.target as Node\n if (!this.root.contains(target) && !this.anchor.contains(target)) this.close()\n }\n this.escListener = (e: KeyboardEvent) => {\n if (e.key === 'Escape') this.close()\n }\n document.addEventListener('keydown', this.escListener)\n // Defer so the click that opened the menu does not immediately close it.\n setTimeout(() => {\n if (this.outsideListener) document.addEventListener('pointerdown', this.outsideListener)\n }, 0)\n }\n\n close(): void {\n this.root.hidden = true\n this.root.classList.remove('imp-menu--wide')\n this.backdrop.hidden = true\n if (this.outsideListener) {\n document.removeEventListener('pointerdown', this.outsideListener)\n this.outsideListener = null\n }\n if (this.escListener) {\n document.removeEventListener('keydown', this.escListener)\n this.escListener = null\n }\n }\n\n destroy(): void {\n this.close()\n this.backdrop.remove()\n this.root.remove()\n }\n}\n","import { clamp, el, formatTime } from '../core/dom'\nimport type { NormalizedChapter } from '../features/chapters'\nimport { chapterAt } from '../features/chapters'\nimport { heatmapPath } from '../features/heatmap'\nimport type { ThumbnailTrack } from '../features/thumbnails'\n\ninterface ProgressCallbacks {\n onSeek(time: number): void\n onScrubStart(): void\n onScrubEnd(): void\n}\n\ninterface Segment {\n chapter: NormalizedChapter\n root: HTMLElement\n fill: HTMLElement\n}\n\n/**\n * Seek bar. Chapters render as flex segments (YouTube-style), each with its\n * own fill — no absolutely positioned tick marks.\n */\nexport class ProgressBar {\n readonly root: HTMLElement\n private track: HTMLElement\n private buffered: HTMLElement\n private segments: Segment[] = []\n private handle: HTMLElement\n private heatmap!: HTMLElement\n private tooltip: HTMLElement\n private tooltipThumb: HTMLElement\n private tooltipCaption: HTMLElement\n private tooltipChapter: HTMLElement\n private tooltipTime: HTMLElement\n\n private duration = 0\n private chapters: NormalizedChapter[] = []\n private thumbnails: ThumbnailTrack | null = null\n private scrubbing = false\n\n constructor(private cb: ProgressCallbacks, ariaLabel = 'Seek') {\n this.root = el('div', 'imp-progress', { role: 'slider', 'aria-label': ariaLabel, tabindex: '-1' })\n this.buffered = el('div', 'imp-progress__buffered')\n this.track = el('div', 'imp-progress__track')\n this.handle = el('div', 'imp-progress__handle')\n\n this.tooltipThumb = el('div', 'imp-progress__thumb')\n this.tooltipChapter = el('div', 'imp-progress__tooltip-chapter')\n this.tooltipTime = el('div', 'imp-progress__tooltip-time')\n // Caption row overlaid on the preview: scene name left, time right.\n this.tooltipCaption = el('div', 'imp-progress__caption')\n this.tooltipCaption.append(this.tooltipChapter, this.tooltipTime)\n this.tooltip = el('div', 'imp-progress__tooltip')\n this.tooltip.append(this.tooltipThumb, this.tooltipCaption)\n\n this.heatmap = el('div', 'imp-progress__heatmap')\n this.heatmap.hidden = true\n\n this.root.append(this.heatmap, this.buffered, this.track, this.handle, this.tooltip)\n this.setChapters([])\n this.bindPointer()\n }\n\n /** Render the popularity curve (empty array hides it). */\n setHeatmap(values: number[], smoothing = 1): void {\n if (values.length === 0) {\n this.heatmap.hidden = true\n this.heatmap.textContent = ''\n return\n }\n this.heatmap.innerHTML =\n `<svg viewBox=\"0 0 1000 100\" preserveAspectRatio=\"none\" aria-hidden=\"true\">` +\n `<path d=\"${heatmapPath(values, smoothing)}\"/></svg>`\n this.heatmap.hidden = false\n }\n\n setDuration(duration: number): void {\n this.duration = Number.isFinite(duration) ? duration : 0\n this.root.classList.toggle('imp-progress--live', !Number.isFinite(duration))\n if (this.chapters.length === 0) this.setChapters([])\n }\n\n setChapters(chapters: NormalizedChapter[]): void {\n this.chapters = chapters\n this.track.textContent = ''\n this.segments = []\n const list: NormalizedChapter[] = chapters.length > 0\n ? chapters\n : [{ start: 0, end: this.duration || 1, title: '' }]\n // Fill gaps between chapters so the bar always spans the full duration.\n const filled: NormalizedChapter[] = []\n let cursor = 0\n for (const ch of list) {\n if (ch.start > cursor) filled.push({ start: cursor, end: ch.start, title: '' })\n filled.push(ch)\n cursor = ch.end\n }\n if (this.duration > 0 && cursor < this.duration) {\n filled.push({ start: cursor, end: this.duration, title: '' })\n }\n for (const chapter of filled) {\n const root = el('div', 'imp-progress__segment')\n root.style.flexGrow = String(Math.max(chapter.end - chapter.start, 0.01))\n const fill = el('div', 'imp-progress__fill')\n root.append(fill)\n this.track.append(root)\n this.segments.push({ chapter, root, fill })\n }\n }\n\n setThumbnails(track: ThumbnailTrack | null): void {\n this.thumbnails = track\n }\n\n update(currentTime: number, duration: number, bufferedEnd: number): void {\n if (duration !== this.duration && Number.isFinite(duration)) this.setDuration(duration)\n if (this.scrubbing) return\n this.render(currentTime)\n const ratio = this.duration > 0 ? clamp(bufferedEnd / this.duration, 0, 1) : 0\n this.buffered.style.width = `${ratio * 100}%`\n this.root.setAttribute('aria-valuemin', '0')\n this.root.setAttribute('aria-valuemax', String(Math.round(this.duration)))\n this.root.setAttribute('aria-valuenow', String(Math.round(currentTime)))\n this.root.setAttribute('aria-valuetext', `${formatTime(currentTime)} / ${formatTime(this.duration)}`)\n }\n\n private render(time: number): void {\n for (const { chapter, fill } of this.segments) {\n const span = chapter.end - chapter.start\n const progress = span > 0 ? clamp((time - chapter.start) / span, 0, 1) : 0\n fill.style.transform = `scaleX(${progress})`\n }\n const ratio = this.duration > 0 ? clamp(time / this.duration, 0, 1) : 0\n this.handle.style.left = `${ratio * 100}%`\n }\n\n private timeFromEvent(e: PointerEvent): number {\n const rect = this.root.getBoundingClientRect()\n const ratio = rect.width > 0 ? clamp((e.clientX - rect.left) / rect.width, 0, 1) : 0\n return ratio * this.duration\n }\n\n private bindPointer(): void {\n this.root.addEventListener('pointerdown', (e) => {\n if (this.duration <= 0) return\n e.preventDefault()\n this.scrubbing = true\n this.root.classList.add('imp-progress--scrubbing')\n this.root.setPointerCapture(e.pointerId)\n this.cb.onScrubStart()\n this.render(this.timeFromEvent(e))\n this.showTooltip(e)\n })\n this.root.addEventListener('pointermove', (e) => {\n if (this.duration <= 0) return\n this.showTooltip(e)\n if (this.scrubbing) this.render(this.timeFromEvent(e))\n })\n this.root.addEventListener('pointerup', (e) => {\n if (!this.scrubbing) return\n this.scrubbing = false\n this.root.classList.remove('imp-progress--scrubbing')\n this.cb.onSeek(this.timeFromEvent(e))\n this.cb.onScrubEnd()\n })\n this.root.addEventListener('pointercancel', () => {\n this.scrubbing = false\n this.root.classList.remove('imp-progress--scrubbing')\n this.cb.onScrubEnd()\n })\n this.root.addEventListener('pointerleave', () => this.hideTooltip())\n }\n\n private showTooltip(e: PointerEvent): void {\n const time = this.timeFromEvent(e)\n this.tooltipTime.textContent = formatTime(time)\n const chapter = chapterAt(this.chapters, time)\n const hasChapter = Boolean(chapter?.title)\n this.tooltipChapter.textContent = chapter?.title ?? ''\n this.tooltipChapter.hidden = !hasChapter\n // No scene name → center the time alone.\n this.tooltipCaption.classList.toggle('imp-progress__caption--time-only', !hasChapter)\n\n const cue = this.thumbnails?.cueAt(time) ?? null\n // Caption overlays the preview frame only when there is one.\n this.tooltip.classList.toggle('imp-progress__tooltip--thumb', Boolean(cue))\n if (cue) {\n this.tooltipThumb.hidden = false\n this.tooltipThumb.style.backgroundImage = `url(\"${cue.src}\")`\n if (cue.xywh) {\n this.tooltipThumb.style.width = `${cue.xywh.w}px`\n this.tooltipThumb.style.height = `${cue.xywh.h}px`\n this.tooltipThumb.style.backgroundPosition = `-${cue.xywh.x}px -${cue.xywh.y}px`\n this.tooltipThumb.style.backgroundSize = 'auto'\n } else {\n this.tooltipThumb.style.width = '160px'\n this.tooltipThumb.style.height = '90px'\n this.tooltipThumb.style.backgroundPosition = 'center'\n this.tooltipThumb.style.backgroundSize = 'cover'\n }\n } else {\n this.tooltipThumb.hidden = true\n }\n\n this.tooltip.classList.add('imp-progress__tooltip--visible')\n const rect = this.root.getBoundingClientRect()\n const x = clamp(e.clientX - rect.left, 0, rect.width)\n const half = this.tooltip.offsetWidth / 2\n this.tooltip.style.left = `${clamp(x, half, Math.max(half, rect.width - half))}px`\n }\n\n private hideTooltip(): void {\n this.tooltip.classList.remove('imp-progress__tooltip--visible')\n }\n}\n","import { applyIcon, coverImg, el, formatTime, iconButton, previewMetaEl, setIcon } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\nimport type { IconSource, NextPreviewOptions, SeekLabelFormat, SettingPlacement } from '../types'\nimport { Menu, type MenuSection, type SettingEntry } from './menu'\nimport { ProgressBar } from './progress'\n\n/**\n * A control that can be collapsed into the ⋯ overflow menu when the bar runs\n * out of room. `el` is the element hidden in the bar; `section()` returns what\n * the ⋯ menu shows in its place (or null when currently unavailable).\n */\ninterface Collapsible {\n key: string\n el: HTMLElement\n /** Lower = collapses first (least important to keep as a dedicated button). */\n priority: number\n available(): boolean\n /** What the ⋯ menu shows in its place — one section, several, or none. */\n section(): MenuSection | MenuSection[] | null\n}\n\n/**\n * Control bar. A single flex row inside the overlay grid. When the player is\n * too narrow, lower-priority controls collapse into the ⋯ menu dynamically\n * (ResizeObserver-driven) so nothing ever overflows the player edge.\n */\nexport class Controls {\n readonly root: HTMLElement\n readonly progress: ProgressBar\n\n private playBtn!: HTMLButtonElement\n private muteBtn!: HTMLButtonElement\n private volumeSlider!: HTMLInputElement\n private timeLabel!: HTMLElement\n private timeCurrent!: HTMLElement\n private timeTotal!: HTMLElement\n private chapterLabel!: HTMLElement\n private liveBadge!: HTMLElement\n private fullscreenBtn!: HTMLButtonElement\n private row!: HTMLElement\n private rightGroup!: HTMLElement\n private seekLabelFn?: SeekLabelFormat\n /** Right-cluster controls keyed for ordering; appended in `controls.order`. */\n private rightItems = new Map<string, HTMLElement>()\n\n private subtitlesBtn: HTMLButtonElement | null = null\n private settingsBtn: HTMLButtonElement | null = null\n private settingsMenu: Menu | null = null\n private subtitlesMenu: Menu | null = null\n private moreMenu: Menu | null = null\n private moreWrap: HTMLElement | null = null\n private qualityBtn: HTMLButtonElement | null = null\n private qualityMenu: Menu | null = null\n /** Unified ⚙ dropdown holding the settings placed in `\"gear\"`. */\n private gearBtn: HTMLButtonElement | null = null\n private gearMenu: Menu | null = null\n /** Which settings live in the gear menu (vs their own bar button / off). */\n private gear = { speed: false, quality: false, subtitles: false }\n /** Next-up hover preview (desktop). */\n private nextPreviewEl: HTMLElement | null = null\n private nextPreviewOpts: Required<NextPreviewOptions> | null = null\n private scenesBtn: HTMLButtonElement | null = null\n private sceneTypeBtn: HTMLButtonElement | null = null\n private sceneTypeMenu: Menu | null = null\n private likeBtn: HTMLButtonElement | null = null\n private dislikeBtn: HTMLButtonElement | null = null\n private likeCountEl: HTMLElement | null = null\n private dislikeCountEl: HTMLElement | null = null\n private prevBtn: HTMLButtonElement | null = null\n private nextBtn: HTMLButtonElement | null = null\n private seekBackBtn: HTMLButtonElement | null = null\n private seekFwdBtn: HTMLButtonElement | null = null\n\n /** Center overlay cluster (prev · −Ns · play · +Ns · next), shown on mobile. */\n readonly center: HTMLElement\n private centerPlayBtn: HTMLButtonElement | null = null\n private centerPrevBtn: HTMLButtonElement | null = null\n private centerNextBtn: HTMLButtonElement | null = null\n\n private collapsibles: Collapsible[] = []\n private overflowed = new Set<string>()\n private resizeObserver: ResizeObserver | null = null\n private reflowScheduled = false\n /** Consumer actions (share/report/…) always live in the ⋯ menu. */\n private actionItems: Array<{ value: string; label: string; icon?: IconSource; run(): void }> = []\n\n private wasPlayingBeforeScrub = false\n private disposers: Array<() => void> = []\n\n constructor(private player: Player) {\n const { labels, icons } = player\n const c = player.controlsOptions\n\n this.root = el('div', 'imp-controls')\n\n this.progress = new ProgressBar({\n onSeek: (t) => player.seek(t),\n onScrubStart: () => {\n // The flag keeps the pause handler from showing the pause screen\n // mid-drag (the scrub pauses the video under the hood).\n player.scrubbing = true\n this.wasPlayingBeforeScrub = !player.paused\n player.pause()\n },\n onScrubEnd: () => {\n player.scrubbing = false\n // skipAds: releasing the scrubber must never trigger a pre-roll.\n if (this.wasPlayingBeforeScrub) void player.play(true)\n // Before the first play the bar is seeded with the declared duration —\n // releasing the scrubber there means \"start from this spot\" (this one\n // is a real start, so ads run as usual).\n else if (!player.started) void player.play()\n },\n }, player.labels.seek)\n if (c.progress) this.root.append(this.progress.root)\n\n const row = el('div', 'imp-controls__row')\n this.row = row\n this.root.append(row)\n\n const left = el('div', 'imp-controls__group')\n const right = el('div', 'imp-controls__group')\n this.rightGroup = right\n const spacer = el('div', 'imp-controls__spacer')\n\n this.chapterLabel = el('div', 'imp-controls__chapter')\n row.append(left, spacer, right)\n spacer.append(this.chapterLabel)\n\n // Seek step values (used by both buttons and the ⋯ fallback items).\n const seekCfg = typeof c.seekButtons === 'object' ? c.seekButtons : {}\n const seekBack = seekCfg.back ?? player.seekStep\n const seekFwd = seekCfg.forward ?? player.seekStep\n this.seekLabelFn = seekCfg.label\n\n // --- left cluster: prev · −Ns · play · +Ns · next · volume · time ---\n // The bar prev button is opt-in (hidePrev defaults true → bar shows only\n // next). The mobile center cluster still gets its own prev regardless.\n if (c.playlist && player.hasPlaylist && !c.hidePrev) {\n this.prevBtn = iconButton('imp-btn--prev', labels.previous, icons.previous)\n this.prevBtn.addEventListener('click', () => player.previous())\n left.append(this.prevBtn)\n }\n\n if (c.seekButtons) {\n this.seekBackBtn = iconButton('imp-btn--seek-back', `${labels.seekBack} ${seekBack}s`, icons.seekBack)\n this.addStepBadge(this.seekBackBtn, seekBack, 'back')\n this.seekBackBtn.addEventListener('click', () => player.skip(-seekBack))\n left.append(this.seekBackBtn)\n }\n\n if (c.play) {\n this.playBtn = iconButton('imp-btn--play', labels.play, icons.play)\n this.playBtn.addEventListener('click', () => player.togglePlay())\n left.append(this.playBtn)\n }\n\n if (c.seekButtons) {\n this.seekFwdBtn = iconButton('imp-btn--seek-forward', `${labels.seekForward} ${seekFwd}s`, icons.seekForward)\n this.addStepBadge(this.seekFwdBtn, seekFwd, 'forward')\n this.seekFwdBtn.addEventListener('click', () => player.skip(seekFwd))\n left.append(this.seekFwdBtn)\n }\n\n if (c.playlist && player.hasPlaylist && !c.hideNext) {\n this.nextBtn = iconButton('imp-btn--next', labels.next, icons.next)\n this.nextBtn.addEventListener('click', () => player.next())\n left.append(this.nextBtn)\n }\n\n if (c.volume) {\n const volumeWrap = el('div', 'imp-volume')\n this.muteBtn = iconButton('imp-btn--mute', labels.mute, icons.volumeHigh)\n this.muteBtn.addEventListener('click', () => player.toggleMute())\n this.volumeSlider = el('input', 'imp-volume__slider', {\n type: 'range', min: '0', max: '1', step: '0.05', 'aria-label': labels.volume,\n })\n this.volumeSlider.addEventListener('input', () => {\n player.setVolume(Number(this.volumeSlider.value))\n })\n volumeWrap.append(this.muteBtn, this.volumeSlider)\n left.append(volumeWrap)\n }\n\n if (c.time) {\n this.timeLabel = el('div', 'imp-controls__time')\n // Split so the total (slash + duration) can be de-emphasized vs current time.\n this.timeCurrent = el('span', 'imp-controls__time-current')\n this.timeTotal = el('span', 'imp-controls__time-total')\n this.timeLabel.append(this.timeCurrent, this.timeTotal)\n this.liveBadge = el('span', 'imp-controls__live')\n this.liveBadge.textContent = labels.live\n this.liveBadge.hidden = true\n left.append(this.timeLabel, this.liveBadge)\n }\n\n // --- right cluster -------------------------------------------------\n this.buildLikeDislike()\n\n // Consumer actions placed directly in the bar (icon button, tooltip = title).\n for (const ca of (player.actionsOptions.custom ?? []).filter((a) => a.placement === 'bar')) {\n const btn = iconButton(`imp-btn--custom imp-btn--custom-${ca.id}`, ca.title, ca.icon ?? icons.more)\n btn.addEventListener('click', () => player.emit('customaction', { id: ca.id }))\n this.rightItems.set(`custom:${ca.id}`, btn)\n this.registerCollapsible({\n key: `custom:${ca.id}`, el: btn, priority: 55, available: () => true,\n section: () => this.simpleSection(`custom:${ca.id}`, ca.title, ca.icon ?? icons.more, () => player.emit('customaction', { id: ca.id })),\n })\n }\n\n // Settings placement: subtitles / quality / speed each live in the gear\n // dropdown ('gear', default), as their own bar button ('bar'), or off.\n const place = (v: SettingPlacement | undefined): 'gear' | 'bar' | 'off' =>\n v === false ? 'off' : v === 'bar' ? 'bar' : 'gear'\n const subsPlace = place(c.subtitles)\n const qualityPlace = place(c.quality)\n const speedPlace = place(c.speed)\n this.gear = { speed: speedPlace === 'gear', quality: qualityPlace === 'gear', subtitles: subsPlace === 'gear' }\n\n // Standalone (bar) settings buttons — only when explicitly pulled out.\n if (subsPlace === 'bar') {\n this.subtitlesBtn = iconButton('imp-btn--subtitles', labels.subtitles, icons.subtitles)\n this.subtitlesMenu = new Menu(this.subtitlesBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.subtitlesBtn, this.subtitlesMenu.root)\n this.subtitlesBtn.addEventListener('click', () => this.toggleSubtitlesMenu())\n this.rightItems.set('subtitles', wrap)\n this.registerCollapsible({\n key: 'subtitles', el: wrap, priority: 30,\n available: () => player.subtitleTracks.length > 0,\n section: () => this.subtitlesSection(),\n })\n }\n if (qualityPlace === 'bar') {\n this.qualityBtn = iconButton('imp-btn--quality', labels.quality, icons.settings)\n this.qualityMenu = new Menu(this.qualityBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.qualityBtn, this.qualityMenu.root)\n this.qualityBtn.addEventListener('click', () => this.toggleQualityMenu())\n this.rightItems.set('quality', wrap)\n this.registerCollapsible({\n key: 'quality', el: wrap, priority: 10,\n available: () => player.qualityLevels.length > 0,\n section: () => this.qualitySection(),\n })\n }\n if (speedPlace === 'bar') {\n this.settingsBtn = iconButton('imp-btn--speed', labels.speed, icons.speed)\n this.settingsMenu = new Menu(this.settingsBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.settingsBtn, this.settingsMenu.root)\n this.settingsBtn.addEventListener('click', () => this.toggleSettingsMenu())\n this.rightItems.set('speed', wrap)\n this.registerCollapsible({\n key: 'speed', el: wrap, priority: 40,\n available: () => true,\n section: () => this.speedSection(),\n })\n }\n\n if (c.scenes) {\n this.scenesBtn = iconButton('imp-btn--scenes', labels.scenes, icons.scenes)\n this.scenesBtn.addEventListener('click', () => player.toggleScenesPanel())\n this.rightItems.set('scenes', this.scenesBtn)\n this.registerCollapsible({\n key: 'scenes', el: this.scenesBtn, priority: 25,\n available: () => player.chapterList.length > 0,\n section: () => this.simpleSection('scenes', labels.scenes, icons.scenes, () => player.toggleScenesPanel()),\n })\n }\n\n // Scene-type selector (icon + current type label, dropdown). Visible only\n // when the source carries typed scene groups.\n if (c.sceneTypes !== false) {\n this.sceneTypeBtn = el('button', 'imp-btn imp-btn--scene-types', { type: 'button' })\n this.sceneTypeMenu = new Menu(this.sceneTypeBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.sceneTypeBtn, this.sceneTypeMenu.root)\n this.sceneTypeBtn.addEventListener('click', () => this.toggleSceneTypeMenu())\n this.rightItems.set('sceneTypes', wrap)\n this.refreshSceneTypeButton()\n // High priority: the scene-type selector is a headline feature — on a\n // narrow mobile bar it must NOT be an early collapse into ⋯ (users read\n // that as \"the button is missing\"). Only prev/next/seek outrank it.\n this.registerCollapsible({\n key: 'sceneTypes', el: wrap, priority: 68,\n available: () => player.sceneTypes.length > 0,\n section: () => this.sceneTypeSection(),\n })\n }\n\n if (c.playlist && player.hasPlaylist && c.playlistButton !== false) {\n const listBtn = iconButton('imp-btn--list', labels.playlist, icons.list)\n listBtn.addEventListener('click', () => player.togglePlaylistPanel())\n this.rightItems.set('playlist', listBtn)\n this.registerCollapsible({\n key: 'list', el: listBtn, priority: 50,\n available: () => true,\n section: () => this.simpleSection('list', labels.playlist, icons.list, () => player.togglePlaylistPanel()),\n })\n }\n\n if (c.pip && 'requestPictureInPicture' in HTMLVideoElement.prototype) {\n const pipBtn = iconButton('imp-btn--pip', labels.pip, icons.pip)\n pipBtn.addEventListener('click', () => void player.togglePip())\n this.rightItems.set('pip', pipBtn)\n this.registerCollapsible({\n key: 'pip', el: pipBtn, priority: 20,\n available: () => true,\n section: () => this.simpleSection('pip', labels.pip, icons.pip, () => void player.togglePip()),\n })\n }\n\n if (c.fullscreen) {\n this.fullscreenBtn = iconButton('imp-btn--fullscreen', labels.fullscreen, icons.fullscreen)\n this.fullscreenBtn.addEventListener('click', () => void player.toggleFullscreen())\n this.rightItems.set('fullscreen', this.fullscreenBtn)\n }\n\n // Playlist nav and seek can also collapse (lower priority kept than submenus).\n if (this.prevBtn) {\n this.registerCollapsible({\n key: 'prev', el: this.prevBtn, priority: 70,\n available: () => true,\n section: () => player.hasPrevious ? this.simpleSection('prev', labels.previous, icons.previous, () => player.previous()) : null,\n })\n }\n if (this.nextBtn) {\n this.registerCollapsible({\n key: 'next', el: this.nextBtn, priority: 72,\n available: () => true,\n section: () => player.hasNext ? this.simpleSection('next', labels.next, icons.next, () => player.next()) : null,\n })\n }\n if (this.seekBackBtn) {\n const b = this.seekBackBtn\n this.registerCollapsible({\n key: 'seekBack', el: b, priority: 80,\n available: () => Number.isFinite(player.duration) && player.duration > 0,\n section: () => this.simpleSection('seekBack', `${labels.seekBack} ${seekBack}s`, icons.seekBack, () => player.skip(-seekBack)),\n })\n }\n if (this.seekFwdBtn) {\n const f = this.seekFwdBtn\n this.registerCollapsible({\n key: 'seekFwd', el: f, priority: 82,\n available: () => Number.isFinite(player.duration) && player.duration > 0,\n section: () => this.simpleSection('seekFwd', `${labels.seekForward} ${seekFwd}s`, icons.seekForward, () => player.skip(seekFwd)),\n })\n }\n\n // --- center overlay cluster (prev · −Ns · play · +Ns · next) -----------\n // Shown on mobile in the middle of the video; the matching bottom-bar\n // buttons are hidden there. Big tap targets, less crowded bar.\n this.center = el('div', 'imp-center-controls')\n if (c.playlist && player.hasPlaylist) {\n this.centerPrevBtn = this.makeCenterButton('prev', labels.previous, icons.previous)\n this.centerPrevBtn.addEventListener('click', () => player.previous())\n this.center.append(this.centerPrevBtn)\n }\n if (c.seekButtons) {\n const b = this.makeCenterButton('seek-back', `${labels.seekBack} ${seekBack}s`, icons.seekBack)\n this.addStepBadge(b, seekBack, 'back')\n b.addEventListener('click', () => player.skip(-seekBack))\n this.center.append(b)\n }\n if (c.play) {\n this.centerPlayBtn = this.makeCenterButton('play', labels.play, icons.play)\n this.centerPlayBtn.addEventListener('click', () => player.togglePlay())\n this.center.append(this.centerPlayBtn)\n }\n if (c.seekButtons) {\n const f = this.makeCenterButton('seek-forward', `${labels.seekForward} ${seekFwd}s`, icons.seekForward)\n this.addStepBadge(f, seekFwd, 'forward')\n f.addEventListener('click', () => player.skip(seekFwd))\n this.center.append(f)\n }\n if (c.playlist && player.hasPlaylist) {\n this.centerNextBtn = this.makeCenterButton('next', labels.next, icons.next)\n this.centerNextBtn.addEventListener('click', () => player.next())\n this.center.append(this.centerNextBtn)\n }\n this.center.style.display = 'none'\n\n // Next-up hover preview (desktop). Wired to whichever next buttons exist.\n this.setupNextPreview()\n\n // Unified ⚙ dropdown — placed immediately left of the ⋯ button. Holds\n // whatever is set to 'gear'; hidden entirely when it has no available\n // section (handled by `available()`).\n if (this.gear.speed || this.gear.quality || this.gear.subtitles) {\n this.gearBtn = iconButton('imp-btn--settings', labels.settings, icons.settings)\n this.gearMenu = new Menu(this.gearBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.gearBtn, this.gearMenu.root)\n this.gearBtn.addEventListener('click', () => this.toggleGearMenu())\n this.rightItems.set('gear', wrap)\n this.registerCollapsible({\n key: 'gear', el: wrap, priority: 40,\n available: () => this.gearSections().length > 0,\n section: () => this.gearSections(),\n })\n }\n\n // Append the right-cluster controls in the configured (or built-in) order.\n this.layoutRightCluster(right)\n\n // ⋯ dropdown — overflow target + consumer actions. Always last.\n this.buildMoreDropdown(right)\n\n this.bind()\n this.syncVolume()\n this.syncPlayState()\n this.setLikeState(player.actionsOptions.likeState ?? null)\n\n // React to the player resizing (fullscreen, layout) ...\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.scheduleReflow())\n this.resizeObserver.observe(player.container)\n }\n // ... and to viewport changes crossing the 767px mobile breakpoint.\n this.onWindowResize = () => this.scheduleReflow()\n window.addEventListener('resize', this.onWindowResize)\n }\n\n private onWindowResize: (() => void) | null = null\n\n private registerCollapsible(c: Collapsible): void {\n this.collapsibles.push(c)\n }\n\n /**\n * Append the right-cluster controls. Built-in order = creation order; if\n * `controls.order` is set, its keys go first (in that order), then any\n * unlisted controls keep their default position. (⋯ is appended separately.)\n */\n private layoutRightCluster(right: HTMLElement): void {\n const insertion = [...this.rightItems.keys()]\n const requested = this.player.controlsOptions.order as string[]\n let keys = insertion\n if (requested.length > 0) {\n const seen = new Set<string>()\n keys = []\n for (const k of requested) {\n if (this.rightItems.has(k) && !seen.has(k)) {\n keys.push(k)\n seen.add(k)\n }\n }\n for (const k of insertion) if (!seen.has(k)) keys.push(k)\n }\n for (const k of keys) {\n const node = this.rightItems.get(k)\n if (node) right.append(node)\n }\n }\n\n /** Center-cluster button — deliberately NOT `.imp-btn` (own sizing/look). */\n private makeCenterButton(modifier: string, label: string, icon: IconSource): HTMLButtonElement {\n const btn = el('button', `imp-center-btn imp-center-btn--${modifier}`, { type: 'button', 'aria-label': label, title: label })\n applyIcon(btn, icon)\n return btn\n }\n\n /** Caption under the seek arrow. Default \"−15s\"/\"+15s\"; overridable via `controls.seekButtons.label`. */\n private addStepBadge(btn: HTMLButtonElement, step: number, dir: 'back' | 'forward'): void {\n const num = el('span', 'imp-seek-num')\n num.textContent = this.seekLabelFn\n ? this.seekLabelFn(step, dir)\n : `${dir === 'back' ? '−' : '+'}${step}${this.player.labels.secondsShort}`\n btn.append(num)\n }\n\n // === gear (unified settings) ==========================================\n\n /** Sections for the gear when overflowed into ⋯ (expanded, no drill-down). */\n private gearSections(): MenuSection[] {\n const p = this.player\n const out: MenuSection[] = []\n if (this.gear.quality && p.qualityLevels.length > 0) out.push(this.qualitySection())\n if (this.gear.speed) out.push(this.speedSection())\n if (this.gear.subtitles && p.subtitleTracks.length > 0) out.push(this.subtitlesSection())\n return out\n }\n\n /** Gear drill-down rows: one per setting, each showing its current value. */\n private gearEntries(): SettingEntry[] {\n const p = this.player\n const out: SettingEntry[] = []\n // Autoplay toggle — only meaningful with a playlist (next item to advance to).\n if (p.hasPlaylist) {\n out.push({\n key: 'autoplay',\n label: p.labels.autoplay,\n toggle: { value: p.autoAdvance, onChange: (v) => p.setAutoAdvance(v) },\n })\n }\n if (this.gear.quality && p.qualityLevels.length > 0) {\n const value = p.currentQuality === -1\n ? p.labels.qualityAuto\n : p.qualityLevels.find((l) => l.index === p.currentQuality)?.label ?? p.labels.qualityAuto\n out.push({ key: 'quality', label: p.labels.quality, value, section: () => this.qualitySection() })\n }\n if (this.gear.speed) {\n const r = p.playbackRate\n out.push({ key: 'speed', label: p.labels.speed, value: r === 1 ? p.labels.speedNormal : `${r}×`, section: () => this.speedSection() })\n }\n if (this.gear.subtitles && p.subtitleTracks.length > 0) {\n const value = p.activeSubtitle === -1\n ? p.labels.subtitlesOff\n : p.subtitleTracks[p.activeSubtitle]?.label ?? p.labels.subtitlesOff\n out.push({ key: 'subtitles', label: p.labels.subtitles, value, section: () => this.subtitlesSection() })\n }\n return out\n }\n\n // === scene types ======================================================\n\n /** Update the scene-type button to the active group's icon + title. */\n private refreshSceneTypeButton(): void {\n const btn = this.sceneTypeBtn\n if (!btn) return\n const active = this.player.activeSceneType\n btn.textContent = ''\n if (!active) return\n if (active.icon) {\n const iconBox = el('span', 'imp-scene-type__icon')\n applyIcon(iconBox, active.icon)\n btn.append(iconBox)\n }\n const label = el('span', 'imp-scene-type__label')\n label.textContent = active.title\n btn.append(label)\n btn.setAttribute('aria-label', `${this.player.labels.sceneTypes}: ${active.title}`)\n btn.title = active.title\n }\n\n private sceneTypeSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.sceneTypes,\n items: p.sceneTypes.map((g) => ({\n label: g.title,\n value: g.id,\n icon: g.icon,\n active: g.id === (p.activeSceneType?.id ?? ''),\n })),\n onSelect: (value) => {\n p.setSceneGroup(value)\n this.refreshSceneTypeButton()\n },\n }\n }\n\n private toggleSceneTypeMenu(): void {\n if (!this.sceneTypeMenu || this.player.sceneTypes.length === 0) return\n this.closeMenus(this.sceneTypeMenu)\n this.sceneTypeMenu.toggle([this.sceneTypeSection()])\n }\n\n private toggleGearMenu(): void {\n if (!this.gearMenu) return\n const entries = this.gearEntries()\n if (entries.length === 0) return\n this.closeMenus(this.gearMenu)\n this.gearMenu.toggleSettings(this.player.labels.settings, entries)\n }\n\n // === next-up hover preview ============================================\n\n private setupNextPreview(): void {\n const p = this.player\n const c = p.controlsOptions\n if (c.nextPreview === false || !c.playlist || !p.hasPlaylist) return\n const o = typeof c.nextPreview === 'object' ? c.nextPreview : {}\n this.nextPreviewOpts = {\n thumbnail: o.thumbnail ?? true,\n title: o.title ?? true,\n duration: o.duration ?? true,\n meta: o.meta ?? true,\n }\n this.nextPreviewEl = el('div', 'imp-next-preview')\n this.nextPreviewEl.hidden = true\n p.container.append(this.nextPreviewEl)\n if (this.nextBtn) this.bindNextPreview(this.nextBtn)\n if (this.centerNextBtn) this.bindNextPreview(this.centerNextBtn)\n }\n\n private bindNextPreview(btn: HTMLButtonElement): void {\n // Mouse only — touch has no hover, and a tap should just go to the next item.\n btn.addEventListener('pointerenter', (e) => {\n if (e.pointerType === 'mouse') this.showNextPreview(btn)\n })\n btn.addEventListener('pointerleave', () => this.hideNextPreview())\n btn.addEventListener('click', () => this.hideNextPreview())\n }\n\n private showNextPreview(btn: HTMLButtonElement): void {\n const card = this.nextPreviewEl\n const opts = this.nextPreviewOpts\n if (!card || !opts) return\n const src = this.player.nextSource\n if (!src) {\n // Nothing playable ahead (e.g. the tail got marked dead) — drop the card.\n this.hideNextPreview()\n return\n }\n\n card.textContent = ''\n const kicker = el('div', 'imp-next-preview__kicker')\n kicker.textContent = this.player.labels.next\n card.append(kicker)\n\n const previewPoster = posterSrc(src.poster)\n if (opts.thumbnail && previewPoster) {\n const thumb = el('div', 'imp-next-preview__thumb')\n const img = coverImg(previewPoster)\n // Poster came back 4xx/5xx → the video won't load either. Mark the source\n // dead and re-render the card with the next alive candidate.\n img.addEventListener('error', () => {\n this.player.markSourceDead(src)\n if (this.nextPreviewEl && !this.nextPreviewEl.hidden) this.showNextPreview(btn)\n }, { once: true })\n thumb.append(img)\n if (opts.duration && src.duration) {\n const d = el('span', 'imp-next-preview__duration')\n d.textContent = formatTime(src.duration)\n thumb.append(d)\n }\n card.append(thumb)\n }\n if (opts.title && src.title) {\n const t = el('div', 'imp-next-preview__title')\n t.textContent = src.title\n card.append(t)\n }\n if (opts.meta && src.previewMeta?.length) {\n card.append(previewMetaEl(src.previewMeta, 'imp-next-preview'))\n }\n\n // Anchor centered above the hovered button, then clamp inside the player so\n // the card never spills past (and gets clipped by) the player edges.\n card.hidden = false\n const cr = this.player.container.getBoundingClientRect()\n const r = btn.getBoundingClientRect()\n const pad = 8\n const cardW = card.offsetWidth\n const centered = r.left - cr.left + r.width / 2 - cardW / 2\n const left = Math.max(pad, Math.min(centered, cr.width - cardW - pad))\n card.style.left = `${left}px`\n card.style.bottom = `${cr.height - (r.top - cr.top) + 10}px`\n }\n\n private hideNextPreview(): void {\n if (this.nextPreviewEl) this.nextPreviewEl.hidden = true\n }\n\n /** like/dislike — visible buttons (collapsible), highlightable via `setLikeState`. */\n private buildLikeDislike(): void {\n const p = this.player\n const a = p.actionsOptions\n const { labels, icons } = p\n\n if (a.like) {\n this.likeBtn = iconButton('imp-btn--like', labels.like, icons.like)\n this.likeBtn.addEventListener('click', () => p.emit('action', { id: 'like' }))\n this.likeCountEl = this.attachCountTooltip(this.likeBtn, a.likeCount)\n const wrap = this.wrapWithTooltip(this.likeBtn, this.likeCountEl)\n this.rightItems.set('like', wrap)\n this.registerCollapsible({\n key: 'like', el: wrap, priority: 62, available: () => true,\n section: () => this.simpleSection('like', labels.like, icons.like, () => p.emit('action', { id: 'like' })),\n })\n }\n if (a.dislike) {\n this.dislikeBtn = iconButton('imp-btn--dislike', labels.dislike, icons.dislike)\n this.dislikeBtn.addEventListener('click', () => p.emit('action', { id: 'dislike' }))\n this.dislikeCountEl = this.attachCountTooltip(this.dislikeBtn, a.dislikeCount)\n const wrap = this.wrapWithTooltip(this.dislikeBtn, this.dislikeCountEl)\n this.rightItems.set('dislike', wrap)\n this.registerCollapsible({\n key: 'dislike', el: wrap, priority: 60, available: () => true,\n section: () => this.simpleSection('dislike', labels.dislike, icons.dislike, () => p.emit('action', { id: 'dislike' })),\n })\n }\n }\n\n private attachCountTooltip(_btn: HTMLButtonElement, initial: number | string | undefined): HTMLElement {\n const tip = el('span', 'imp-count-tooltip')\n this.setCountText(tip, initial)\n return tip\n }\n\n private wrapWithTooltip(btn: HTMLButtonElement, tip: HTMLElement): HTMLElement {\n const wrap = el('div', 'imp-action')\n wrap.append(btn, tip)\n return wrap\n }\n\n private setCountText(tip: HTMLElement, value: number | string | undefined): void {\n const text = value === undefined || value === null || value === '' ? '' : String(value)\n tip.textContent = text\n tip.hidden = text === ''\n }\n\n setLikeCounts(likeCount?: number | string, dislikeCount?: number | string): void {\n if (this.likeCountEl && likeCount !== undefined) this.setCountText(this.likeCountEl, likeCount)\n if (this.dislikeCountEl && dislikeCount !== undefined) this.setCountText(this.dislikeCountEl, dislikeCount)\n }\n\n setLikeState(state: 'like' | 'dislike' | null): void {\n this.likeBtn?.classList.toggle('imp-btn--active', state === 'like')\n this.dislikeBtn?.classList.toggle('imp-btn--active', state === 'dislike')\n }\n\n // === ⋯ menu ===========================================================\n\n private buildMoreDropdown(right: HTMLElement): void {\n const p = this.player\n const a = p.actionsOptions\n const { labels, icons } = p\n\n if (a.addTo) this.actionItems.push({ value: 'addTo', label: labels.addTo, icon: icons.addTo, run: () => p.emit('action', { id: 'addTo' }) })\n if (a.share) this.actionItems.push({ value: 'share', label: labels.share, icon: icons.share, run: () => void p.share() })\n if (a.report) this.actionItems.push({ value: 'report', label: labels.report, icon: icons.report, run: () => p.emit('action', { id: 'report' }) })\n // Bar-placed custom actions render as their own buttons; only menu ones here.\n for (const custom of (a.custom ?? []).filter((ca) => ca.placement !== 'bar')) {\n this.actionItems.push({ value: `custom:${custom.id}`, label: custom.title, icon: custom.icon, run: () => p.emit('customaction', { id: custom.id }) })\n }\n\n const moreBtn = iconButton('imp-btn--more', labels.more, icons.more)\n this.moreMenu = new Menu(moreBtn)\n const wrap = el('div', 'imp-controls__menu-anchor imp-controls__more')\n wrap.append(moreBtn, this.moreMenu.root)\n moreBtn.addEventListener('click', () => {\n this.closeMenus(this.moreMenu)\n this.moreMenu?.toggle(this.buildMoreSections())\n })\n right.append(wrap)\n this.moreWrap = wrap\n }\n\n /** ⋯ menu = overflowed controls (in display order) + consumer actions. */\n private buildMoreSections(): MenuSection[] {\n const sections: MenuSection[] = []\n const runMap = new Map<string, () => void>()\n const simpleItems: Array<{ label: string; value: string; icon?: IconSource; active: boolean }> = []\n\n // Overflowed controls, ordered for reading (nav → toggles → submenus).\n const order = ['prev', 'next', 'seekBack', 'seekFwd', 'like', 'dislike', 'scenes', 'sceneTypes', 'pip', 'list', 'subtitles', 'speed', 'quality', 'gear']\n const rank = (key: string) => {\n const i = order.indexOf(key)\n return i === -1 ? order.length : i // custom:* etc. sort after known keys\n }\n const overflowedSorted = this.collapsibles\n .filter((c) => this.overflowed.has(c.key) && c.available())\n .sort((a, b) => rank(a.key) - rank(b.key))\n\n for (const c of overflowedSorted) {\n const result = c.section()\n if (!result) continue\n // A collapsible may contribute one section or several (the gear menu).\n for (const section of Array.isArray(result) ? result : [result]) {\n // Single-item, no-title contributions merge into one block; titled ones stand alone.\n if (!section.title && section.items.length === 1) {\n const it = section.items[0]\n simpleItems.push(it)\n runMap.set(it.value, () => section.onSelect(it.value))\n } else {\n sections.push(section)\n }\n }\n }\n if (simpleItems.length > 0) {\n sections.unshift({ title: '', items: simpleItems, onSelect: (v) => runMap.get(v)?.() })\n }\n\n if (this.actionItems.length > 0) {\n const map = new Map(this.actionItems.map((a) => [a.value, a.run]))\n sections.push({\n title: '',\n items: this.actionItems.map(({ value, label, icon }) => ({ value, label, icon, active: false })),\n onSelect: (v) => map.get(v)?.(),\n })\n }\n return sections\n }\n\n private simpleSection(value: string, label: string, icon: IconSource, run: () => void): MenuSection {\n return { title: '', items: [{ value, label, icon, active: false }], onSelect: () => run() }\n }\n\n private speedSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.speed,\n items: p.playbackRates.map((rate) => ({\n label: rate === 1 ? p.labels.speedNormal : `${rate}×`,\n value: String(rate),\n active: p.playbackRate === rate,\n })),\n onSelect: (value) => p.setPlaybackRate(Number(value)),\n }\n }\n\n private qualitySection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.quality,\n items: [\n ...(p.qualityAutoAvailable ? [{ label: p.labels.qualityAuto, value: '-1', active: p.currentQuality === -1 }] : []),\n ...p.qualityLevels.map((level) => ({ label: level.label, value: String(level.index), active: p.currentQuality === level.index })),\n ],\n onSelect: (value) => p.setQuality(Number(value)),\n }\n }\n\n private subtitlesSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.subtitles,\n items: [\n { label: p.labels.subtitlesOff, value: '-1', active: p.activeSubtitle === -1 },\n ...p.subtitleTracks.map((track, index) => ({ label: track.label, value: String(index), active: p.activeSubtitle === index })),\n ],\n onSelect: (value) => p.setSubtitle(Number(value)),\n }\n }\n\n // === dynamic overflow =================================================\n\n private scheduleReflow(): void {\n if (this.reflowScheduled) return\n this.reflowScheduled = true\n requestAnimationFrame(() => {\n this.reflowScheduled = false\n this.reflow()\n })\n }\n\n /**\n * Hide controls that don't fit (lowest priority first) and remember them so\n * the ⋯ menu can offer them. Unavailable controls are hidden outright.\n */\n private reflow(): void {\n // Mobile layout up to the site's 767px breakpoint (viewport-based).\n const compact = window.innerWidth <= 767\n // Legacy mobile layout: the full prev · −Ns · play · +Ns · next cluster\n // over the video. Default ('bar') keeps only ±N there — play and next live\n // in the bottom bar, same as desktop.\n const centerLayout = compact && this.player.controlsOptions.mobileLayout === 'center'\n\n // The seek ±N (and center play) live over the video when seekPlacement is\n // 'overlay' (default, every viewport) or always on mobile. In 'bar' mode on\n // desktop they stay in the bottom bar instead.\n const overlay = this.player.controlsOptions.seekPlacement !== 'bar'\n const useCenter = overlay || compact\n this.center.style.display = useCenter ? 'flex' : 'none'\n\n // prev/next sit in the center cluster only in the legacy mobile layout; on\n // desktop (and in the default mobile layout) they live in the bottom bar\n // next to the bar play button.\n if (this.centerPrevBtn) this.centerPrevBtn.style.display = centerLayout ? '' : 'none'\n if (this.centerNextBtn) this.centerNextBtn.style.display = centerLayout ? '' : 'none'\n\n // Bar play/pause: hidden only in the legacy mobile layout, where the\n // center cluster owns playback. The default mobile layout mirrors desktop:\n // big center play (with ±N) over the video AND a bar play bottom-left.\n if (this.playBtn) this.playBtn.style.display = centerLayout ? 'none' : ''\n\n // Which bar buttons are currently relocated into the center cluster:\n // - seek ±N → whenever the cluster is used (overlay desktop or mobile);\n // - prev/next → legacy mobile layout only.\n const inCenter = (key: string): boolean => {\n if (key === 'seekBack' || key === 'seekFwd') return useCenter\n if (key === 'prev' || key === 'next') return centerLayout\n return false\n }\n\n // Reset: availability first, clear forced-overflow hides.\n this.overflowed.clear()\n for (const c of this.collapsibles) {\n if (inCenter(c.key)) {\n // Relocated into the center cluster — hide its bottom-bar twin.\n c.el.style.display = 'none'\n continue\n }\n const ok = c.available()\n c.el.style.display = ok ? '' : 'none'\n c.el.classList.remove('imp-collapsed')\n }\n\n const byPriority = this.collapsibles\n .filter((c) => c.available() && !inCenter(c.key))\n .sort((a, b) => a.priority - b.priority)\n\n // Greedily collapse while the right cluster spills past the row's edge.\n let guard = byPriority.length\n while (guard-- > 0 && this.overflowsRow()) {\n const next = byPriority.find((c) => !this.overflowed.has(c.key))\n if (!next) break\n next.el.style.display = 'none'\n next.el.classList.add('imp-collapsed')\n this.overflowed.add(next.key)\n }\n\n // ⋯ visible if it has anything to show.\n if (this.moreWrap) {\n const hasContent = this.actionItems.length > 0 || this.overflowed.size > 0\n this.moreWrap.style.display = hasContent ? '' : 'none'\n }\n }\n\n private overflowsRow(): boolean {\n const rowRight = this.row.getBoundingClientRect().right\n const groupRight = this.rightGroup.getBoundingClientRect().right\n return groupRight > rowRight + 1\n }\n\n private bind(): void {\n const p = this.player\n this.disposers.push(\n p.on('timeupdate', ({ currentTime, duration }) => {\n this.progress.update(currentTime, duration, p.bufferedEnd)\n if (this.timeLabel) {\n if (p.live) {\n this.timeLabel.hidden = true\n this.liveBadge.hidden = false\n } else {\n this.timeLabel.hidden = false\n this.liveBadge.hidden = true\n this.timeCurrent.textContent = formatTime(currentTime)\n this.timeTotal.textContent = ` / ${formatTime(duration)}`\n }\n }\n }),\n p.on('play', () => this.syncPlayState()),\n p.on('pause', () => this.syncPlayState()),\n p.on('ended', () => this.syncPlayState()),\n p.on('volumechange', () => this.syncVolume()),\n p.on('chapterchange', ({ chapter }) => {\n this.chapterLabel.textContent = chapter?.title ?? ''\n }),\n p.on('fullscreenchange', ({ active }) => {\n if (this.fullscreenBtn) {\n setIcon(this.fullscreenBtn, active ? p.icons.fullscreenExit : p.icons.fullscreen, active ? p.labels.exitFullscreen : p.labels.fullscreen)\n }\n this.scheduleReflow()\n }),\n p.on('playlistitemchange', () => {\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }),\n p.on('sourcechange', () => {\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }),\n )\n this.syncPlaylistButtons()\n this.reflow()\n }\n\n private syncPlayState(): void {\n const p = this.player\n const [icon, label] = p.ended\n ? [p.icons.replay, p.labels.replay]\n : p.paused\n ? [p.icons.play, p.labels.play]\n : [p.icons.pause, p.labels.pause]\n if (this.playBtn) setIcon(this.playBtn, icon, label)\n if (this.centerPlayBtn) setIcon(this.centerPlayBtn, icon, label)\n }\n\n private syncVolume(): void {\n if (!this.muteBtn) return\n const p = this.player\n const effective = p.muted ? 0 : p.volume\n const icon = effective === 0 ? p.icons.volumeMute : effective < 0.5 ? p.icons.volumeLow : p.icons.volumeHigh\n setIcon(this.muteBtn, icon, p.muted ? p.labels.unmute : p.labels.mute)\n this.volumeSlider.value = String(effective)\n this.volumeSlider.style.setProperty('--imp-volume-fill', `${effective * 100}%`)\n }\n\n /** Called by the player when per-source data (chapters/quality/subtitles) changes. */\n syncFeatureButtons(): void {\n this.refreshSceneTypeButton()\n // hasNext can flip when a source is marked dead — keep nav buttons honest.\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }\n\n private syncPlaylistButtons(): void {\n const p = this.player\n if (this.prevBtn) this.prevBtn.disabled = !p.hasPrevious\n if (this.nextBtn) this.nextBtn.disabled = !p.hasNext\n if (this.centerPrevBtn) this.centerPrevBtn.disabled = !p.hasPrevious\n if (this.centerNextBtn) this.centerNextBtn.disabled = !p.hasNext\n }\n\n private closeMenus(except?: Menu | null): void {\n for (const menu of [this.gearMenu, this.settingsMenu, this.subtitlesMenu, this.qualityMenu, this.sceneTypeMenu, this.moreMenu]) {\n if (menu && menu !== except) menu.close()\n }\n }\n\n /** True while any control-bar dropdown (gear / quality / scene type / ⋯ / …) is open. */\n anyMenuOpen(): boolean {\n return [this.gearMenu, this.settingsMenu, this.subtitlesMenu, this.qualityMenu, this.sceneTypeMenu, this.moreMenu]\n .some((menu) => menu?.open)\n }\n\n private toggleSettingsMenu(): void {\n if (!this.settingsMenu) return\n this.closeMenus(this.settingsMenu)\n this.settingsMenu.toggle([this.speedSection()])\n }\n\n private toggleQualityMenu(): void {\n if (!this.qualityMenu || this.player.qualityLevels.length === 0) return\n this.closeMenus(this.qualityMenu)\n this.qualityMenu.toggle([this.qualitySection()])\n }\n\n private toggleSubtitlesMenu(): void {\n if (!this.subtitlesMenu || this.player.subtitleTracks.length === 0) return\n this.closeMenus(this.subtitlesMenu)\n this.subtitlesMenu.toggle([this.subtitlesSection()])\n }\n\n destroy(): void {\n for (const dispose of this.disposers) dispose()\n this.disposers = []\n this.resizeObserver?.disconnect()\n if (this.onWindowResize) window.removeEventListener('resize', this.onWindowResize)\n this.gearMenu?.destroy()\n this.sceneTypeMenu?.destroy()\n this.settingsMenu?.destroy()\n this.subtitlesMenu?.destroy()\n this.qualityMenu?.destroy()\n this.moreMenu?.destroy()\n this.nextPreviewEl?.remove()\n this.root.remove()\n }\n}\n","import { applyIcon, coverImg, el, formatTime, iconButton, previewMetaEl } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\nimport type { PlayerLabels, RelatedItem, RelatedOptions, VideoSource } from '../types'\n\n/** Preview poster: cover image + big play button, shown until the first play. */\nexport class PosterOverlay {\n readonly root: HTMLElement\n /** Wrapper <div> holding a real <img> — same markup as the lazy placeholder\n * (no swap on mount), and the <img> stays LCP-eligible. */\n private image: HTMLElement\n private img: HTMLImageElement\n\n constructor(player: Player) {\n this.root = el('div', 'imp-poster')\n this.image = el('div', 'imp-poster__image')\n this.image.hidden = true\n this.img = coverImg('')\n this.image.append(this.img)\n const big = iconButton('imp-poster__play', player.labels.play, player.icons.bigPlay)\n big.addEventListener('click', () => void player.play())\n this.root.append(this.image, big)\n // Background tap (not the play button). On touch, reveal the control bar\n // instead of starting playback (mouse click still plays as before).\n this.root.addEventListener('pointerup', (e) => {\n if (e.target !== this.root && e.target !== this.image && e.target !== this.img) return\n if (e.pointerType === 'touch' && player.posterTapReveal()) return\n void player.play()\n })\n }\n\n setSource(source: VideoSource | null): void {\n const p = source?.poster\n if (p) {\n const full = typeof p === 'string' ? { src: p } : p\n this.img.src = full.src\n // LCP-grade attributes when the consumer provides the full descriptor.\n if (full.srcset) this.img.srcset = full.srcset\n else this.img.removeAttribute('srcset')\n if (full.sizes) this.img.sizes = full.sizes\n else this.img.removeAttribute('sizes')\n if (full.width) this.img.width = full.width\n else this.img.removeAttribute('width')\n if (full.height) this.img.height = full.height\n else this.img.removeAttribute('height')\n this.img.alt = full.alt ?? ''\n // First poster is the page's hero — hint the browser to load it eagerly.\n this.img.fetchPriority = 'high'\n this.image.hidden = false\n } else {\n this.img.removeAttribute('src')\n this.image.hidden = true\n }\n }\n\n show(): void {\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/**\n * Pause screen \"slot\". Default content is title + description of the current\n * source; consumers can replace it with any element (or a factory), and the\n * Vue wrapper feeds a real <slot> through the same API.\n */\nexport class PauseScreen {\n readonly root: HTMLElement\n private defaultContent: HTMLElement\n private channel: HTMLElement\n private channelAvatar: HTMLElement\n private channelName: HTMLElement\n private channelUrl: string | null = null\n private title: HTMLElement\n private description: HTMLElement\n private sponsor: HTMLAnchorElement\n private sponsorLabel: HTMLElement\n private sponsorText: HTMLElement\n private custom: HTMLElement | null = null\n /** Which parts the consumer allows (a hidden part stays hidden even with data). */\n private visible = { title: true, description: true, sponsor: true }\n\n constructor(private labels: PlayerLabels) {\n this.root = el('div', 'imp-pause-screen')\n this.root.hidden = true\n this.defaultContent = el('div', 'imp-pause-screen__default')\n\n this.channel = el('button', 'imp-channel', { type: 'button' })\n this.channel.hidden = true\n this.channelAvatar = el('div', 'imp-channel__avatar')\n this.channelName = el('div', 'imp-channel__name')\n // The plate sits at the right edge — text first, avatar at the outer edge.\n this.channel.append(this.channelName, this.channelAvatar)\n this.channel.addEventListener('click', () => {\n if (this.channelUrl) window.open(this.channelUrl, '_blank', 'noopener')\n })\n\n const heading = el('div', 'imp-pause-screen__heading')\n this.title = el('div', 'imp-pause-screen__title')\n this.description = el('div', 'imp-pause-screen__description')\n\n // Sponsor / promo link. Sits ABOVE the title (the promo slot comes first),\n // shown only when set on the source.\n this.sponsor = el('a', 'imp-sponsor', { target: '_blank', rel: 'nofollow noopener' }) as HTMLAnchorElement\n this.sponsor.hidden = true\n this.sponsorLabel = el('span', 'imp-sponsor__label')\n this.sponsorText = el('span', 'imp-sponsor__text')\n this.sponsor.append(this.sponsorLabel, this.sponsorText)\n // Don't let a click on the link bubble to the pause-screen play toggle.\n this.sponsor.addEventListener('click', (e) => e.stopPropagation())\n\n // Order on the overlay: sponsor (ad link) → title → description.\n heading.append(this.sponsor, this.title, this.description)\n // Title block on the left, channel pinned to the right edge.\n this.defaultContent.append(heading, this.channel)\n this.root.append(this.defaultContent)\n }\n\n setCustomContent(element: HTMLElement | null): void {\n this.custom?.remove()\n this.custom = element\n if (element) {\n element.classList.add('imp-pause-screen__custom')\n this.root.append(element)\n }\n this.defaultContent.hidden = element !== null\n // Custom content renders as a full-cover centered overlay (ad use-case),\n // not the top info strip.\n this.root.classList.toggle('imp-pause-screen--custom', element !== null)\n }\n\n /** True while consumer-provided custom content is mounted. */\n get hasCustom(): boolean {\n return this.custom !== null\n }\n\n /** Hide specific parts of the default overlay (consumer renders them itself). */\n setVisibility(parts: { title?: boolean; description?: boolean; sponsor?: boolean }): void {\n this.visible = {\n title: parts.title ?? this.visible.title,\n description: parts.description ?? this.visible.description,\n sponsor: parts.sponsor ?? this.visible.sponsor,\n }\n }\n\n setSource(source: VideoSource | null): void {\n this.title.textContent = source?.title ?? ''\n this.description.textContent = source?.description ?? ''\n this.title.hidden = !this.visible.title || !source?.title\n this.description.hidden = !this.visible.description || !source?.description\n\n // Sponsor link — only for sponsored videos (i.e. when provided) and allowed.\n const sponsor = this.visible.sponsor ? source?.sponsor : undefined\n this.sponsor.hidden = !sponsor\n if (sponsor) {\n this.sponsor.href = sponsor.url\n this.sponsorText.textContent = sponsor.text\n this.sponsorLabel.textContent = sponsor.label ?? this.labels.sponsored\n }\n\n const channel = source?.channel\n this.channel.hidden = !channel\n this.channelUrl = channel?.url ?? null\n this.channel.classList.toggle('imp-channel--link', Boolean(channel?.url))\n if (channel) {\n this.channelName.textContent = channel.name\n this.channelAvatar.className = `imp-channel__avatar imp-channel__avatar--${channel.avatarShape ?? 'circle'}`\n if (channel.avatar) {\n this.channelAvatar.textContent = ''\n this.channelAvatar.append(coverImg(channel.avatar, channel.name))\n } else {\n // Styled first-letter placeholder when there is no avatar.\n this.channelAvatar.textContent = (channel.name.trim()[0] ?? '?').toUpperCase()\n }\n }\n }\n\n show(): void {\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/** Grid of related videos, shown on pause and/or ended. */\nexport class RelatedOverlay {\n readonly root: HTMLElement\n private grid: HTMLElement\n private heading: HTMLElement\n private clickBehavior: NonNullable<RelatedOptions['clickBehavior']> = 'player'\n\n constructor(private player: Player) {\n this.root = el('div', 'imp-related')\n this.root.hidden = true\n this.heading = el('div', 'imp-related__title')\n this.grid = el('div', 'imp-related__grid')\n const close = iconButton('imp-related__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n this.root.append(close, this.heading, this.grid)\n }\n\n setOptions(options: RelatedOptions | undefined): void {\n this.grid.textContent = ''\n if (!options) return\n this.clickBehavior = options.clickBehavior ?? 'player'\n this.heading.textContent = options.title ?? this.player.labels.related\n for (const item of options.items) {\n this.grid.append(this.buildCard(item))\n }\n }\n\n private buildCard(item: RelatedItem): HTMLElement {\n const card = el('button', 'imp-related__card', { type: 'button' })\n const thumb = el('div', 'imp-related__thumb')\n if (item.poster) thumb.append(coverImg(item.poster))\n if (item.duration) {\n const badge = el('span', 'imp-related__duration')\n badge.textContent = item.duration\n thumb.append(badge)\n }\n const title = el('div', 'imp-related__card-title')\n title.textContent = item.title\n card.append(thumb, title)\n card.addEventListener('click', () => {\n // The event always fires — consumers can fully drive navigation themselves.\n this.player.emit('relatedclick', { item })\n this.hide()\n this.activate(item)\n })\n return card\n }\n\n /** Apply the configured click behavior. */\n private activate(item: RelatedItem): void {\n const url = item.url ?? item.source?.src\n switch (this.clickBehavior) {\n case 'newWindow':\n if (url) window.open(url, '_blank', 'noopener')\n break\n case 'currentTab':\n if (url) window.location.href = url\n break\n case 'player':\n default:\n if (item.source) {\n this.player.load(item.source)\n void this.player.play()\n } else if (item.url) {\n window.open(item.url, '_blank', 'noopener')\n }\n }\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n if (this.grid.childElementCount === 0) return\n this.root.hidden = false\n this.player.emit('relatedshow', undefined)\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/**\n * End-of-video \"up next\" overlay shown when autoplay is OFF and a next item\n * exists: a preview card of the next video + a \"Play next\" button.\n */\nexport class UpNextOverlay {\n readonly root: HTMLElement\n\n constructor(private player: Player) {\n this.root = el('div', 'imp-upnext')\n this.root.hidden = true\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n const src = this.player.nextSource\n if (!src) {\n // Nothing playable ahead (tail marked dead) — don't leave a stale card.\n this.hide()\n return\n }\n const { labels } = this.player\n\n this.root.textContent = ''\n const card = el('div', 'imp-upnext__card')\n\n const kicker = el('div', 'imp-upnext__kicker')\n kicker.textContent = labels.nextVideo\n card.append(kicker)\n\n const upPoster = posterSrc(src.poster)\n if (upPoster) {\n const thumb = el('button', 'imp-upnext__thumb', { type: 'button', 'aria-label': labels.playNext })\n const img = coverImg(upPoster)\n // Dead poster → dead video: skip it and re-render with the next candidate.\n img.addEventListener('error', () => {\n this.player.markSourceDead(src)\n if (!this.root.hidden) this.show()\n }, { once: true })\n thumb.append(img)\n if (src.duration) {\n const dur = el('span', 'imp-upnext__duration')\n dur.textContent = formatTime(src.duration)\n thumb.append(dur)\n }\n const play = el('span', 'imp-upnext__thumb-play')\n applyIcon(play, this.player.icons.bigPlay)\n thumb.append(play)\n thumb.addEventListener('click', () => this.player.next())\n card.append(thumb)\n }\n\n if (src.title) {\n const title = el('div', 'imp-upnext__title')\n title.textContent = src.title\n card.append(title)\n }\n if (src.previewMeta?.length) {\n card.append(previewMetaEl(src.previewMeta, 'imp-upnext'))\n }\n\n const btn = el('button', 'imp-upnext__btn', { type: 'button' })\n btn.textContent = labels.playNext\n btn.addEventListener('click', () => this.player.next())\n card.append(btn)\n\n this.root.append(card)\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n","import { coverImg, el, formatTime, iconButton } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\n\n/** Slide-in panel listing playlist items; survives source changes. */\nexport class PlaylistPanel {\n readonly root: HTMLElement\n private list: HTMLElement\n private shuffleBtn: HTMLButtonElement\n private repeatBtn: HTMLButtonElement\n\n constructor(private player: Player, layout: 'sidebar' | 'bottom' = 'sidebar', playlistTitle?: string) {\n this.root = el('div', `imp-playlist imp-playlist--${layout}`)\n this.root.hidden = true\n const header = el('div', 'imp-playlist__header')\n const title = el('div', 'imp-playlist__heading')\n if (playlistTitle) {\n // Named playlist: small \"Playlist\" kicker + the name in the primary font.\n const kicker = el('div', 'imp-playlist__kicker')\n kicker.textContent = player.labels.playlist\n const name = el('div', 'imp-playlist__name')\n name.textContent = playlistTitle\n title.append(kicker, name)\n } else {\n title.textContent = player.labels.playlist\n }\n\n const tools = el('div', 'imp-playlist__tools')\n this.shuffleBtn = iconButton('imp-playlist__shuffle', player.labels.shuffle, player.icons.shuffle)\n this.shuffleBtn.addEventListener('click', () => {\n player.setShuffle(!player.shuffle)\n this.syncModes()\n })\n this.repeatBtn = iconButton('imp-playlist__repeat', player.labels.repeat, player.icons.repeat)\n this.repeatBtn.addEventListener('click', () => {\n player.setRepeat(!player.repeat)\n this.syncModes()\n })\n const close = iconButton('imp-playlist__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n tools.append(this.shuffleBtn, this.repeatBtn, close)\n\n header.append(title, tools)\n this.list = el('div', 'imp-playlist__list')\n this.root.append(header, this.list)\n this.rebuild()\n this.syncModes()\n }\n\n private syncModes(): void {\n this.shuffleBtn.classList.toggle('imp-btn--active', this.player.shuffle)\n this.repeatBtn.classList.toggle('imp-btn--active', this.player.repeat)\n }\n\n rebuild(): void {\n this.list.textContent = ''\n this.player.playlist.forEach((source, index) => {\n const item = el('button', 'imp-playlist__item', { type: 'button' })\n if (index === this.player.index) item.classList.add('imp-playlist__item--active')\n const thumb = el('div', 'imp-playlist__thumb')\n const thumbSrc = posterSrc(source.poster)\n if (thumbSrc) thumb.append(coverImg(thumbSrc))\n const meta = el('div', 'imp-playlist__meta')\n const itemTitle = el('div', 'imp-playlist__title')\n itemTitle.textContent = source.title ?? `#${index + 1}`\n meta.append(itemTitle)\n if (source.duration) {\n const dur = el('div', 'imp-playlist__duration')\n dur.textContent = formatTime(source.duration)\n meta.append(dur)\n }\n item.append(thumb, meta)\n item.addEventListener('click', () => {\n this.player.playItem(index)\n // Mobile full-cover sheet: close on pick so the video shows at once.\n if (window.innerWidth <= 767) this.hide()\n })\n this.list.append(item)\n })\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n this.rebuild()\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n\n toggle(): void {\n if (this.visible) this.hide()\n else this.show()\n }\n}\n","import { coverImg, el, formatTime, iconButton } from '../core/dom'\nimport type { Player } from '../player'\n\n/**\n * Scene list — the chapters of the current video as a clickable panel, each\n * with a preview cropped from the VTT sprite at the scene start.\n */\nexport class ScenesPanel {\n readonly root: HTMLElement\n private list: HTMLElement\n private items: HTMLButtonElement[] = []\n\n constructor(private player: Player, layout: 'sidebar' | 'bottom' = 'bottom') {\n this.root = el('div', `imp-playlist imp-scenes imp-playlist--${layout}`)\n this.root.hidden = true\n const header = el('div', 'imp-playlist__header')\n const title = el('span')\n title.textContent = player.labels.scenes\n const close = iconButton('imp-playlist__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n header.append(title, close)\n this.list = el('div', 'imp-playlist__list')\n this.root.append(header, this.list)\n\n player.on('chapterchange', () => this.syncActive())\n }\n\n /** Re-render from the player's current chapters + thumbnail track. */\n rebuild(): void {\n this.list.textContent = ''\n this.items = []\n const thumbs = this.player.thumbnails\n for (const chapter of this.player.chapterList) {\n const item = el('button', 'imp-playlist__item', { type: 'button' })\n const thumb = el('div', 'imp-playlist__thumb')\n\n const cue = thumbs?.cueAt(chapter.start + 0.01) ?? null\n if (cue?.xywh) {\n // Crop the sprite region and scale it into the thumb box.\n const inner = el('div', 'imp-scenes__sprite')\n inner.style.width = `${cue.xywh.w}px`\n inner.style.height = `${cue.xywh.h}px`\n inner.style.backgroundImage = `url(\"${cue.src}\")`\n inner.style.backgroundPosition = `-${cue.xywh.x}px -${cue.xywh.y}px`\n inner.style.setProperty('--imp-sprite-w', String(cue.xywh.w))\n thumb.append(inner)\n } else if (cue) {\n // Whole-image cue (no sprite crop) → a real <img>; sprite crops above\n // keep the background-position mechanism.\n thumb.append(coverImg(cue.src))\n }\n\n const meta = el('div', 'imp-playlist__meta')\n const itemTitle = el('div', 'imp-playlist__title')\n itemTitle.textContent = chapter.title || formatTime(chapter.start)\n const time = el('div', 'imp-playlist__duration')\n time.textContent = formatTime(chapter.start)\n meta.append(itemTitle, time)\n\n item.append(thumb, meta)\n item.addEventListener('click', () => {\n this.player.seek(chapter.start)\n void this.player.play()\n // On mobile the panel is a full-cover sheet — close it so the chosen\n // scene is visible immediately (no manual close). Desktop sidebar/strip\n // stays open: the video is visible alongside it.\n if (window.innerWidth <= 767) this.hide()\n })\n this.list.append(item)\n this.items.push(item)\n }\n this.syncActive()\n }\n\n private syncActive(): void {\n const current = this.player.chapter\n const chapters = this.player.chapterList\n this.items.forEach((item, i) => {\n item.classList.toggle('imp-playlist__item--active', current !== null && chapters[i] === current)\n })\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n this.rebuild()\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n\n toggle(): void {\n if (this.visible) this.hide()\n else this.show()\n }\n}\n","import { clamp, el } from './core/dom'\nimport { posterSrc } from './placeholder'\nimport { Emitter } from './core/events'\nimport { defaultIcons } from './core/icons'\nimport { defaultLabels } from './core/labels'\nimport { getLocale } from './core/localeRegistry'\nimport { buildHeatmapValues, buildHeatmapValuesDense } from './features/heatmap'\nimport { AdManager } from './features/ads/manager'\nimport { chapterAt, loadChaptersVtt, normalizeChapters, type NormalizedChapter } from './features/chapters'\nimport { attachSource, type Level, type SourceController } from './features/hls'\nimport { ThumbnailTrack } from './features/thumbnails'\nimport { Controls } from './ui/controls'\nimport { PauseScreen, PosterOverlay, RelatedOverlay, UpNextOverlay } from './ui/overlays'\nimport { PlaylistPanel } from './ui/playlistPanel'\nimport { ScenesPanel } from './ui/scenesPanel'\nimport type {\n ActionsOptions,\n ControlsOptions,\n IconName,\n IconSource,\n PlayerEventMap,\n PlayerLabels,\n PlayerOptions,\n PlaylistOptions,\n SceneGroup,\n SubtitleTrack,\n VideoSource,\n} from './types'\n\n/**\n * Page-level valve for the benign \"play() was interrupted by a new load\n * request / a call to pause()\" AbortError. Interrupting a pending `play()`\n * REJECTS its promise by spec — unavoidable when sources switch — and if the\n * pending promise lives in consumer code (a bare `video.play()` in the app),\n * the library cannot attach a catch to it. These exact messages are produced\n * only by media-element play interruptions and are pure console noise, so we\n * suppress precisely them while at least one player instance exists.\n */\nlet playValveCount = 0\nfunction onBenignPlayAbort(e: PromiseRejectionEvent): void {\n const r = e.reason as { name?: string; message?: string } | null | undefined\n if (r?.name === 'AbortError' && /interrupted by a (new load request|call to pause)/i.test(r.message ?? '')) {\n e.preventDefault()\n }\n}\nfunction acquirePlayValve(): void {\n if (typeof window !== 'undefined' && ++playValveCount === 1) {\n window.addEventListener('unhandledrejection', onBenignPlayAbort)\n }\n}\nfunction releasePlayValve(): void {\n if (typeof window !== 'undefined' && --playValveCount === 0) {\n window.removeEventListener('unhandledrejection', onBenignPlayAbort)\n }\n}\n\n/**\n * A rejected `play()` we recover from is still worth a line in the console:\n * silent `.catch(() => {})` made gesture/autoplay-policy problems (e.g. the\n * mobile double-tap-to-start bug) undiagnosable from the field.\n */\nfunction logPlayRejection(err: unknown): void {\n console.debug('[itube-player] play() rejected:', err)\n}\n\n/** `subtitles` accepts a URL, one track, or an array — normalize to an array. */\nfunction normalizeSubtitles(input: VideoSource['subtitles']): SubtitleTrack[] {\n if (!input) return []\n if (typeof input === 'string') return [{ src: input, label: 'Subtitles' }]\n return Array.isArray(input) ? input : [input]\n}\n\nconst defaultControls: Required<ControlsOptions> = {\n play: true,\n progress: true,\n time: true,\n volume: true,\n fullscreen: true,\n pip: true,\n // Settings-type controls default into the unified ⚙ dropdown.\n speed: 'gear',\n settings: 'gear', // legacy alias for `speed`; resolved in the constructor\n quality: 'gear',\n scenes: true,\n sceneTypes: true,\n heatmap: true,\n subtitles: 'gear',\n seekButtons: true,\n // Prev/seek/play/next as a centered overlay on every viewport.\n seekPlacement: 'overlay',\n // Mobile: only ±N seek over the video; play/next live in the bottom bar.\n mobileLayout: 'bar',\n order: [], // empty → built-in right-cluster order\n playlist: true,\n hidePrev: true,\n hideNext: false,\n playlistButton: true,\n nextPreview: true,\n hideDelay: 2500,\n revealBeforePlay: true,\n}\n\n/**\n * Framework-agnostic video player.\n *\n * ```ts\n * import { Player } from 'itube-modern-player'\n * import 'itube-modern-player/style.css'\n *\n * const player = new Player('#mount', { source: { src: 'video.m3u8', title: 'Demo' } })\n * ```\n *\n * Design rules this codebase follows (deliberately unlike fluid-player):\n * - no globals, no instance registry — `new Player()` as many times as you like;\n * - one overlay layer laid out with grid/flex, not a pile of absolute elements;\n * - `destroy()` returns the mount node to its original state;\n * - every event is typed, every string and icon is replaceable.\n */\nexport class Player {\n readonly container: HTMLElement\n readonly video: HTMLVideoElement\n\n readonly labels: PlayerLabels\n readonly icons: Record<IconName, IconSource>\n readonly controlsOptions: Required<ControlsOptions>\n readonly actionsOptions: ActionsOptions\n readonly playbackRates: number[]\n readonly seekStep: number\n\n /** Set by the controls while the user drags the seek bar. */\n scrubbing = false\n\n private emitter = new Emitter<PlayerEventMap>()\n private mount: HTMLElement\n private options: PlayerOptions\n private playlistOptions: Required<PlaylistOptions>\n /** External navigation mode: advance intents emit `advancerequest` instead of switching. */\n private externalAdvance = false\n private abort = new AbortController()\n\n private sources: VideoSource[] = []\n /** Sources detected as broken (poster failed to load) — \"next\" skips them. */\n private deadSources = new WeakSet<VideoSource>()\n private currentIndex = -1\n private sourceController: SourceController | null = null\n private loadToken = 0\n\n private chapters: NormalizedChapter[] = []\n private currentChapter: NormalizedChapter | null = null\n /** Typed scene breakdowns; the active one drives `chapters`. */\n private sceneGroupList: SceneGroup[] = []\n private activeSceneGroupId = ''\n private progressiveQuality = -1\n private thumbTrack: ThumbnailTrack | null = null\n private shuffleMode = false\n\n private controls: Controls\n private poster: PosterOverlay\n private pauseScreen: PauseScreen\n private related: RelatedOverlay\n private upNext: UpNextOverlay\n /** Runtime autoplay-next toggle (gear switch); seeds from playlist.autoAdvance. */\n private autoAdvanceEnabled = true\n /** localStorage key for persisted prefs, or null when persistence is off. */\n private persistKey: string | null = null\n private persistVolume = false\n private persistAutoAdvance = false\n private playlistPanel: PlaylistPanel\n private scenesPanel: ScenesPanel\n private spinner: HTMLElement\n private errorBox!: HTMLElement\n private errorText!: HTMLElement\n private retryBtn!: HTMLButtonElement\n private errorNextBtn!: HTMLButtonElement\n private decodeRecoveries = 0\n private adManager: AdManager | null = null\n\n private playedOnce = false\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n /** Auto-hide timer for the before-play control-bar \"peek\" (touch). */\n private peekTimer: ReturnType<typeof setTimeout> | null = null\n /** Retry-button throttle: last press time + the re-enable timer. */\n private lastRetryAt = 0\n private retryTimer: ReturnType<typeof setTimeout> | null = null\n /** Playhead position to restore when retrying after a mid-video error. */\n private errorResumeTime = 0\n /** Last known good playhead time (for the retry-resume above). */\n private lastPlayheadTime = 0\n /** Seek requested before metadata (pre-start scrub) — applied on loadedmetadata. */\n private pendingSeek: number | null = null\n private destroyed = false\n\n constructor(target: string | HTMLElement, options: PlayerOptions = {}) {\n const mount = typeof target === 'string' ? document.querySelector<HTMLElement>(target) : target\n if (!mount) throw new Error(`[itube-player] mount target not found: ${String(target)}`)\n this.mount = mount\n this.options = options\n\n this.labels = { ...defaultLabels, ...getLocale(options.language), ...options.labels }\n this.icons = { ...defaultIcons, ...options.icons }\n // Resolve the deprecated `settings` alias from the raw options (an explicit\n // `speed` wins; else legacy `settings`; else the 'gear' default).\n const rawControls = options.controls ?? {}\n const speed = rawControls.speed ?? rawControls.settings ?? 'gear'\n this.controlsOptions = { ...defaultControls, ...rawControls, speed, settings: speed }\n this.actionsOptions = options.actions ?? {}\n this.playbackRates = options.playbackRates ?? [0.5, 1, 1.5, 2]\n this.seekStep = options.seekStep ?? 15\n this.playlistOptions = { title: '', autoAdvance: true, loop: false, shuffle: false, startIndex: 0, layout: 'sidebar', externalAdvance: false, ...options.playlist }\n this.shuffleMode = this.playlistOptions.shuffle\n this.externalAdvance = this.playlistOptions.externalAdvance\n\n // Persistence: set up before applying volume/autoAdvance so saved prefs win.\n if (options.persist) {\n const p = options.persist === true ? {} : options.persist\n this.persistKey = p.key ?? 'itube-player'\n this.persistVolume = p.volume !== false\n this.persistAutoAdvance = p.autoAdvance !== false\n }\n const saved = this.readPersisted()\n\n this.autoAdvanceEnabled = this.persistAutoAdvance && typeof saved?.autoAdvance === 'boolean'\n ? saved.autoAdvance\n : this.playlistOptions.autoAdvance\n\n this.sources = options.source ? (Array.isArray(options.source) ? [...options.source] : [options.source]) : []\n\n // --- DOM ----------------------------------------------------------\n this.container = el('div', 'imp-player', { tabindex: '0' })\n if (options.className) this.container.classList.add(options.className)\n\n // Custom styling → CSS variables on the container (overridable per instance).\n const styling = options.styling\n if (styling?.themeColor) this.container.style.setProperty('--imp-accent', styling.themeColor)\n if (styling?.likeColor) this.container.style.setProperty('--imp-like', styling.likeColor)\n if (styling?.dislikeColor) this.container.style.setProperty('--imp-dislike', styling.dislikeColor)\n if (styling?.fontFamily) this.container.style.setProperty('--imp-font', styling.fontFamily)\n if (styling?.borderRadius !== undefined) {\n const r = typeof styling.borderRadius === 'number' ? `${styling.borderRadius}px` : styling.borderRadius\n this.container.style.setProperty('--imp-radius', r)\n }\n if (styling?.playButtonStyle === 'inverted') this.container.classList.add('imp-player--play-inverted')\n\n // A consumer-provided element (the lazy entries pass one unlocked inside\n // the click gesture — see gesture.ts) is adopted as-is, just classed.\n this.video = options.videoElement ?? el('video', 'imp-video')\n this.video.classList.add('imp-video')\n if (options.playsInline !== false) this.video.setAttribute('playsinline', '')\n if (options.crossOrigin !== undefined) this.video.crossOrigin = options.crossOrigin\n this.video.preload = 'metadata'\n if (options.loop) this.video.loop = true\n const savedAV = this.persistVolume ? saved : null\n this.video.volume = clamp(savedAV?.volume ?? options.volume ?? 1, 0, 1)\n if (savedAV?.muted ?? options.muted) this.video.muted = true\n\n const layer = el('div', 'imp-layer')\n this.spinner = el('div', 'imp-spinner')\n this.spinner.hidden = true\n this.errorBox = el('div', 'imp-error')\n this.errorBox.hidden = true\n this.errorText = el('div', 'imp-error__text')\n this.retryBtn = el('button', 'imp-error__retry', { type: 'button' })\n const retryLabel = el('span', 'imp-error__retry-label')\n retryLabel.textContent = this.labels.tryAgain\n // Shown (spinning) instead of the label while a retry is in flight.\n this.retryBtn.append(retryLabel, el('span', 'imp-error__retry-spinner'))\n this.retryBtn.addEventListener('click', () => this.retry())\n // \"Next video\" escape hatch: a broken item mid-playlist (e.g. auto-advance\n // landed on a dead stream) must not trap the user on retry alone.\n this.errorNextBtn = el('button', 'imp-error__next', { type: 'button' })\n this.errorNextBtn.textContent = this.labels.nextVideo\n this.errorNextBtn.addEventListener('click', () => {\n // Unlike retry, this leaves the broken source behind — drop the overlay\n // right away and let the next video load with the normal spinner UX.\n this.clearError()\n this.next()\n })\n const errorActions = el('div', 'imp-error__actions')\n errorActions.append(this.retryBtn, this.errorNextBtn)\n this.errorBox.append(this.errorText, errorActions)\n\n this.pauseScreen = new PauseScreen(this.labels)\n this.poster = new PosterOverlay(this)\n this.related = new RelatedOverlay(this)\n this.related.setOptions(options.related)\n this.upNext = new UpNextOverlay(this)\n this.controls = new Controls(this)\n this.playlistPanel = new PlaylistPanel(this, this.playlistOptions.layout, this.playlistOptions.title || undefined)\n this.scenesPanel = new ScenesPanel(this, options.scenes?.layout ?? 'bottom')\n\n const middle = el('div', 'imp-layer__middle')\n middle.append(this.spinner, this.errorBox, this.controls.center)\n const bottom = el('div', 'imp-layer__bottom')\n bottom.append(this.controls.root)\n layer.append(this.pauseScreen.root, this.related.root, this.upNext.root, middle, bottom)\n\n this.container.append(this.video, layer, this.poster.root, this.playlistPanel.root, this.scenesPanel.root)\n this.mount.append(this.container)\n\n if (options.pauseScreen instanceof HTMLElement) {\n this.pauseScreen.setCustomContent(options.pauseScreen)\n } else if (typeof options.pauseScreen === 'function') {\n this.pauseScreen.setCustomContent(options.pauseScreen(this))\n } else if (options.pauseScreen && typeof options.pauseScreen === 'object') {\n // Plain config object: default overlay with some parts hidden.\n this.pauseScreen.setVisibility(options.pauseScreen)\n }\n\n const adConfig = options.adConfig ?? options.ads\n if (adConfig && adConfig.adList.length > 0) {\n this.adManager = new AdManager(\n { container: this.container, contentVideo: this.video, emitter: this.emitter, labels: this.labels, closeIcon: this.icons.close },\n adConfig,\n )\n }\n\n this.emitter.on('error', ({ fatal }) => {\n // Only a fatal playback error takes over the UI (a failed thumbnail /\n // chapter track must not blank the player). Show a friendly localized\n // message + a retry button, and hide play/seek controls.\n if (!fatal) return\n // Remember where playback broke so \"try again\" resumes from there. Prefer\n // the last tracked playhead; fall back to the live currentTime. A zero\n // capture (failed reload never played) must NOT wipe a previous position.\n const at = this.lastPlayheadTime || this.video.currentTime\n if (Number.isFinite(at) && at > 0) this.errorResumeTime = at\n this.errorText.textContent = this.labels.videoNotFound\n // Offer \"next video\" only when there is somewhere to go (always true in\n // external-advance mode — the intent is emitted to the app).\n this.errorNextBtn.hidden = !this.hasNext\n this.errorBox.hidden = false\n this.spinner.hidden = true\n this.poster.hide()\n this.container.classList.add('imp-player--error')\n })\n\n acquirePlayValve()\n this.bindVideoEvents()\n this.bindIdleHide()\n if (options.keyboard !== false) this.bindKeyboard()\n\n // Tap/click on the video surface. Desktop (mouse): toggles playback, as\n // before. Touch (phones): a tap toggles *controls* visibility instead —\n // play/pause lives on the on-screen center button. This is the standard\n // mobile model and avoids the iOS problem where one tap fired both a\n // play-toggle (click) and a show-controls (pointerdown) at once.\n // We use pointerup (not click) so iOS doesn't also deliver a synthesized\n // mouse click that double-fires the handler.\n this.video.addEventListener('pointerup', (e) => {\n if (this.adPlaying || !this.playedOnce) return\n if (e.pointerType === 'mouse') {\n this.togglePlay()\n return\n }\n // Touch / pen: reveal controls if hidden, hide them if shown while\n // playing. When paused the controls (and pause screen) stay up.\n if (this.container.classList.contains('imp-player--idle')) {\n this.showControlsNow()\n this.scheduleIdle()\n } else if (!this.video.paused) {\n this.container.classList.add('imp-player--idle')\n }\n }, { signal: this.abort.signal })\n this.pauseScreen.root.addEventListener('click', (e) => {\n // With custom content (e.g. an ad block) the backdrop must not resume —\n // the consumer's own \"close & continue\" control owns that.\n if (this.pauseScreen.hasCustom) return\n if (e.target === this.pauseScreen.root) this.togglePlay()\n }, { signal: this.abort.signal })\n\n if (this.sources.length > 0) {\n // `playOnInit` is the current name; `autoplay` stays as a deprecated alias.\n void this.loadItem(clamp(this.playlistOptions.startIndex, 0, this.sources.length - 1), Boolean(options.playOnInit ?? options.autoplay))\n }\n // Deferred so code running right after `new Player()` can actually\n // subscribe — a synchronous emit here would fire before any `.on('ready')`.\n queueMicrotask(() => {\n if (!this.destroyed) this.emitter.emit('ready', { player: this })\n })\n }\n\n // === events ===========================================================\n\n on<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): () => void {\n return this.emitter.on(event, fn)\n }\n\n once<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): () => void {\n return this.emitter.once(event, fn)\n }\n\n off<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): void {\n this.emitter.off(event, fn)\n }\n\n /** @internal — UI modules emit through the player. */\n emit<K extends keyof PlayerEventMap>(event: K, payload: PlayerEventMap[K]): void {\n this.emitter.emit(event, payload)\n }\n\n // === playback =========================================================\n\n get paused(): boolean {\n return this.video.paused\n }\n\n get ended(): boolean {\n return this.video.ended\n }\n\n get currentTime(): number {\n return this.video.currentTime\n }\n\n get duration(): number {\n return this.video.duration || 0\n }\n\n get live(): boolean {\n return this.video.duration === Infinity\n }\n\n get bufferedEnd(): number {\n const ranges = this.video.buffered\n return ranges.length > 0 ? ranges.end(ranges.length - 1) : 0\n }\n\n get adPlaying(): boolean {\n return this.adManager?.adPlaying ?? false\n }\n\n async play(skipAds = false): Promise<void> {\n if (this.destroyed || this.adPlaying) return\n // `skipAds` — used by retry(): a broken main video must not trigger a VAST.\n if (this.adManager && !skipAds) {\n await this.adManager.playPreRoll()\n if (this.destroyed) return\n // The pre-roll resumes content itself; if there was none, fall through.\n if (!this.video.paused) return\n }\n try {\n await this.video.play()\n } catch (err) {\n const name = (err as { name?: string })?.name\n // Switching sources (esp. hls.js mid-attach) can momentarily leave the\n // element with no decodable source: retry once when it's ready.\n if (name === 'NotSupportedError' && this.video.readyState === 0) {\n // Token-guarded: if another source loads meanwhile, don't autostart it.\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token === this.loadToken) void this.video.play().catch(logPlayRejection)\n }, { once: true, signal: this.abort.signal })\n return\n }\n // AbortError = a newer load interrupted this play (benign). Otherwise it's\n // typically an autoplay-policy block — restore the poster so the user can\n // tap play. Genuine media failures surface via the `error` event.\n if (name !== 'AbortError') {\n logPlayRejection(err)\n if (!this.playedOnce) this.poster.show()\n }\n }\n }\n\n pause(): void {\n if (this.adPlaying) return\n this.video.pause()\n }\n\n togglePlay(): void {\n if (this.video.paused || this.video.ended) void this.play()\n else this.pause()\n }\n\n /**\n * Clear a fatal-error overlay and reload the current source, resuming from\n * where playback broke. Rate-limited by `options.retryThrottle` (default 5s):\n * the button is disabled for that window after each press.\n */\n retry(): void {\n const throttle = this.options.retryThrottle ?? 5000\n const now = Date.now()\n if (now - this.lastRetryAt < throttle) return\n this.lastRetryAt = now\n // Disabled + a spinner in place of the label while the reload is in flight.\n this.retryBtn.disabled = true\n this.retryBtn.classList.add('imp-error__retry--loading')\n if (this.retryTimer) clearTimeout(this.retryTimer)\n this.retryTimer = setTimeout(() => {\n if (this.destroyed) return\n this.retryBtn.disabled = false\n this.retryBtn.classList.remove('imp-error__retry--loading')\n }, throttle)\n\n const resumeAt = this.errorResumeTime\n // Keep the error overlay (now showing the spinner) up during the reload —\n // clearing it here would flash the controls before the outcome is known.\n // A successful (re)load clears it via the canplay/playing/seeked handler.\n // skipAds: don't run a VAST when re-attempting a broken main video.\n void this.loadItem(this.currentIndex, true, true)\n // loadItem cleared errorResumeTime (normal-navigation hygiene) — restore it\n // so a SECOND retry after another failure still resumes from the same spot.\n this.errorResumeTime = resumeAt\n // Resume from the position the error interrupted (mid-video errors).\n // Token-guarded: if the user switches videos before metadata arrives, the\n // stale listener must not seek the NEW source to the old position.\n if (resumeAt > 0) {\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token !== this.loadToken) return\n try { this.video.currentTime = resumeAt } catch { /* range not ready */ }\n }, { once: true, signal: this.abort.signal })\n }\n }\n\n private clearError(): void {\n this.errorBox.hidden = true\n this.container.classList.remove('imp-player--error')\n }\n\n /** True once the current source has started playing at least once. */\n get started(): boolean {\n return this.playedOnce\n }\n\n seek(time: number): void {\n if (Number.isFinite(this.video.duration)) {\n this.video.currentTime = clamp(time, 0, this.video.duration)\n return\n }\n // Metadata not in yet (scrubbing the bar before the first play, using the\n // declared `source.duration`): stash the target, applied on loadedmetadata.\n const declared = this.source?.duration\n if (typeof declared === 'number' && declared > 0) {\n this.pendingSeek = clamp(time, 0, declared)\n }\n }\n\n /** Relative seek, e.g. `skip(-10)`. */\n skip(delta: number): void {\n this.seek(this.video.currentTime + delta)\n }\n\n // === volume ===========================================================\n\n get volume(): number {\n return this.video.volume\n }\n\n get muted(): boolean {\n return this.video.muted\n }\n\n setVolume(volume: number): void {\n this.video.volume = clamp(volume, 0, 1)\n if (volume > 0) this.video.muted = false\n }\n\n setMuted(muted: boolean): void {\n this.video.muted = muted\n }\n\n toggleMute(): void {\n this.video.muted = !this.video.muted\n }\n\n // === persistence ======================================================\n\n /** Read persisted prefs (safe against private mode / SSR / bad JSON). */\n private readPersisted(): { volume?: number; muted?: boolean; autoAdvance?: boolean } | null {\n if (!this.persistKey) return null\n try {\n const raw = localStorage.getItem(this.persistKey)\n const data = raw ? JSON.parse(raw) : null\n return data && typeof data === 'object' ? data : null\n } catch {\n return null\n }\n }\n\n private persistTimer: ReturnType<typeof setTimeout> | null = null\n\n /** Debounced persistState — for high-frequency triggers (volume drag). */\n private schedulePersist(): void {\n if (this.persistTimer) clearTimeout(this.persistTimer)\n this.persistTimer = setTimeout(() => {\n this.persistTimer = null\n if (!this.destroyed) this.persistState()\n }, 300)\n }\n\n /** Write the enabled prefs to localStorage. No-op when persistence is off. */\n private persistState(): void {\n if (!this.persistKey) return\n try {\n const data: { volume?: number; muted?: boolean; autoAdvance?: boolean } = {}\n if (this.persistVolume) {\n data.volume = this.video.volume\n data.muted = this.video.muted\n }\n if (this.persistAutoAdvance) data.autoAdvance = this.autoAdvanceEnabled\n localStorage.setItem(this.persistKey, JSON.stringify(data))\n } catch {\n // private mode / quota / SSR — persistence is best-effort.\n }\n }\n\n // === rate / quality ===================================================\n\n get playbackRate(): number {\n return this.video.playbackRate\n }\n\n setPlaybackRate(rate: number): void {\n this.video.playbackRate = rate\n this.emitter.emit('ratechange', { rate })\n }\n\n get qualityLevels(): Level[] {\n if (this.sourceController?.kind === 'hls') return this.sourceController.levels\n const qualities = this.source?.qualities\n return qualities\n ? qualities.map((q, index) => ({ index, label: q.label ?? String(q.quality ?? index) }))\n : []\n }\n\n get qualityAutoAvailable(): boolean {\n return this.sourceController?.kind === 'hls'\n }\n\n get currentQuality(): number {\n if (this.sourceController?.kind === 'hls') return this.sourceController.selected\n return this.progressiveQuality\n }\n\n setQuality(index: number): void {\n if (this.sourceController?.kind === 'hls') {\n this.sourceController.setLevel(index)\n const label = index === -1\n ? this.labels.qualityAuto\n : this.sourceController.levels.find((l) => l.index === index)?.label ?? String(index)\n this.emitter.emit('qualitychange', { label })\n return\n }\n const qualities = this.source?.qualities\n if (!qualities || !qualities[index] || index === this.progressiveQuality) return\n const time = this.video.currentTime\n const wasPlaying = !this.video.paused && !this.video.ended\n this.progressiveQuality = index\n this.video.src = qualities[index].src\n // Restore the position only once metadata is in — a seek on readyState 0\n // is silently dropped by some engines (the switch would reset to 0:00).\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token !== this.loadToken) return\n try { this.video.currentTime = time } catch { /* range not ready */ }\n if (wasPlaying) void this.video.play().catch(logPlayRejection)\n }, { once: true, signal: this.abort.signal })\n this.emitter.emit('qualitychange', { label: qualities[index].label ?? String(qualities[index].quality ?? index) })\n }\n\n // === subtitles ========================================================\n\n get subtitleTracks(): SubtitleTrack[] {\n return normalizeSubtitles(this.source?.subtitles)\n }\n\n get activeSubtitle(): number {\n const tracks = this.video.textTracks\n for (let i = 0; i < tracks.length; i++) {\n if (tracks[i].mode === 'showing') return i\n }\n return -1\n }\n\n setSubtitle(index: number): void {\n const tracks = this.video.textTracks\n for (let i = 0; i < tracks.length; i++) {\n tracks[i].mode = i === index ? 'showing' : 'disabled'\n }\n this.emitter.emit('subtitlechange', { track: this.subtitleTracks[index] ?? null })\n }\n\n // === playlist =========================================================\n\n get playlist(): VideoSource[] {\n return this.sources\n }\n\n get index(): number {\n return this.currentIndex\n }\n\n get source(): VideoSource | null {\n return this.sources[this.currentIndex] ?? null\n }\n\n /**\n * Mark a source as broken (e.g. its poster came back 4xx/5xx while building\n * the next-up preview — the video itself won't load either). Every \"next\"\n * computation (hover preview, end overlay, `next()`, auto-advance) skips\n * dead sources from then on. The playlist panel still lists them.\n */\n markSourceDead(source: VideoSource): void {\n this.deadSources.add(source)\n this.controls.syncFeatureButtons()\n }\n\n /** Linear index of the next non-dead item; -1 when nothing playable is ahead. */\n private nextAliveIndex(): number {\n const n = this.sources.length\n if (n < 2) return -1\n for (let step = 1; step < n; step++) {\n const raw = this.currentIndex + step\n if (raw >= n && !this.playlistOptions.loop) return -1\n const idx = raw % n\n if (!this.deadSources.has(this.sources[idx])) return idx\n }\n return -1\n }\n\n /**\n * The item that `next()` would play in linear order (for the next-up preview).\n * `null` in shuffle mode (the pick is random) or when there is no next item.\n * Skips sources marked dead via `markSourceDead`.\n */\n get nextSource(): VideoSource | null {\n if (this.shuffleMode || this.sources.length < 2) return null\n const i = this.nextAliveIndex()\n return i === -1 ? null : this.sources[i]\n }\n\n get hasPlaylist(): boolean {\n // External mode: the app owns the \"playlist\" — render the nav UI even for\n // a single source (next intents are emitted, not executed).\n return this.sources.length > 1 || this.externalAdvance\n }\n\n get hasNext(): boolean {\n if (this.externalAdvance) return true\n if (this.shuffleMode) return this.sources.length > 1\n return this.nextAliveIndex() !== -1\n }\n\n get hasPrevious(): boolean {\n if (this.externalAdvance) return true\n return this.playlistOptions.loop ? this.sources.length > 1 : this.currentIndex > 0\n }\n\n /** Shuffle mode: `next()` and auto-advance pick a random item. */\n get shuffle(): boolean {\n return this.shuffleMode\n }\n\n setShuffle(on: boolean): void {\n this.shuffleMode = on\n }\n\n /** Repeat mode: after the last item the list starts over instead of ending. */\n get repeat(): boolean {\n return this.playlistOptions.loop\n }\n\n setRepeat(on: boolean): void {\n this.playlistOptions.loop = on\n }\n\n /** External mode: emit the intent instead of switching. Returns true when handled. */\n private emitAdvanceRequest(direction: 'next' | 'previous' | 'select' | 'ended', index: number | null): boolean {\n if (!this.externalAdvance) return false\n const known = index !== null && index >= 0 && index < this.sources.length\n this.emitter.emit('advancerequest', {\n direction,\n index: known ? index : null,\n source: known ? this.sources[index] : null,\n })\n return true\n }\n\n next(): void {\n if (!this.hasNext) return\n if (this.emitAdvanceRequest('next', this.sources.length > 1 ? (this.currentIndex + 1) % this.sources.length : null)) return\n if (this.shuffleMode && this.sources.length > 1) {\n // Random pick among non-dead candidates (falls back to any other item\n // when everything else is marked dead — better than a dead end).\n const alive = this.sources\n .map((s, i) => ({ s, i }))\n .filter(({ s, i }) => i !== this.currentIndex && !this.deadSources.has(s))\n const pool = alive.length > 0 ? alive.map(({ i }) => i) : this.sources.map((_, i) => i).filter((i) => i !== this.currentIndex)\n this.playItem(pool[Math.floor(Math.random() * pool.length)])\n return\n }\n const i = this.nextAliveIndex()\n if (i !== -1) this.playItem(i)\n }\n\n previous(): void {\n if (!this.hasPrevious) return\n if (this.emitAdvanceRequest('previous', this.sources.length > 1 ? (this.currentIndex - 1 + this.sources.length) % this.sources.length : null)) return\n this.playItem((this.currentIndex - 1 + this.sources.length) % this.sources.length)\n }\n\n /** Jump to a playlist item and start playback (external mode: emits the intent instead). */\n playItem(index: number): void {\n if (index < 0 || index >= this.sources.length) return\n if (index !== this.currentIndex && this.emitAdvanceRequest('select', index)) return\n void this.loadItem(index, true).then(() => {\n this.emitter.emit('playlistitemchange', { source: this.sources[index], index })\n })\n }\n\n togglePlaylistPanel(): void {\n this.scenesPanel.hide()\n this.playlistPanel.toggle()\n }\n\n toggleScenesPanel(): void {\n this.playlistPanel.hide()\n this.scenesPanel.toggle()\n }\n\n /** Thumbnail track of the current source (used by the seek tooltip and the scenes panel). */\n get thumbnails(): ThumbnailTrack | null {\n return this.thumbTrack\n }\n\n /**\n * Replace what the player is playing. A single source resets the playlist\n * to one item; an array installs a new playlist.\n */\n load(source: VideoSource | VideoSource[], startIndex = 0): void {\n this.sources = Array.isArray(source) ? [...source] : [source]\n this.playlistPanel.rebuild()\n void this.loadItem(clamp(startIndex, 0, this.sources.length - 1), false)\n }\n\n // === fullscreen / pip =================================================\n\n get isFullscreen(): boolean {\n return document.fullscreenElement === this.container\n }\n\n async toggleFullscreen(): Promise<void> {\n try {\n if (this.isFullscreen) {\n await document.exitFullscreen()\n } else if (this.container.requestFullscreen) {\n await this.container.requestFullscreen()\n } else {\n // iOS Safari: only the video element can go fullscreen.\n const v = this.video as HTMLVideoElement & { webkitEnterFullscreen?: () => void }\n v.webkitEnterFullscreen?.()\n }\n } catch {\n // Rejected by policy (no gesture) or unsupported — not an app error.\n }\n }\n\n async togglePip(): Promise<void> {\n try {\n if (document.pictureInPictureElement === this.video) {\n await document.exitPictureInPicture()\n } else {\n await this.video.requestPictureInPicture()\n }\n } catch {\n // PiP rejected (policy/unsupported/disabled) — ignore.\n }\n }\n\n // === chapters =========================================================\n\n /** Normalized chapters of the current source (after metadata is known). */\n get chapterList(): NormalizedChapter[] {\n return this.chapters\n }\n\n get chapter(): NormalizedChapter | null {\n return this.currentChapter\n }\n\n // === scene types (groups) =============================================\n\n /** Typed scene breakdowns attached to the current source (may be empty). */\n get sceneTypes(): SceneGroup[] {\n return this.sceneGroupList\n }\n\n /** Currently selected scene type, or null when there are no groups. */\n get activeSceneType(): SceneGroup | null {\n return this.sceneGroupList.find((g) => g.id === this.activeSceneGroupId) ?? null\n }\n\n /** Switch the active scene type — rebuilds the timeline segments + scenes panel. */\n setSceneGroup(id: string): void {\n if (id === this.activeSceneGroupId || !this.sceneGroupList.some((g) => g.id === id)) return\n this.activeSceneGroupId = id\n this.applyActiveSceneGroup()\n this.emitter.emit('scenetypechange', { group: this.activeSceneType })\n }\n\n /**\n * Best duration currently known: the media's real one once metadata is in,\n * else the declared `source.duration` (lets scene groups and the seek bar\n * render before the first play), else NaN.\n */\n private get knownDuration(): number {\n const d = this.video.duration\n if (Number.isFinite(d) && d > 0) return d\n const declared = this.source?.duration\n return typeof declared === 'number' && declared > 0 ? declared : NaN\n }\n\n /** Normalize the active group's scenes into `chapters` and refresh the UI. */\n private applyActiveSceneGroup(): void {\n this.chapters = normalizeChapters(this.activeSceneType?.scenes ?? [], this.knownDuration)\n this.currentChapter = null\n this.controls.progress.setChapters(this.chapters)\n this.controls.syncFeatureButtons()\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n }\n\n // === actions ==========================================================\n\n /**\n * Share the current video: native share sheet when the device has one,\n * otherwise the `action` event with `id: \"share\"` so the app can show\n * its own dialog.\n */\n async share(): Promise<void> {\n const data = {\n title: this.source?.title ?? document.title,\n url: window.location.href,\n }\n if (typeof navigator.share === 'function') {\n try {\n await navigator.share(data)\n } catch {\n /* user dismissed the sheet — not an error */\n }\n return\n }\n this.emitter.emit('action', { id: 'share' })\n }\n\n /** Highlight the like or dislike button (`null` clears both). */\n setLikeState(state: 'like' | 'dislike' | null): void {\n this.controls.setLikeState(state)\n }\n\n /** Update like/dislike counters shown as tooltips above the buttons (`undefined` leaves one untouched). */\n setLikeCounts(likeCount?: number | string, dislikeCount?: number | string): void {\n this.controls.setLikeCounts(likeCount, dislikeCount)\n }\n\n // === pause screen =====================================================\n\n /** Inject custom pause-screen content (used by the Vue wrapper's slot). */\n setPauseScreenContent(element: HTMLElement | null): void {\n this.pauseScreen.setCustomContent(element)\n }\n\n // === teardown =========================================================\n\n destroy(): void {\n if (this.destroyed) return\n this.destroyed = true\n this.loadToken++\n this.adManager?.destroy()\n this.controls.destroy()\n this.sourceController?.destroy()\n this.abort.abort()\n if (this.idleTimer) clearTimeout(this.idleTimer)\n if (this.peekTimer) clearTimeout(this.peekTimer)\n if (this.retryTimer) clearTimeout(this.retryTimer)\n if (this.persistTimer) {\n // Flush a pending debounced write so the last volume isn't lost.\n clearTimeout(this.persistTimer)\n this.persistTimer = null\n this.persistState()\n }\n this.container.remove()\n releasePlayValve()\n this.emitter.emit('destroy', undefined)\n this.emitter.removeAll()\n }\n\n // === internals ========================================================\n\n private async loadItem(index: number, autoplay: boolean, skipAds = false): Promise<void> {\n const token = ++this.loadToken\n const source = this.sources[index]\n if (!source) return\n this.currentIndex = index\n\n // Tear down the previous source.\n this.sourceController?.destroy()\n this.sourceController = null\n this.chapters = []\n this.currentChapter = null\n this.sceneGroupList = []\n this.activeSceneGroupId = ''\n this.progressiveQuality = -1\n this.playedOnce = false\n this.lastPlayheadTime = 0\n this.errorResumeTime = 0\n this.pendingSeek = null\n this.endPeek()\n for (const track of [...this.video.querySelectorAll('track')]) track.remove()\n this.adManager?.resetForNewSource()\n this.related.hide()\n this.upNext.hide()\n this.pauseScreen.hide()\n this.scenesPanel.hide()\n this.thumbTrack = null\n // NB: don't clear the error overlay here — during a retry the reload must\n // keep it visible (spinner) until playback actually starts (canplay/playing\n // /seeked handler clears it), otherwise the controls flash for a frame.\n this.decodeRecoveries = 0\n\n // Per-source UI state.\n this.video.poster = posterSrc(source.poster) ?? ''\n this.poster.setSource(source)\n this.pauseScreen.setSource(source)\n this.controls.progress.setChapters([])\n this.controls.progress.setThumbnails(null)\n this.controls.progress.setHeatmap([])\n // Seed the seek bar with the declared duration so it is interactive BEFORE\n // the first play — a pre-start scrub stores a pending seek and starts\n // playback from that spot. The real duration replaces it on loadedmetadata.\n if (typeof source.duration === 'number' && source.duration > 0) {\n this.controls.progress.update(0, source.duration, 0)\n }\n if (autoplay) this.poster.hide()\n else this.poster.show()\n this.playlistPanel.rebuild()\n\n // Subtitle <track> elements (native rendering). Disabled by default —\n // a track only starts visible when it carries `default: true`.\n for (const sub of normalizeSubtitles(source.subtitles)) {\n const track = el('track', '', {\n kind: 'subtitles',\n src: sub.src,\n label: sub.label,\n ...(sub.srclang ? { srclang: sub.srclang } : {}),\n })\n if (sub.default) track.setAttribute('default', '')\n this.video.append(track)\n }\n\n // Effective media URL: progressive quality list wins over plain src.\n let src = source.src\n let type = source.type\n if (source.qualities && source.qualities.length > 0) {\n const initial = Math.max(0, source.qualities.findIndex((q) => q.src === source.src))\n this.progressiveQuality = initial\n src = source.qualities[initial].src\n type = source.qualities[initial].type ?? type\n }\n\n // While a pre-roll occupies the screen, let the browser buffer the first\n // fragments of the content in the background (hls.js does this on its\n // own; `auto` covers progressive sources).\n this.video.preload = this.adManager?.hasPendingPreRoll ? 'auto' : 'metadata'\n\n const controller = await attachSource(this.video, src, type, {\n onLevels: () => {\n if (token === this.loadToken) this.controls.syncFeatureButtons()\n },\n onLevelSwitch: (label) => this.emitter.emit('qualitychange', { label }),\n onError: (message, cause) => this.emitter.emit('error', { message, cause, fatal: true }),\n })\n if (token !== this.loadToken) {\n controller.destroy()\n return\n }\n this.sourceController = controller\n\n this.emitter.emit('sourcechange', { source, index })\n\n // Async extras — each guarded by the load token.\n if (source.thumbnails) {\n ThumbnailTrack.load(source.thumbnails)\n .then((track) => {\n if (token !== this.loadToken) return\n this.thumbTrack = track\n this.controls.progress.setThumbnails(track)\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n })\n .catch((cause) => this.emitter.emit('error', { message: 'Failed to load thumbnails track', cause }))\n }\n if (source.sceneGroups && source.sceneGroups.length > 0) {\n // Typed scene breakdowns: pick the first group, build the timeline from it.\n this.sceneGroupList = source.sceneGroups\n this.activeSceneGroupId = source.sceneGroups[0].id\n // Apply right away — the type-selector button and the segments must not\n // wait for metadata (with a not-yet-started HLS stream it never arrives\n // until play, and the button looked missing). Explicit scene ends and/or\n // the declared `source.duration` are enough to render; once the real\n // duration lands, re-apply to clamp against it.\n this.applyActiveSceneGroup()\n if (!(Number.isFinite(this.video.duration) && this.video.duration > 0)) {\n this.video.addEventListener('loadedmetadata', () => {\n if (token === this.loadToken) this.applyActiveSceneGroup()\n }, { once: true, signal: this.abort.signal })\n }\n } else if (source.chapters) {\n const apply = (raw: Parameters<typeof normalizeChapters>[0]) => {\n const set = () => {\n if (token !== this.loadToken) return\n this.chapters = normalizeChapters(raw, this.knownDuration)\n this.controls.progress.setChapters(this.chapters)\n this.controls.syncFeatureButtons()\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n }\n if (Number.isFinite(this.video.duration) && this.video.duration > 0) set()\n else this.video.addEventListener('loadedmetadata', set, { once: true, signal: this.abort.signal })\n }\n if (typeof source.chapters === 'string') {\n loadChaptersVtt(source.chapters)\n .then((parsed) => apply(parsed))\n .catch((cause) => this.emitter.emit('error', { message: 'Failed to load chapters track', cause }))\n } else {\n apply(source.chapters)\n }\n }\n\n const hm = source.heatmap\n const dense = hm && !Array.isArray(hm)\n const hasHeatmap = dense ? hm.values.length > 0 : Boolean(hm && hm.length > 0)\n if (hm && hasHeatmap && this.controlsOptions.heatmap) {\n const applyHeatmap = () => {\n if (token !== this.loadToken) return\n const values = dense\n ? buildHeatmapValuesDense(hm.values, hm.mode)\n : buildHeatmapValues(hm, this.video.duration)\n this.controls.progress.setHeatmap(values, dense ? (hm.smoothing ?? 1) : 1)\n }\n // Dense data is self-contained (bucket = duration/length), so it needs no\n // duration; sparse points need duration to bucket by time.\n if (dense || (Number.isFinite(this.video.duration) && this.video.duration > 0)) applyHeatmap()\n else this.video.addEventListener('loadedmetadata', applyHeatmap, { once: true, signal: this.abort.signal })\n }\n\n // `play()` handles its own failures (autoplay block → poster, hls attach\n // race → retry), so a bare call is safe here.\n if (autoplay) void this.play(skipAds)\n }\n\n private bindVideoEvents(): void {\n const { signal } = this.abort\n const v = this.video\n\n v.addEventListener('play', () => {\n this.playedOnce = true\n this.endPeek()\n this.poster.hide()\n this.pauseScreen.hide()\n if (!this.related.visible) this.related.hide()\n this.related.hide()\n this.upNext.hide()\n // Playback requested but not enough buffered yet (e.g. switching to the\n // next playlist item on a slow connection): show the loader instead of a\n // frozen poster. 'playing' hides it once real frames arrive.\n if (v.readyState < 3 /* HAVE_FUTURE_DATA */) this.spinner.hidden = false\n this.emitter.emit('play', undefined)\n this.scheduleIdle()\n }, { signal })\n\n v.addEventListener('pause', () => {\n this.spinner.hidden = true\n this.emitter.emit('pause', undefined)\n this.showControlsNow()\n if (this.video.ended || this.scrubbing || this.adPlaying || !this.playedOnce) return\n if (this.options.pauseScreen !== false) this.pauseScreen.show()\n if (this.options.related?.showOn?.includes('pause')) this.related.show()\n }, { signal })\n\n v.addEventListener('ended', () => {\n void this.handleEnded()\n }, { signal })\n\n v.addEventListener('timeupdate', () => {\n if (!this.adPlaying && v.currentTime > 0) this.lastPlayheadTime = v.currentTime\n this.emitter.emit('timeupdate', { currentTime: v.currentTime, duration: v.duration || 0 })\n this.adManager?.checkMidRolls(v.currentTime)\n const chapter = chapterAt(this.chapters, v.currentTime)\n if (chapter !== this.currentChapter) {\n this.currentChapter = chapter\n this.emitter.emit('chapterchange', { chapter })\n }\n }, { signal })\n\n v.addEventListener('progress', () => {\n this.emitter.emit('progress', { buffered: this.bufferedEnd })\n }, { signal })\n\n v.addEventListener('volumechange', () => {\n this.emitter.emit('volumechange', { volume: v.volume, muted: v.muted })\n // Debounced: dragging the slider fires dozens of volumechange per second —\n // no need to hit localStorage on every step.\n if (this.persistVolume) this.schedulePersist()\n }, { signal })\n\n v.addEventListener('loadedmetadata', () => {\n // A pre-start scrub (seek bar seeded with the declared duration) lands here.\n if (this.pendingSeek !== null) {\n const t = clamp(this.pendingSeek, 0, Number.isFinite(v.duration) ? v.duration : this.pendingSeek)\n this.pendingSeek = null\n try { v.currentTime = t } catch { /* range not ready */ }\n }\n // Feed the real duration to the seek bar before the first timeupdate so\n // it is interactive pre-start even without a declared `source.duration`.\n if (!this.playedOnce && Number.isFinite(v.duration)) {\n this.controls.progress.update(v.currentTime, v.duration, this.bufferedEnd)\n }\n }, { signal })\n\n v.addEventListener('seeking', () => this.emitter.emit('seeking', { currentTime: v.currentTime }), { signal })\n v.addEventListener('seeked', () => this.emitter.emit('seeked', { currentTime: v.currentTime }), { signal })\n\n v.addEventListener('waiting', () => {\n this.spinner.hidden = false\n }, { signal })\n for (const eventName of ['playing', 'canplay', 'seeked'] as const) {\n v.addEventListener(eventName, () => {\n this.spinner.hidden = true\n this.decodeRecoveries = 0\n // Only *actual playback* dismisses the fatal-error overlay. A transient\n // `canplay`/`seeked` during a still-failing reload must not flash it away\n // (that caused the controls to blink before the error re-appeared).\n if (eventName === 'playing') this.clearError()\n }, { signal })\n }\n\n v.addEventListener('error', () => {\n const mediaError = v.error\n if (!mediaError) return\n // While hls.js drives the element (MSE), the native <video> can emit\n // transient decode errors (e.g. DEMUXER_ERROR_COULD_NOT_PARSE on a\n // viewport/visibility change) that hls.js recovers from on its own via\n // its ERROR event. Surfacing those as fatal here is wrong — let the HLS\n // controller own error handling and only react to native sources.\n if (this.sourceController?.kind === 'hls') return\n // No src attribute → the error came from our own teardown, ignore it.\n if (v.getAttribute('src') === null && !v.currentSrc) return\n\n // Native playback (incl. Safari/iOS HLS) can throw transient decode/parse\n // errors when seeking backward (DEMUXER_ERROR_COULD_NOT_PARSE). Try a\n // silent reload-and-restore a couple of times before surfacing an error.\n const decodeish = mediaError.code === MediaError.MEDIA_ERR_DECODE || /DEMUXER|PARSE|DECODE/i.test(mediaError.message || '')\n if (decodeish && this.decodeRecoveries < 2 && Number.isFinite(v.duration)) {\n this.decodeRecoveries++\n const at = v.currentTime\n const wasPlaying = !v.paused\n v.load()\n const restore = () => {\n try { v.currentTime = at } catch { /* range not ready yet */ }\n if (wasPlaying) void v.play().catch(logPlayRejection)\n }\n v.readyState >= 1 ? restore() : v.addEventListener('loadedmetadata', restore, { once: true, signal: this.abort.signal })\n return\n }\n\n this.emitter.emit('error', { message: mediaError.message || `Media error (code ${mediaError.code})`, cause: mediaError, fatal: true })\n }, { signal })\n\n v.addEventListener('enterpictureinpicture', () => this.emitter.emit('pipchange', { active: true }), { signal })\n v.addEventListener('leavepictureinpicture', () => this.emitter.emit('pipchange', { active: false }), { signal })\n\n document.addEventListener('fullscreenchange', () => {\n this.emitter.emit('fullscreenchange', { active: this.isFullscreen })\n this.container.classList.toggle('imp-player--fullscreen', this.isFullscreen)\n }, { signal })\n\n // iOS Safari video-element fullscreen (webkitEnterFullscreen) never touches\n // document.fullscreenElement — sync state/icon from the webkit events.\n v.addEventListener('webkitbeginfullscreen', () => {\n this.emitter.emit('fullscreenchange', { active: true })\n this.container.classList.add('imp-player--fullscreen')\n }, { signal })\n v.addEventListener('webkitendfullscreen', () => {\n this.emitter.emit('fullscreenchange', { active: false })\n this.container.classList.remove('imp-player--fullscreen')\n }, { signal })\n }\n\n private async handleEnded(): Promise<void> {\n this.emitter.emit('ended', undefined)\n this.showControlsNow()\n if (this.adManager) {\n await this.adManager.playPostRoll()\n if (this.destroyed) return\n }\n if (this.autoAdvanceEnabled && this.hasNext) {\n // External mode: the end-of-video advance is an intent too ('ended'\n // direction so the app can distinguish it from a next-button click).\n if (this.emitAdvanceRequest('ended', this.sources.length > 1 ? (this.currentIndex + 1) % this.sources.length : null)) return\n this.next()\n return\n }\n // Autoplay off: offer the next item with a \"play next\" card instead.\n if (!this.autoAdvanceEnabled && this.hasNext) {\n this.upNext.show()\n return\n }\n if (this.options.related?.showOn?.includes('ended') ?? Boolean(this.options.related)) {\n this.related.show()\n }\n }\n\n /** Autoplay-next state (gear switch). Off → show the up-next card on end. */\n get autoAdvance(): boolean {\n return this.autoAdvanceEnabled\n }\n\n setAutoAdvance(on: boolean): void {\n this.autoAdvanceEnabled = on\n if (on) this.upNext.hide()\n if (this.persistAutoAdvance) this.persistState()\n }\n\n // === controls visibility ==============================================\n\n private bindIdleHide(): void {\n const { signal } = this.abort\n const reset = () => {\n this.showControlsNow()\n this.scheduleIdle()\n }\n // Mouse/keyboard reveal-on-activity. Touch is deliberately excluded here:\n // a tap is handled by the pointerup tap-toggle on the video, so letting\n // pointerdown also fire reset() would make one tap both show *and* (via the\n // tap handler) hide the controls — they'd never actually toggle.\n this.container.addEventListener('pointermove', (e) => {\n if (e.pointerType === 'touch') return\n reset()\n // Before the first play the poster covers the bar — lift it on hover.\n if (!this.playedOnce) this.revealPeek(false)\n }, { signal })\n this.container.addEventListener('pointerdown', (e) => {\n if (e.pointerType !== 'touch') reset()\n }, { signal })\n this.container.addEventListener('keydown', reset, { signal })\n this.container.addEventListener('pointerleave', (e) => {\n if (e.pointerType === 'touch') return\n // Keep the controls (and their open dropdown) up while a menu is open.\n if (this.controls.anyMenuOpen()) return\n if (!this.playedOnce) this.endPeek()\n else if (!this.video.paused) this.container.classList.add('imp-player--idle')\n }, { signal })\n }\n\n private showControlsNow(): void {\n this.container.classList.remove('imp-player--idle')\n }\n\n /**\n * Before the first play, lift the control bar above the poster (\"peek\").\n * `autoHide` (touch) hides it after 3s of no interaction; on mouse the caller\n * clears it on pointer-leave instead. No-op once playback has started or when\n * the behavior is disabled via `controls.revealBeforePlay: false`.\n */\n private revealPeek(autoHide: boolean): void {\n if (this.playedOnce || !this.controlsOptions.revealBeforePlay) return\n this.container.classList.add('imp-player--peek')\n if (this.peekTimer) clearTimeout(this.peekTimer)\n this.peekTimer = null\n if (autoHide) {\n this.peekTimer = setTimeout(() => {\n // Don't auto-hide out from under an open dropdown.\n if (!this.playedOnce && !this.destroyed && !this.controls.anyMenuOpen()) {\n this.container.classList.remove('imp-player--peek')\n }\n }, 3000)\n }\n }\n\n private endPeek(): void {\n if (this.peekTimer) clearTimeout(this.peekTimer)\n this.peekTimer = null\n this.container.classList.remove('imp-player--peek')\n }\n\n /**\n * Poster background tapped on touch: reveal the control bar instead of\n * starting playback. Returns true when handled (so the poster skips `play()`).\n */\n posterTapReveal(): boolean {\n if (this.playedOnce || !this.controlsOptions.revealBeforePlay) return false\n this.revealPeek(true)\n return true\n }\n\n private scheduleIdle(): void {\n if (this.idleTimer) clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => {\n if (!this.video.paused && !this.destroyed) {\n this.container.classList.add('imp-player--idle')\n }\n }, this.controlsOptions.hideDelay)\n }\n\n // === keyboard =========================================================\n\n private bindKeyboard(): void {\n this.container.addEventListener('keydown', (e) => {\n const target = e.target as HTMLElement\n const interactive = target.closest('input, select, textarea, [contenteditable]')\n if (interactive) return\n const isButton = target.closest('button') !== null\n\n switch (e.key) {\n case ' ':\n case 'k':\n if (e.key === ' ' && isButton) return\n e.preventDefault()\n this.togglePlay()\n break\n case 'ArrowLeft':\n e.preventDefault()\n this.skip(-this.seekStep)\n break\n case 'ArrowRight':\n e.preventDefault()\n this.skip(this.seekStep)\n break\n case 'ArrowUp':\n e.preventDefault()\n this.setVolume(this.volume + 0.1)\n break\n case 'ArrowDown':\n e.preventDefault()\n this.setVolume(this.volume - 0.1)\n break\n case 'm':\n this.toggleMute()\n break\n case 'f':\n void this.toggleFullscreen()\n break\n case 'Home':\n this.seek(0)\n break\n case 'End':\n this.seek(this.duration)\n break\n default:\n if (/^[0-9]$/.test(e.key) && Number.isFinite(this.video.duration)) {\n this.seek((Number(e.key) / 10) * this.video.duration)\n }\n }\n }, { signal: this.abort.signal })\n }\n}\n"],"names":["el","tag","className","attrs","node","key","value","applyIcon","target","icon","coverImg","src","alt","img","iconButton","label","btn","previewMetaEl","items","base","row","item","i","text","chunk","ic","setIcon","clamp","min","max","formatTime","seconds","s","m","h","mm","ss","parseVttTime","raw","ms","parseVttCues","cues","blocks","block","lines","l","timingIndex","rawStart","rawEnd","start","end","cueText","resolveUrl","url","Emitter","event","fn","set","off","payload","err","svg","body","viewBox","defaultIcons","registry","defaultLabels","registerLocale","code","labels","registerLocales","map","getLocale","registeredLanguages","smooth","kernel","n","_","acc","k","j","idx","percentile","sorted","p","normalize","mode","v","sm","a","b","FLOOR","buildHeatmapValues","points","duration","buckets","hasData","point","buildHeatmapValuesDense","values","maxBuckets","out","counts","sum","heatmapPath","smoothing","width","height","step","at","c","y0","d","x0p","y0p","x1","y1","x2","y2","x3p","y3p","cp1x","cp1y","cp2x","cp2y","resolveVast","roll","opts","fetchAndParse","tagUrl","depth","controller","timer","xmlText","res","doc","ad","collectTracking","wrapper","nextTag","textOf","inline","linear","media","pickMediaFile","parseDuration","parseSkipOffset","_a","_b","playable","f","progressive","pool","pick","pct","firePixels","urls","AdManager","host","options","currentTime","due","kind","rolls","run","content","wasPlaying","cause","error","resolve","layer","video","ac","signal","spinner","hud","badge","countdown","actions","visitBtn","skipBtn","skipAfter","fired","fireOnce","lastProgress","lastTime","watchdog","cleanup","finish","skipped","t","remaining","onAdClick","adStarted","pauseEmitted","e","normalizeChapters","chapters","result","loadChaptersVtt","cue","chapterAt","time","chapter","isHlsSource","type","hlsModule","loadHls","globalHls","attachSource","cb","native","HlsCtor","attachHls","nativeController","hls","index","level","_e","data","mediaRecoveries","networkRecoveries","otherRecovered","ThumbnailTrack","vttUrl","rawSrc","fragment","entry","lo","hi","mid","CHEVRON_RIGHT","CHEVRON_LEFT","Menu","anchor","sections","section","title","entries","_title","chevron","toggle","sw","next","back","iconBox","ProgressBar","ariaLabel","list","filled","cursor","ch","root","fill","track","bufferedEnd","ratio","span","progress","rect","hasChapter","x","half","Controls","player","icons","left","right","spacer","seekCfg","seekBack","seekFwd","volumeWrap","ca","place","subsPlace","qualityPlace","speedPlace","wrap","listBtn","pipBtn","insertion","requested","keys","seen","modifier","dir","num","r","active","g","o","card","kicker","previewPoster","posterSrc","thumb","cr","pad","cardW","centered","_btn","initial","tip","likeCount","dislikeCount","state","custom","moreBtn","runMap","simpleItems","order","rank","overflowedSorted","it","rate","compact","centerLayout","useCenter","inCenter","ok","byPriority","guard","hasContent","rowRight","effective","except","menu","dispose","PosterOverlay","big","source","full","PauseScreen","heading","element","parts","sponsor","channel","RelatedOverlay","close","UpNextOverlay","upPoster","dur","play","PlaylistPanel","layout","playlistTitle","header","name","tools","thumbSrc","meta","itemTitle","ScenesPanel","thumbs","inner","current","playValveCount","onBenignPlayAbort","acquirePlayValve","releasePlayValve","logPlayRejection","normalizeSubtitles","input","defaultControls","Player","mount","rawControls","speed","saved","styling","savedAV","retryLabel","errorActions","middle","bottom","adConfig","fatal","ranges","skipAds","token","throttle","now","resumeAt","declared","delta","volume","muted","qualities","q","tracks","on","direction","known","alive","startIndex","id","autoplay","sub","message","apply","parsed","hm","dense","hasHeatmap","applyHeatmap","eventName","mediaError","restore","reset","autoHide","isButton"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.cjs","sources":["../src/core/dom.ts","../src/core/events.ts","../src/core/icons.ts","../src/core/localeRegistry.ts","../src/features/heatmap.ts","../src/features/ads/vast.ts","../src/features/ads/manager.ts","../src/features/chapters.ts","../src/features/hls.ts","../src/features/thumbnails.ts","../src/ui/menu.ts","../src/ui/progress.ts","../src/ui/controls.ts","../src/ui/overlays.ts","../src/ui/playlistPanel.ts","../src/ui/scenesPanel.ts","../src/player.ts"],"sourcesContent":["/** DOM helpers. All player markup is built through these — no innerHTML templates. */\n\nimport type { IconSource, PreviewMetaItem } from '../types'\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string,\n attrs?: Record<string, string>,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className) node.className = className\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, value)\n }\n return node\n}\n\n/**\n * Render an icon into `target`, honoring its kind: raw SVG (set as innerHTML)\n * or an image URL (an `<img class=\"imp-icon-img\">`). A bare string is raw SVG\n * when it starts with `<`, otherwise an image URL.\n */\nexport function applyIcon(target: HTMLElement, icon: IconSource): void {\n target.textContent = ''\n if (typeof icon === 'object') {\n if ('url' in icon) target.append(el('img', 'imp-icon-img', { src: icon.url, alt: '' }))\n else target.innerHTML = icon.svg\n return\n }\n if (/^\\s*</.test(icon)) target.innerHTML = icon\n else target.append(el('img', 'imp-icon-img', { src: icon, alt: '' }))\n}\n\n/**\n * Real `<img>` filling its (positioned, overflow-hidden) container like\n * `background-size: cover`. Used for every poster/preview surface — a real\n * element (not a CSS background) is lazy-loadable, LCP-eligible and keeps\n * the markup identical between the lazy placeholder and the live player.\n */\nexport function coverImg(src: string, alt = ''): HTMLImageElement {\n const img = el('img', 'imp-cover-img', { alt, decoding: 'async' }) as HTMLImageElement\n img.src = src\n return img\n}\n\n/** Icon-only button with an accessible label. */\nexport function iconButton(className: string, label: string, icon: IconSource): HTMLButtonElement {\n const btn = el('button', `imp-btn ${className}`, { type: 'button', 'aria-label': label, title: label })\n applyIcon(btn, icon)\n return btn\n}\n\n/**\n * Build the preview-meta row (used by the next-video hover card and the end\n * overlay). Each chunk renders as a `<span>`; a chunk with an `icon` gets an\n * inline leading glyph (e.g. a user icon before \"Uploaded by user\"). Chunks are\n * separated by a `·` via CSS. `base` is the BEM block, e.g. `\"imp-upnext\"`.\n */\nexport function previewMetaEl(items: PreviewMetaItem[], base: string): HTMLElement {\n const row = el('div', `${base}__meta`)\n items.forEach((item, i) => {\n // A real space between chunks is the only line-break opportunity at the\n // separator: the `·` itself is glued to the end of the previous chunk (CSS\n // ::after), so it never orphans at the start of a wrapped line. Text inside\n // a chunk still wraps word-by-word at its own spaces.\n if (i > 0) row.append(document.createTextNode(' '))\n const text = typeof item === 'string' ? item : item.text\n const icon = typeof item === 'string' ? undefined : item.icon\n const chunk = el('span', `${base}__meta-chunk`)\n if (icon) {\n const ic = el('span', `${base}__meta-icon`)\n applyIcon(ic, icon)\n chunk.append(ic)\n }\n chunk.append(document.createTextNode(text))\n row.append(chunk)\n })\n return row\n}\n\nexport function setIcon(btn: HTMLButtonElement, icon: IconSource, label?: string): void {\n applyIcon(btn, icon)\n if (label !== undefined) {\n btn.setAttribute('aria-label', label)\n btn.setAttribute('title', label)\n }\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/** `73` → `\"1:13\"`, `3673` → `\"1:01:13\"`. Infinity/NaN → `\"0:00\"`. */\nexport function formatTime(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00'\n const s = Math.floor(seconds % 60)\n const m = Math.floor((seconds / 60) % 60)\n const h = Math.floor(seconds / 3600)\n const mm = h > 0 ? String(m).padStart(2, '0') : String(m)\n const ss = String(s).padStart(2, '0')\n return h > 0 ? `${h}:${mm}:${ss}` : `${mm}:${ss}`\n}\n\n/** Parse `\"00:01:02.500\"` / `\"01:02.500\"` VTT timestamps into seconds. */\nexport function parseVttTime(raw: string): number | null {\n const m = raw.trim().match(/^(?:(\\d+):)?(\\d{1,2}):(\\d{2})(?:[.,](\\d{1,3}))?$/)\n if (!m) return null\n const h = m[1] ? Number(m[1]) : 0\n const min = Number(m[2])\n const s = Number(m[3])\n const ms = m[4] ? Number(m[4].padEnd(3, '0')) : 0\n return h * 3600 + min * 60 + s + ms / 1000\n}\n\n/** Tiny WebVTT cue parser — enough for chapters and thumbnail tracks. */\nexport function parseVttCues(text: string): Array<{ start: number; end: number; text: string }> {\n const cues: Array<{ start: number; end: number; text: string }> = []\n // Normalize newlines, drop the WEBVTT header block, split into cue blocks.\n const blocks = text.replace(/\\r\\n?/g, '\\n').split(/\\n\\n+/)\n for (const block of blocks) {\n const lines = block.split('\\n').filter((l) => l.trim() !== '')\n if (lines.length === 0) continue\n let timingIndex = lines.findIndex((l) => l.includes('-->'))\n if (timingIndex === -1) continue\n const [rawStart, rawEnd] = lines[timingIndex].split('-->')\n const start = parseVttTime(rawStart)\n const end = parseVttTime((rawEnd ?? '').split(' ')[1] ?? rawEnd ?? '')\n ?? parseVttTime(rawEnd ?? '')\n if (start === null || end === null) continue\n const cueText = lines.slice(timingIndex + 1).join('\\n').trim()\n if (cueText) cues.push({ start, end, text: cueText })\n }\n return cues\n}\n\n/** Resolve `url` against the location of the VTT file it came from. */\nexport function resolveUrl(url: string, base: string): string {\n try {\n return new URL(url, new URL(base, window.location.href)).toString()\n } catch {\n return url\n }\n}\n","/** Minimal strictly-typed event emitter. No globals, no wildcard magic. */\nexport class Emitter<M> {\n private listeners = new Map<keyof M, Set<(payload: never) => void>>()\n\n /** Subscribe. Returns an unsubscribe function. */\n on<K extends keyof M>(event: K, fn: (payload: M[K]) => void): () => void {\n let set = this.listeners.get(event)\n if (!set) {\n set = new Set()\n this.listeners.set(event, set)\n }\n set.add(fn as (payload: never) => void)\n return () => this.off(event, fn)\n }\n\n once<K extends keyof M>(event: K, fn: (payload: M[K]) => void): () => void {\n const off = this.on(event, (payload) => {\n off()\n fn(payload)\n })\n return off\n }\n\n off<K extends keyof M>(event: K, fn: (payload: M[K]) => void): void {\n this.listeners.get(event)?.delete(fn as (payload: never) => void)\n }\n\n emit<K extends keyof M>(event: K, payload: M[K]): void {\n const set = this.listeners.get(event)\n if (!set) return\n for (const fn of [...set]) {\n try {\n ;(fn as (payload: M[K]) => void)(payload)\n } catch (err) {\n // A broken consumer listener must not break the player.\n console.error('[itube-player] listener error', err)\n }\n }\n }\n\n removeAll(): void {\n this.listeners.clear()\n }\n}\n","import type { IconName } from '../types'\n\nconst svg = (body: string, viewBox = '0 0 24 24') =>\n `<svg viewBox=\"${viewBox}\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">${body}</svg>`\n\n/** Default icon set. Every icon can be replaced via `options.icons`. */\nexport const defaultIcons: Record<IconName, string> = {\n play: svg('<path d=\"M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z\"/>'),\n pause: svg('<rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\"/><rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\"/>'),\n replay: svg('<path d=\"M12 5V2.5L7.5 6 12 9.5V7a5 5 0 1 1-5 5H5a7 7 0 1 0 7-7Z\"/>'),\n bigPlay: svg('<path d=\"M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.54-6.86a1.05 1.05 0 0 0 0-1.76L9.56 4.26C8.87 3.84 8 4.34 8 5.14Z\"/>'),\n volumeHigh: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z\"/><path d=\"M14.5 5.2a8 8 0 0 1 0 13.6v-2a6 6 0 0 0 0-9.6v-2Z\"/>'),\n volumeLow: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"M14.5 8.6a4.5 4.5 0 0 1 0 6.8v-1.9a2.5 2.5 0 0 0 0-3v-1.9Z\"/>'),\n volumeMute: svg('<path d=\"M4 9v6h3.5L12 19.5v-15L7.5 9H4Z\"/><path d=\"m15.3 9.3 1.4 1.4 1.4-1.4 1.4 1.4-1.4 1.4 1.4 1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4-1.4 1.4-1.4-1.4-1.4 1.4-1.4Z\"/>'),\n fullscreen: svg('<path d=\"M5 5h5v2H7v3H5V5Zm9 0h5v5h-2V7h-3V5ZM5 14h2v3h3v2H5v-5Zm12 0h2v5h-5v-2h3v-3Z\"/>'),\n fullscreenExit: svg('<path d=\"M10 10H5V8h3V5h2v5Zm4 0V5h2v3h3v2h-5Zm-4 4v5H8v-3H5v-2h5Zm4 0h5v2h-3v3h-2v-5Z\"/>'),\n pip: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h14V7H5Z\"/><rect x=\"12\" y=\"11\" width=\"6\" height=\"4\"/>'),\n settings: svg('<path d=\"M12 8.5A3.5 3.5 0 1 0 12 15.5 3.5 3.5 0 0 0 12 8.5Zm0 2a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z\"/><path d=\"M10.3 2.8 9.9 5.1a7 7 0 0 0-1.5.86l-2.2-.8-1.7 3 1.8 1.5a7 7 0 0 0 0 1.7l-1.8 1.5 1.7 3 2.2-.8c.46.36.97.65 1.5.86l.4 2.3h3.4l.4-2.3a7 7 0 0 0 1.5-.86l2.2.8 1.7-3-1.8-1.5a7 7 0 0 0 0-1.7l1.8-1.5-1.7-3-2.2.8a7 7 0 0 0-1.5-.86l-.4-2.3h-3.4Zm1.7 5.7a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Z\"/>'),\n speed: svg('<path d=\"M12 4a9 9 0 0 0-9 9 8.96 8.96 0 0 0 1.62 5.16l1.64-1.15A7 7 0 0 1 5 13a7 7 0 1 1 12.74 4.01l1.64 1.15A8.96 8.96 0 0 0 21 13a9 9 0 0 0-9-9Z\"/><path d=\"m15.6 8.3-3.95 3.13a1.5 1.5 0 1 0 1.92 1.92L16.7 9.4a.78.78 0 0 0-1.1-1.1Z\"/>'),\n scenes: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h3V7H5Zm5 0v10h9V7h-9Zm-5 3h3v1H5v-1Zm0 3h3v1H5v-1Z\"/>'),\n shuffle: svg('<path d=\"M4 6h2.6c1.5 0 2.9.7 3.8 1.9l4.1 5.4a2.75 2.75 0 0 0 2.2 1.1H19l-1.8-1.8 1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 16.4h-2.3a4.75 4.75 0 0 1-3.8-1.9L8.8 9.1A2.75 2.75 0 0 0 6.6 8H4V6Z\"/><path d=\"M19 7.6h-2.3c-.86 0-1.67.41-2.18 1.1l-.93 1.23-1.25-1.65.58-.77A4.75 4.75 0 0 1 16.7 5.6H19L17.2 3.8l1.4-1.4 4.2 4.2-4.2 4.2-1.4-1.4L19 7.6Z\" transform=\"translate(0 1.2)\"/>'),\n repeat: svg('<path d=\"M7 7h10v2.5L21 6l-4-3.5V5H5v6h2V7Zm10 10H7v-2.5L3 18l4 3.5V19h12v-6h-2v4Z\"/>'),\n subtitles: svg('<path d=\"M3 5h18v14H3V5Zm2 2v10h14V7H5Zm2 3h6v2H7v-2Zm8 0h2v2h-2v-2ZM7 14h2v2H7v-2Zm4 0h6v2h-6v-2Z\"/>'),\n list: svg('<path d=\"M4 6h2v2H4V6Zm4 0h12v2H8V6ZM4 11h2v2H4v-2Zm4 0h12v2H8v-2ZM4 16h2v2H4v-2Zm4 0h12v2H8v-2Z\"/>'),\n next: svg('<path d=\"M6 5.5v13c0 .77.84 1.25 1.5.85l9-6.5a1 1 0 0 0 0-1.7l-9-6.5c-.66-.4-1.5.08-1.5.85Z\"/><rect x=\"17\" y=\"5\" width=\"2.5\" height=\"14\" rx=\"1\"/>'),\n previous: svg('<path d=\"M18 5.5v13c0 .77-.84 1.25-1.5.85l-9-6.5a1 1 0 0 1 0-1.7l9-6.5c.66-.4 1.5.08 1.5.85Z\"/><rect x=\"4.5\" y=\"5\" width=\"2.5\" height=\"14\" rx=\"1\"/>'),\n // Thin circular-arrow (\"rotate\") icons — open center so the step number reads\n // clearly inside the ring (matches the reference skip ±N look).\n seekForward: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><polyline points=\"23 4 23 10 17 10\"/><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\"/></svg>',\n seekBack: '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><polyline points=\"1 4 1 10 7 10\"/><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\"/></svg>',\n like: svg('<path d=\"M9 21H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h4v11Zm2 0h7.1a2 2 0 0 0 2-1.6l1.3-7a2 2 0 0 0-2-2.4H14V5.5A2.5 2.5 0 0 0 11.5 3l-.5.1V10h-.9L11 21Z\"/>'),\n dislike: svg('<path d=\"M15 3h4a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-4V3Zm-2 0H5.9a2 2 0 0 0-2 1.6l-1.3 7a2 2 0 0 0 2 2.4H10v4.5A2.5 2.5 0 0 0 12.5 21l.5-.1V14h.9L13 3Z\"/>'),\n addTo: svg('<path d=\"M4 6h12v2H4V6Zm0 4h12v2H4v-2Zm0 4h8v2H4v-2Zm14 0v-4h2v4h4v2h-4v4h-2v-4h-4v-2h4Z\"/>'),\n share: svg('<path d=\"M18 8a3 3 0 1 0-2.83-4H15a3 3 0 0 0 .14 1.06L8.4 8.94a3 3 0 1 0 0 6.12l6.74 3.88A3 3 0 1 0 16 16.6l-6.74-3.88a3.03 3.03 0 0 0 0-1.44L16 7.4c.55.38 1.24.6 2 .6Z\"/>'),\n report: svg('<path d=\"M5 3h2v18H5V3Zm4 1h10l-2.5 4L19 12H9V4Z\"/>'),\n more: svg('<circle cx=\"12\" cy=\"5\" r=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/><circle cx=\"12\" cy=\"19\" r=\"2\"/>'),\n close: svg('<path d=\"m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6 10.6 12 5 6.4 6.4 5Z\"/>'),\n // Generic \"uploaded by a user\" glyph (head + shoulders) for preview meta.\n user: svg('<path d=\"M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm0 2c-3.6 0-8 1.8-8 4.4V21h16v-2.6c0-2.6-4.4-4.4-8-4.4Z\"/>'),\n // Generic \"channel\" glyph (play badge) for preview meta when a channel is set.\n channel: svg('<path d=\"M3 5h18a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm7 3.5v7l6-3.5-6-3.5Z\"/>'),\n}\n","import type { PlayerLabels } from '../types'\nimport { defaultLabels } from './labels'\n\n/**\n * Locale registry. The core only ships English; the main package entry\n * registers all built-in dictionaries, while `itube-modern-player/core`\n * leaves them out so size-conscious builds can register just one:\n *\n * ```ts\n * import { Player, registerLocale } from 'itube-modern-player/core'\n * import { locales } from 'itube-modern-player/locales'\n * registerLocale('ru', locales.ru)\n * ```\n */\nconst registry: Partial<Record<string, PlayerLabels>> = { en: defaultLabels }\n\nexport function registerLocale(code: string, labels: PlayerLabels): void {\n registry[code] = labels\n}\n\nexport function registerLocales(map: Partial<Record<string, PlayerLabels>>): void {\n for (const [code, labels] of Object.entries(map)) {\n if (labels) registry[code] = labels\n }\n}\n\n/** Resolve a registered locale with graceful fallback to English. */\nexport function getLocale(code: string | undefined): PlayerLabels {\n return (code && registry[code]) || defaultLabels\n}\n\n/** Codes currently registered (built-ins and custom). */\nexport function registeredLanguages(): string[] {\n return Object.keys(registry)\n}\n","import type { HeatmapPoint } from '../types'\n\n/** Gaussian-ish ±2 smoothing; edges clamp to the nearest sample (no end dip). */\nfunction smooth(raw: number[]): number[] {\n const kernel = [0.06, 0.24, 0.4, 0.24, 0.06]\n const n = raw.length\n return raw.map((_, i) =>\n kernel.reduce((acc, k, j) => {\n const idx = Math.min(n - 1, Math.max(0, i + j - 2))\n return acc + k * raw[idx]\n }, 0),\n )\n}\n\n/** Value at percentile `p` (0..1) of an already-ascending-sorted array. */\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0\n const idx = Math.min(sorted.length - 1, Math.max(0, Math.round((sorted.length - 1) * p)))\n return sorted[idx]\n}\n\n/**\n * Smooth + normalize raw bucket values into a 0..1 curve.\n * - `\"peaks\"`: divide by the max (replayed-spikes look);\n * - `\"retention\"`: divide by the 95th percentile so a lone start spike can't\n * crush the rest of the curve to a flat line.\n * A small floor keeps flat areas reading as \"watched\", not \"missing data\".\n */\nfunction normalize(raw: number[], mode: 'retention' | 'peaks'): number[] {\n if (raw.length === 0 || raw.every((v) => v <= 0)) return []\n const sm = smooth(raw)\n const max = mode === 'retention' ? percentile([...sm].sort((a, b) => a - b), 0.95) : Math.max(...sm)\n if (max <= 0) return []\n const FLOOR = 0.08\n return sm.map((v) => FLOOR + (1 - FLOOR) * Math.min(1, Math.max(0, v / max)))\n}\n\n/**\n * Sparse samples → normalized curve. Buckets the duration into `buckets` slots,\n * sums samples into them, then smooth+normalize as \"peaks\".\n */\nexport function buildHeatmapValues(points: HeatmapPoint[], duration: number, buckets = 100): number[] {\n if (points.length === 0 || !Number.isFinite(duration) || duration <= 0) return []\n const raw = new Array<number>(buckets).fill(0)\n let hasData = false\n for (const point of points) {\n if (!Number.isFinite(point.time) || point.time < 0 || point.time > duration) continue\n const value = Math.max(0, point.value)\n if (value === 0) continue\n raw[Math.min(buckets - 1, Math.floor((point.time / duration) * buckets))] += value\n hasData = true\n }\n if (!hasData) return []\n return normalize(raw, 'peaks')\n}\n\n/**\n * Dense per-bucket series (partner format) → normalized curve. Each value is one\n * equal time slot; no time field needed. `mode` picks the normalization.\n * Long feeds (2h video ≈ 1400 five-second buckets) are resampled down to\n * `maxBuckets` by averaging — the SVG spline stays light and smooth.\n */\nexport function buildHeatmapValuesDense(values: number[], mode: 'retention' | 'peaks' = 'peaks', maxBuckets = 200): number[] {\n let raw = values.map((v) => (Number.isFinite(v) ? Math.max(0, v) : 0))\n if (raw.length > maxBuckets) {\n const out = new Array<number>(maxBuckets).fill(0)\n const counts = new Array<number>(maxBuckets).fill(0)\n for (let i = 0; i < raw.length; i++) {\n const j = Math.min(maxBuckets - 1, Math.floor((i / raw.length) * maxBuckets))\n out[j] += raw[i]\n counts[j]++\n }\n raw = out.map((sum, j) => (counts[j] > 0 ? sum / counts[j] : 0))\n }\n return normalize(raw, mode)\n}\n\n/**\n * Build a SMOOTH SVG area path (Catmull-Rom → cubic Bézier) for normalized\n * values (0..1) in a `width`×`height` viewBox. Rendered with\n * `preserveAspectRatio=\"none\"`, so the numbers are arbitrary units, not pixels.\n */\nexport function heatmapPath(values: number[], smoothing = 1, width = 1000, height = 100): string {\n const n = values.length\n if (n === 0) return ''\n const step = width / (n - 1 || 1)\n // Catmull-Rom tangent length. `smoothing` 1 → the classic 1/6 (roundest);\n // 0 → straight segments. Clamped at the low end so it never goes negative.\n const k = Math.max(0, smoothing) / 6\n const at = (i: number): [number, number] => {\n const c = Math.min(n - 1, Math.max(0, i))\n return [c * step, height - values[c] * height]\n }\n\n const [, y0] = at(0)\n let d = `M 0 ${height} L 0 ${y0.toFixed(1)}`\n for (let i = 0; i < n - 1; i++) {\n const [x0p, y0p] = at(i - 1)\n const [x1, y1] = at(i)\n const [x2, y2] = at(i + 1)\n const [x3p, y3p] = at(i + 2)\n const cp1x = x1 + (x2 - x0p) * k\n // Clamp control-point y into the viewBox so the spline never overshoots\n // above the top edge (or below the baseline) on sharp value changes.\n const cp1y = Math.min(height, Math.max(0, y1 + (y2 - y0p) * k))\n const cp2x = x2 - (x3p - x1) * k\n const cp2y = Math.min(height, Math.max(0, y2 - (y3p - y1) * k))\n d += ` C ${cp1x.toFixed(1)} ${cp1y.toFixed(1)} ${cp2x.toFixed(1)} ${cp2y.toFixed(1)} ${x2.toFixed(1)} ${y2.toFixed(1)}`\n }\n d += ` L ${width} ${height} Z`\n return d\n}\n","import type { AdRoll, AdsOptions, ResolvedAd } from '../../types'\n\n/**\n * Minimal VAST 2/3/4 resolver — the same subset fluid-player consumes:\n * InLine Linear creatives, Wrapper redirects, MediaFiles, ClickThrough,\n * Impression and quartile tracking, skipoffset.\n */\nexport async function resolveVast(roll: AdRoll, opts: Required<Pick<AdsOptions, 'maxWrapperDepth' | 'requestTimeout'>>): Promise<ResolvedAd> {\n if (roll.src) {\n // Direct media file — no VAST round-trip needed.\n return {\n roll: roll.roll,\n mediaUrl: roll.src,\n clickThrough: roll.clickUrl,\n impressions: [],\n tracking: {},\n }\n }\n if (!roll.vastTag) throw new Error('Ad roll has neither \"vastTag\" nor \"src\"')\n return fetchAndParse(roll, roll.vastTag, opts, 0, { impressions: [], tracking: {} })\n}\n\ninterface Accumulated {\n impressions: string[]\n tracking: ResolvedAd['tracking']\n}\n\nasync function fetchAndParse(\n roll: AdRoll,\n tagUrl: string,\n opts: { maxWrapperDepth: number; requestTimeout: number },\n depth: number,\n acc: Accumulated,\n): Promise<ResolvedAd> {\n if (depth > opts.maxWrapperDepth) throw new Error('VAST wrapper depth limit exceeded')\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), opts.requestTimeout)\n let xmlText: string\n try {\n const res = await fetch(tagUrl, { signal: controller.signal, credentials: 'omit' })\n if (!res.ok) throw new Error(`VAST request failed (${res.status})`)\n xmlText = await res.text()\n } finally {\n clearTimeout(timer)\n }\n\n const doc = new DOMParser().parseFromString(xmlText, 'text/xml')\n if (doc.querySelector('parsererror')) throw new Error('VAST response is not valid XML')\n\n const ad = doc.querySelector('VAST > Ad')\n if (!ad) throw new Error('VAST response contains no ads')\n\n collectTracking(ad, acc)\n\n const wrapper = ad.querySelector(':scope > Wrapper')\n if (wrapper) {\n const nextTag = textOf(wrapper.querySelector('VASTAdTagURI'))\n if (!nextTag) throw new Error('VAST wrapper without VASTAdTagURI')\n return fetchAndParse(roll, nextTag, opts, depth + 1, acc)\n }\n\n const inline = ad.querySelector(':scope > InLine')\n if (!inline) throw new Error('VAST ad has neither InLine nor Wrapper')\n\n const linear = inline.querySelector('Creatives > Creative > Linear')\n if (!linear) throw new Error('VAST ad has no Linear creative')\n\n const media = pickMediaFile(linear)\n if (!media) throw new Error('VAST ad has no playable MediaFile')\n\n return {\n roll: roll.roll,\n mediaUrl: media.url,\n mediaType: media.type,\n clickThrough: textOf(linear.querySelector('VideoClicks > ClickThrough')) ?? roll.clickUrl,\n duration: parseDuration(textOf(linear.querySelector(':scope > Duration'))),\n skipOffset: parseSkipOffset(\n linear.getAttribute('skipoffset'),\n parseDuration(textOf(linear.querySelector(':scope > Duration'))),\n ),\n impressions: acc.impressions,\n tracking: acc.tracking,\n adTitle: textOf(inline.querySelector(':scope > AdTitle')) ?? undefined,\n }\n}\n\n/** Impressions, quartile tracking and click tracking accumulate across wrappers. */\nfunction collectTracking(ad: Element, acc: Accumulated): void {\n for (const node of ad.querySelectorAll('Impression')) {\n const url = textOf(node)\n if (url) acc.impressions.push(url)\n }\n for (const node of ad.querySelectorAll('Linear > TrackingEvents > Tracking')) {\n const event = node.getAttribute('event') as keyof ResolvedAd['tracking'] | null\n const url = textOf(node)\n if (!event || !url) continue\n if (!['start', 'firstQuartile', 'midpoint', 'thirdQuartile', 'complete', 'skip', 'pause', 'resume'].includes(event)) continue\n ;(acc.tracking[event] ??= []).push(url)\n }\n for (const node of ad.querySelectorAll('Linear > VideoClicks > ClickTracking')) {\n const url = textOf(node)\n if (url) (acc.tracking.click ??= []).push(url)\n }\n}\n\nfunction pickMediaFile(linear: Element): { url: string; type?: string } | null {\n const files = [...linear.querySelectorAll('MediaFiles > MediaFile')]\n .map((node) => ({\n url: textOf(node) ?? '',\n type: node.getAttribute('type') ?? undefined,\n delivery: node.getAttribute('delivery') ?? '',\n bitrate: Number(node.getAttribute('bitrate') ?? 0),\n apiFramework: node.getAttribute('apiFramework') ?? '',\n }))\n // VPAID needs a JS runtime we deliberately do not ship.\n .filter((f) => f.url && f.apiFramework.toUpperCase() !== 'VPAID')\n\n const playable = files.filter(\n (f) => !f.type || /(video\\/(mp4|webm|ogg)|application\\/(x-mpegurl|vnd\\.apple\\.mpegurl))/i.test(f.type),\n )\n if (playable.length === 0) return null\n // Mid-bitrate progressive file is the safest default.\n playable.sort((a, b) => a.bitrate - b.bitrate)\n const progressive = playable.filter((f) => f.delivery !== 'streaming')\n const pool = progressive.length > 0 ? progressive : playable\n const pick = pool[Math.floor(pool.length / 2)]\n return { url: pick.url, type: pick.type }\n}\n\n/** `\"00:00:15\"` / `\"00:15.250\"` → seconds. */\nfunction parseDuration(raw: string | null): number | undefined {\n if (!raw) return undefined\n const m = raw.trim().match(/^(?:(\\d+):)?(\\d{1,2}):(\\d{2})(?:\\.(\\d{1,3}))?$/)\n if (!m) return undefined\n return (m[1] ? Number(m[1]) * 3600 : 0) + Number(m[2]) * 60 + Number(m[3]) + (m[4] ? Number(m[4].padEnd(3, '0')) / 1000 : 0)\n}\n\n/** skipoffset is either a timestamp or a percentage of the ad duration. */\nfunction parseSkipOffset(raw: string | null, duration: number | undefined): number | undefined {\n if (!raw) return undefined\n const pct = raw.trim().match(/^(\\d+(?:\\.\\d+)?)%$/)\n if (pct) return duration !== undefined ? (Number(pct[1]) / 100) * duration : undefined\n return parseDuration(raw)\n}\n\n/** Fire a tracking pixel without caring about the response. */\nexport function firePixels(urls: string[] | undefined): void {\n if (!urls) return\n for (const url of urls) {\n try {\n new Image().src = url\n } catch {\n /* tracking must never throw */\n }\n }\n}\n\nfunction textOf(node: Element | null): string | null {\n const text = node?.textContent?.trim().replace(/^<!\\[CDATA\\[|\\]\\]>$/g, '').trim()\n return text || null\n}\n","import { el, formatTime } from '../../core/dom'\nimport type { Emitter } from '../../core/events'\nimport type { AdRoll, AdsOptions, IconSource, PlayerEventMap, PlayerLabels, ResolvedAd } from '../../types'\nimport { firePixels, resolveVast } from './vast'\n\ninterface AdHost {\n container: HTMLElement\n contentVideo: HTMLVideoElement\n emitter: Emitter<PlayerEventMap>\n labels: PlayerLabels\n closeIcon: IconSource\n}\n\n/**\n * Plays pre/mid/post rolls in a dedicated <video> layered over the content.\n * The content element is never touched, so its position, HLS session and\n * text tracks survive every ad break.\n */\nexport class AdManager {\n private opts: Required<Pick<AdsOptions, 'skipDelay' | 'maxWrapperDepth' | 'requestTimeout' | 'mediaTimeout' | 'playOn'>> & AdsOptions\n private playedRolls = new Set<AdRoll>()\n private sourcesSeen = 0\n private activeBreak: Promise<void> | null = null\n private layer: HTMLElement | null = null\n private adVideo: HTMLVideoElement | null = null\n private destroyed = false\n\n /**\n * Create (once) and \"bless\" the ad <video> synchronously inside the user\n * gesture that started playback. A bare `load()` during the gesture lets\n * the later `play()` succeed even though VAST fetches happen in between —\n * the same trick every ad SDK uses for iOS/strict autoplay policies.\n */\n private ensureAdVideo(): HTMLVideoElement {\n if (!this.adVideo) {\n this.adVideo = el('video', 'imp-ad__video', { playsinline: '' })\n try {\n this.adVideo.load()\n } catch {\n /* some engines throw on load() without src — harmless */\n }\n }\n return this.adVideo\n }\n\n constructor(private host: AdHost, options: AdsOptions) {\n this.opts = {\n skipDelay: 5,\n maxWrapperDepth: 3,\n requestTimeout: 8000,\n mediaTimeout: 10000,\n playOn: 'every',\n ...options,\n }\n }\n\n get adPlaying(): boolean {\n return this.activeBreak !== null\n }\n\n /** True while a pre-roll is still due for the current source. */\n get hasPendingPreRoll(): boolean {\n return this.pending('preRoll').length > 0\n }\n\n /** Called on every source change. Honors the `playOn` frequency setting. */\n resetForNewSource(): void {\n this.sourcesSeen++\n if (this.opts.playOn === 'first' && this.sourcesSeen > 1) {\n // Ads ran (or had their chance) on the first video — mute them for the rest.\n for (const roll of this.opts.adList) this.playedRolls.add(roll)\n return\n }\n this.playedRolls.clear()\n }\n\n async playPreRoll(): Promise<void> {\n await this.playRolls(this.pending('preRoll'))\n }\n\n async playPostRoll(): Promise<void> {\n await this.playRolls(this.pending('postRoll'))\n }\n\n /** Called from timeupdate; fires due mid-rolls. */\n checkMidRolls(currentTime: number): void {\n if (this.adPlaying) return\n const due = this.pending('midRoll').filter((roll) => roll.timer !== undefined && currentTime >= roll.timer)\n if (due.length > 0) void this.playRolls(due)\n }\n\n private pending(kind: AdRoll['roll']): AdRoll[] {\n return this.opts.adList.filter((roll) => roll.roll === kind && !this.playedRolls.has(roll))\n }\n\n private async playRolls(rolls: AdRoll[]): Promise<void> {\n if (rolls.length === 0 || this.destroyed) return\n if (this.activeBreak) return this.activeBreak\n\n // Synchronously, while we may still be inside a user gesture.\n this.ensureAdVideo()\n\n const run = (async () => {\n const content = this.host.contentVideo\n const wasPlaying = !content.paused && !content.ended\n content.pause()\n for (const roll of rolls) {\n this.playedRolls.add(roll)\n try {\n const ad = await resolveVast(roll, this.opts)\n await this.playAd(ad)\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause))\n this.host.emitter.emit('aderror', { roll, error })\n }\n if (this.destroyed) return\n }\n if (wasPlaying || rolls[0].roll === 'preRoll') {\n void content.play().catch(() => {})\n }\n })()\n\n this.activeBreak = run.finally(() => {\n this.activeBreak = null\n })\n return this.activeBreak\n }\n\n private playAd(ad: ResolvedAd): Promise<void> {\n return new Promise<void>((resolve) => {\n const { labels } = this.host\n const layer = el('div', 'imp-ad')\n // One persistent (gesture-blessed) element, fresh listeners per ad.\n const video = this.ensureAdVideo()\n const ac = new AbortController()\n const signal = ac.signal\n video.src = ad.mediaUrl\n video.muted = this.host.contentVideo.muted\n video.volume = this.host.contentVideo.volume\n\n const spinner = el('div', 'imp-spinner imp-ad__spinner')\n\n const hud = el('div', 'imp-ad__hud')\n const badge = el('span', 'imp-ad__badge')\n badge.textContent = ad.adTitle ? `${labels.adLabel} · ${ad.adTitle}` : labels.adLabel\n const countdown = el('span', 'imp-ad__countdown')\n hud.append(badge, countdown)\n\n const actions = el('div', 'imp-ad__actions')\n let visitBtn: HTMLButtonElement | null = null\n if (ad.clickThrough) {\n visitBtn = el('button', 'imp-ad__visit', { type: 'button' })\n visitBtn.textContent = labels.visitAdvertiser\n actions.append(visitBtn)\n }\n const skipBtn = el('button', 'imp-ad__skip', { type: 'button' })\n skipBtn.hidden = true\n actions.append(skipBtn)\n\n layer.append(video, spinner, hud, actions)\n this.host.container.append(layer)\n this.layer = layer\n\n const skipAfter = ad.skipOffset ?? this.opts.skipDelay\n const fired = new Set<string>()\n const fireOnce = (event: keyof ResolvedAd['tracking']) => {\n if (fired.has(event)) return\n fired.add(event)\n firePixels(ad.tracking[event])\n }\n\n // Watchdog: a creative that never starts (dead-slow ad CDN) or stalls\n // mid-play must not hold the content hostage with a black screen.\n let lastProgress = Date.now()\n let lastTime = -1\n const watchdog = setInterval(() => {\n // A deliberately paused ad (click-through, tap-to-play) is not a stall.\n if (video.paused) {\n lastProgress = Date.now()\n return\n }\n if (video.currentTime !== lastTime) {\n lastTime = video.currentTime\n lastProgress = Date.now()\n return\n }\n if (Date.now() - lastProgress >= this.opts.mediaTimeout) {\n this.host.emitter.emit('aderror', {\n roll: { roll: ad.roll },\n error: new Error(`Ad media stalled for ${this.opts.mediaTimeout} ms — skipping`),\n })\n cleanup()\n }\n }, 500)\n\n const cleanup = () => {\n clearInterval(watchdog)\n ac.abort()\n video.pause()\n video.removeAttribute('src')\n video.load()\n layer.remove()\n this.layer = null\n resolve()\n }\n\n const finish = (skipped: boolean) => {\n if (skipped) {\n fireOnce('skip')\n this.host.emitter.emit('adskip', { ad })\n } else {\n fireOnce('complete')\n }\n this.host.emitter.emit('adend', { ad })\n cleanup()\n }\n\n video.addEventListener('playing', () => {\n firePixels(ad.impressions)\n fireOnce('start')\n this.host.emitter.emit('adstart', { ad })\n }, { once: true, signal })\n\n video.addEventListener('playing', () => {\n spinner.hidden = true\n }, { signal })\n video.addEventListener('waiting', () => {\n spinner.hidden = false\n }, { signal })\n\n video.addEventListener('timeupdate', () => {\n const t = video.currentTime\n const d = video.duration\n if (Number.isFinite(d) && d > 0) {\n countdown.textContent = formatTime(Math.max(0, d - t))\n if (t / d >= 0.25) fireOnce('firstQuartile')\n if (t / d >= 0.5) fireOnce('midpoint')\n if (t / d >= 0.75) fireOnce('thirdQuartile')\n }\n if (skipAfter >= 0) {\n const remaining = Math.ceil(skipAfter - t)\n if (remaining > 0) {\n skipBtn.hidden = false\n skipBtn.disabled = true\n skipBtn.textContent = `${labels.skipAdIn} ${remaining}`\n } else {\n skipBtn.hidden = false\n skipBtn.disabled = false\n skipBtn.textContent = labels.skipAd\n }\n }\n }, { signal })\n\n video.addEventListener('ended', () => finish(false), { signal })\n video.addEventListener('error', () => {\n this.host.emitter.emit('aderror', {\n roll: { roll: ad.roll },\n error: new Error('Ad media failed to play'),\n })\n cleanup()\n }, { signal })\n\n skipBtn.addEventListener('click', () => finish(true))\n\n const onAdClick = () => {\n if (!ad.clickThrough) return\n fireOnce('click')\n this.host.emitter.emit('adclick', { ad })\n window.open(ad.clickThrough, '_blank', 'noopener')\n video.pause()\n }\n video.addEventListener('click', onAdClick, { signal })\n visitBtn?.addEventListener('click', onAdClick)\n\n // pause / resume: player events + VAST tracking pixels. Only after the\n // creative actually started, and never for the teardown pause.\n let adStarted = false\n let pauseEmitted = false\n video.addEventListener('playing', () => {\n adStarted = true\n }, { once: true, signal })\n video.addEventListener('pause', () => {\n if (video.ended || !layer.isConnected) return\n layer.classList.add('imp-ad--paused')\n if (adStarted && !pauseEmitted) {\n pauseEmitted = true\n firePixels(ad.tracking.pause)\n this.host.emitter.emit('adpause', { ad })\n }\n }, { signal })\n video.addEventListener('play', () => {\n layer.classList.remove('imp-ad--paused')\n if (pauseEmitted) {\n pauseEmitted = false\n firePixels(ad.tracking.resume)\n this.host.emitter.emit('adresume', { ad })\n }\n }, { signal })\n layer.addEventListener('click', (e) => {\n if (e.target === layer || layer.classList.contains('imp-ad--paused')) {\n if (video.paused) void video.play().catch(() => {})\n }\n })\n\n void video.play().catch(() => {\n // Autoplay rejected (no gesture) — surface the ad with a tap-to-play state.\n layer.classList.add('imp-ad--paused')\n })\n })\n }\n\n /** Skip-button icon access for potential theming. */\n get closeIcon(): IconSource {\n return this.host.closeIcon\n }\n\n destroy(): void {\n this.destroyed = true\n this.adVideo?.pause()\n this.layer?.remove()\n this.layer = null\n this.adVideo = null\n }\n}\n","import { parseVttCues } from '../core/dom'\nimport type { Chapter } from '../types'\n\n/** Chapter with a guaranteed `end`, ready for rendering. */\nexport interface NormalizedChapter extends Chapter {\n end: number\n}\n\n/**\n * Sort chapters, fill missing `end` values from the next chapter (or video duration).\n * Out-of-range chapters are clamped, empty ones dropped.\n */\nexport function normalizeChapters(chapters: Chapter[], duration: number): NormalizedChapter[] {\n const sorted = [...chapters].sort((a, b) => a.start - b.start)\n const result: NormalizedChapter[] = []\n for (let i = 0; i < sorted.length; i++) {\n const start = Math.max(0, sorted[i].start)\n if (Number.isFinite(duration) && start >= duration) continue\n let end = sorted[i].end ?? sorted[i + 1]?.start ?? duration\n if (Number.isFinite(duration)) end = Math.min(end, duration)\n // NaN end (no explicit end AND no known duration yet) would slip through\n // the <= comparison and render a broken segment — drop it instead.\n if (!Number.isFinite(end) || end <= start) continue\n result.push({ start, end, title: sorted[i].title })\n }\n return result\n}\n\nexport async function loadChaptersVtt(url: string): Promise<Chapter[]> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`Failed to load chapters VTT (${res.status})`)\n const text = await res.text()\n return parseVttCues(text).map((cue) => ({ start: cue.start, end: cue.end, title: cue.text }))\n}\n\nexport function chapterAt(chapters: NormalizedChapter[], time: number): NormalizedChapter | null {\n for (const chapter of chapters) {\n if (time >= chapter.start && time < chapter.end) return chapter\n }\n return null\n}\n","import type Hls from 'hls.js'\n\n/** A selectable rendition (HLS level or progressive quality). */\nexport interface Level {\n /** Index understood by `setLevel`. `-1` is reserved for \"auto\". */\n index: number\n label: string\n}\n\n/** Wraps whatever is feeding the <video> so the player can switch quality and tear down. */\nexport interface SourceController {\n kind: 'hls' | 'native'\n levels: Level[]\n /** Currently *selected* level (-1 = auto). */\n selected: number\n setLevel(index: number): void\n destroy(): void\n}\n\nexport function isHlsSource(src: string, type?: string): boolean {\n if (type) return /application\\/(x-mpegurl|vnd\\.apple\\.mpegurl)/i.test(type)\n return /\\.m3u8(\\?|#|$)/i.test(src)\n}\n\nlet hlsModule: typeof Hls | null | undefined\n\nasync function loadHls(): Promise<typeof Hls | null> {\n if (hlsModule !== undefined) return hlsModule\n // Script-tag setups (IIFE bundle) provide hls.js as a global instead.\n const globalHls = (globalThis as { Hls?: typeof Hls }).Hls\n if (globalHls) {\n hlsModule = globalHls\n return hlsModule\n }\n try {\n // The \"light\" build drops rarely-needed features (alt-audio, subtitles,\n // EME, low-latency) — far smaller than the full build for the common case\n // of plain VOD m3u8. Consumers who need them can provide window.Hls\n // (full build) which is picked up above.\n const mod = await import('hls.js/light')\n hlsModule = mod.default\n return hlsModule\n } catch {\n // Do NOT cache the failure: a transient network hiccup on the chunk would\n // otherwise permanently break HLS for the rest of the page's lifetime.\n return null\n }\n}\n\nexport interface AttachCallbacks {\n onLevels(levels: Level[]): void\n onLevelSwitch(label: string): void\n onError(message: string, cause?: unknown): void\n}\n\n/**\n * Point the <video> at `src`. m3u8 goes through hls.js (or native HLS on Safari),\n * everything else is plain `video.src`.\n */\nexport async function attachSource(\n video: HTMLVideoElement,\n src: string,\n type: string | undefined,\n cb: AttachCallbacks,\n): Promise<SourceController> {\n if (isHlsSource(src, type)) {\n const native = video.canPlayType('application/vnd.apple.mpegurl')\n const HlsCtor = native ? null : await loadHls()\n if (HlsCtor && HlsCtor.isSupported()) {\n return attachHls(HlsCtor, video, src, cb)\n }\n if (native) {\n video.src = src\n return nativeController(video)\n }\n cb.onError('HLS is not supported in this browser and hls.js could not be loaded.')\n return nativeController(video)\n }\n video.src = src\n return nativeController(video)\n}\n\nfunction nativeController(video: HTMLVideoElement): SourceController {\n return {\n kind: 'native',\n levels: [],\n selected: -1,\n setLevel() {},\n destroy() {\n video.removeAttribute('src')\n video.load()\n },\n }\n}\n\nfunction attachHls(\n HlsCtor: typeof Hls,\n video: HTMLVideoElement,\n src: string,\n cb: AttachCallbacks,\n): SourceController {\n const hls = new HlsCtor({ enableWorker: true })\n const controller: SourceController = {\n kind: 'hls',\n levels: [],\n selected: -1,\n setLevel(index: number) {\n controller.selected = index\n hls.currentLevel = index\n },\n destroy() {\n hls.destroy()\n video.removeAttribute('src')\n video.load()\n },\n }\n\n hls.on(HlsCtor.Events.MANIFEST_PARSED, () => {\n controller.levels = hls.levels\n .map((level, index) => ({\n index,\n label: level.height ? `${level.height}p` : `${Math.round(level.bitrate / 1000)} kbps`,\n }))\n .reverse()\n cb.onLevels(controller.levels)\n })\n\n hls.on(HlsCtor.Events.LEVEL_SWITCHED, (_e, data) => {\n const level = hls.levels[data.level]\n if (level) cb.onLevelSwitch(level.height ? `${level.height}p` : `${Math.round(level.bitrate / 1000)} kbps`)\n })\n\n // Bounded recovery so a persistently broken stream can't loop forever, but\n // transient fatals (e.g. a layout/visibility change toggling buffering) get\n // a real chance to self-heal before we surface an error.\n let mediaRecoveries = 0\n let networkRecoveries = 0\n let otherRecovered = false\n\n // Any successfully buffered fragment = the network is alive again; give\n // future hiccups a fresh retry budget.\n hls.on(HlsCtor.Events.FRAG_BUFFERED, () => {\n networkRecoveries = 0\n })\n\n hls.on(HlsCtor.Events.ERROR, (_e, data) => {\n if (!data.fatal) return\n switch (data.type) {\n case HlsCtor.ErrorTypes.NETWORK_ERROR:\n // Bounded: a dead manifest (404/DNS) must SURFACE, not spin forever —\n // unbounded startLoad() kept the spinner up and the player's fatal\n // error overlay never appeared for missing HLS streams.\n if (networkRecoveries < 3) {\n networkRecoveries++\n hls.startLoad()\n } else {\n cb.onError(`HLS network error: ${data.details}`, data)\n hls.destroy()\n }\n break\n case HlsCtor.ErrorTypes.MEDIA_ERROR:\n if (mediaRecoveries < 3) {\n mediaRecoveries++\n hls.recoverMediaError()\n } else {\n cb.onError(`HLS media error: ${data.details}`, data)\n hls.destroy()\n }\n break\n default:\n // Try a single full reload before declaring the stream dead.\n if (!otherRecovered) {\n otherRecovered = true\n try {\n hls.loadSource(src)\n hls.startLoad()\n } catch {\n cb.onError(`HLS fatal error: ${data.details}`, data)\n hls.destroy()\n }\n } else {\n cb.onError(`HLS fatal error: ${data.details}`, data)\n hls.destroy()\n }\n }\n })\n\n hls.loadSource(src)\n hls.attachMedia(video)\n return controller\n}\n","import { parseVttCues, resolveUrl } from '../core/dom'\nimport type { ThumbnailCue } from '../types'\n\n/**\n * Preview thumbnails described by a WebVTT file. Supports both sprite sheets\n * (`sprite.jpg#xywh=160,0,160,90`) and one-image-per-cue tracks.\n */\nexport class ThumbnailTrack {\n private constructor(private cues: ThumbnailCue[]) {}\n\n static async load(vttUrl: string): Promise<ThumbnailTrack> {\n const res = await fetch(vttUrl)\n if (!res.ok) throw new Error(`Failed to load thumbnails VTT (${res.status})`)\n const text = await res.text()\n const cues: ThumbnailCue[] = []\n for (const cue of parseVttCues(text)) {\n const [rawSrc, fragment] = cue.text.trim().split('#')\n if (!rawSrc) continue\n const entry: ThumbnailCue = {\n start: cue.start,\n end: cue.end,\n src: resolveUrl(rawSrc, vttUrl),\n }\n const m = fragment?.match(/xywh=(\\d+),(\\d+),(\\d+),(\\d+)/)\n if (m) entry.xywh = { x: Number(m[1]), y: Number(m[2]), w: Number(m[3]), h: Number(m[4]) }\n cues.push(entry)\n }\n cues.sort((a, b) => a.start - b.start)\n return new ThumbnailTrack(cues)\n }\n\n cueAt(time: number): ThumbnailCue | null {\n // Binary search — thumbnail tracks for long videos can hold thousands of cues.\n let lo = 0\n let hi = this.cues.length - 1\n while (lo <= hi) {\n const mid = (lo + hi) >> 1\n const cue = this.cues[mid]\n if (time < cue.start) hi = mid - 1\n else if (time >= cue.end) lo = mid + 1\n else return cue\n }\n return null\n }\n}\n","import { applyIcon, el } from '../core/dom'\nimport type { IconSource } from '../types'\n\nexport interface MenuItem {\n label: string\n value: string\n active: boolean\n /** Optional icon — raw SVG (`{ svg }`) or image URL (`{ url }`). */\n icon?: IconSource\n}\n\nexport interface MenuSection {\n title: string\n items: MenuItem[]\n onSelect(value: string): void\n}\n\n/**\n * One row in the settings (gear) drill-down. Either a drill row (label + current\n * value → options) or a toggle row (label + on/off switch).\n */\nexport interface SettingEntry {\n key: string\n label: string\n /** Current value shown on a drill row, e.g. \"Auto\", \"1×\", \"Off\". */\n value?: string\n /** The options panel to drill into when a drill row is clicked. */\n section?(): MenuSection\n /** Present → render a toggle switch instead of a drill row. */\n toggle?: {\n value: boolean\n onChange(next: boolean): void\n }\n}\n\nconst CHEVRON_RIGHT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 6 6 6-6 6\"/></svg>'\nconst CHEVRON_LEFT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m15 6-6 6 6 6\"/></svg>'\n\n/**\n * Popup menu anchored above a control button. One instance per button;\n * rebuilt from data on every open, closed on outside click / Escape.\n */\nexport class Menu {\n readonly root: HTMLElement\n /** Dim layer behind the mobile bottom-sheet; tap to dismiss. Hidden on desktop. */\n private backdrop: HTMLElement\n private outsideListener: ((e: Event) => void) | null = null\n private escListener: ((e: KeyboardEvent) => void) | null = null\n\n constructor(private anchor: HTMLElement) {\n this.root = el('div', 'imp-menu')\n this.root.hidden = true\n this.backdrop = el('div', 'imp-menu__backdrop')\n this.backdrop.hidden = true\n this.backdrop.addEventListener('pointerdown', (e) => {\n e.preventDefault()\n this.close()\n })\n }\n\n get open(): boolean {\n return !this.root.hidden\n }\n\n toggle(sections: MenuSection[]): void {\n if (this.open) this.close()\n else this.show(sections)\n }\n\n show(sections: MenuSection[]): void {\n this.root.textContent = ''\n for (const section of sections) {\n if (section.items.length === 0) continue\n this.root.append(this.renderSection(section))\n }\n this.reveal()\n }\n\n /** Settings (gear) drill-down: a root list of rows, each opening its options. */\n toggleSettings(title: string, entries: SettingEntry[]): void {\n if (this.open) this.close()\n else this.showSettings(title, entries)\n }\n\n showSettings(_title: string, entries: SettingEntry[]): void {\n this.root.classList.add('imp-menu--wide')\n this.renderSettingsRoot(entries)\n this.reveal()\n }\n\n private renderSettingsRoot(entries: SettingEntry[]): void {\n this.root.textContent = ''\n const block = el('div', 'imp-menu__section')\n for (const entry of entries) {\n if (entry.toggle) {\n block.append(this.buildToggleRow(entry, entry.toggle))\n continue\n }\n const row = el('button', 'imp-menu__item imp-menu__row', { type: 'button' })\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n const value = el('span', 'imp-menu__value')\n value.textContent = entry.value ?? ''\n const chevron = el('span', 'imp-menu__chevron')\n chevron.innerHTML = CHEVRON_RIGHT\n row.append(label, value, chevron)\n row.addEventListener('click', () => this.renderSettingsDetail(entries, entry))\n block.append(row)\n }\n this.root.append(block)\n }\n\n /** Toggle row: label + an on/off switch. Stays in the menu (no close). */\n private buildToggleRow(entry: SettingEntry, toggle: NonNullable<SettingEntry['toggle']>): HTMLElement {\n const row = el('button', 'imp-menu__item imp-menu__row imp-menu__toggle', {\n type: 'button', role: 'switch', 'aria-checked': String(toggle.value),\n })\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n const sw = el('span', 'imp-switch')\n sw.append(el('span', 'imp-switch__knob'))\n row.classList.toggle('imp-menu__toggle--on', toggle.value)\n row.append(label, sw)\n row.addEventListener('click', () => {\n const next = row.getAttribute('aria-checked') !== 'true'\n row.setAttribute('aria-checked', String(next))\n row.classList.toggle('imp-menu__toggle--on', next)\n toggle.onChange(next)\n })\n return row\n }\n\n private renderSettingsDetail(entries: SettingEntry[], entry: SettingEntry): void {\n if (!entry.section) return // toggle rows have no drill-down panel\n this.root.textContent = ''\n const back = el('button', 'imp-menu__item imp-menu__back', { type: 'button' })\n const chevron = el('span', 'imp-menu__chevron')\n chevron.innerHTML = CHEVRON_LEFT\n const label = el('span', 'imp-menu__label')\n label.textContent = entry.label\n back.append(chevron, label)\n back.addEventListener('click', () => this.renderSettingsRoot(entries))\n this.root.append(back)\n this.root.append(this.renderSection(entry.section()))\n this.root.scrollTop = 0\n }\n\n /** Build one option block (radio items) from a section. */\n private renderSection(section: MenuSection): HTMLElement {\n const block = el('div', 'imp-menu__section')\n if (section.title) {\n const title = el('div', 'imp-menu__title')\n title.textContent = section.title\n block.append(title)\n }\n for (const item of section.items) {\n const btn = el('button', 'imp-menu__item', { type: 'button', role: 'menuitemradio' })\n btn.setAttribute('aria-checked', String(item.active))\n if (item.active) btn.classList.add('imp-menu__item--active')\n if (item.icon) {\n const iconBox = el('span', 'imp-menu__icon')\n applyIcon(iconBox, item.icon)\n btn.append(iconBox)\n }\n const text = el('span', 'imp-menu__label')\n text.textContent = item.label\n btn.append(text)\n btn.addEventListener('click', () => {\n section.onSelect(item.value)\n this.close()\n })\n block.append(btn)\n }\n return block\n }\n\n /** Reveal the populated menu + backdrop and arm outside-click / Escape. */\n private reveal(): void {\n // The backdrop lives NEXT to the menu root (not inside the anchor button —\n // a div inside <button> is invalid HTML and a backdrop tap would re-trigger\n // the button, closing and instantly reopening the menu).\n const host = this.root.parentElement\n if (host && this.backdrop.parentElement !== host) host.insertBefore(this.backdrop, this.root)\n this.root.hidden = false\n this.backdrop.hidden = false\n this.outsideListener = (e: Event) => {\n const target = e.target as Node\n if (!this.root.contains(target) && !this.anchor.contains(target)) this.close()\n }\n this.escListener = (e: KeyboardEvent) => {\n if (e.key === 'Escape') this.close()\n }\n document.addEventListener('keydown', this.escListener)\n // Defer so the click that opened the menu does not immediately close it.\n setTimeout(() => {\n if (this.outsideListener) document.addEventListener('pointerdown', this.outsideListener)\n }, 0)\n }\n\n close(): void {\n this.root.hidden = true\n this.root.classList.remove('imp-menu--wide')\n this.backdrop.hidden = true\n if (this.outsideListener) {\n document.removeEventListener('pointerdown', this.outsideListener)\n this.outsideListener = null\n }\n if (this.escListener) {\n document.removeEventListener('keydown', this.escListener)\n this.escListener = null\n }\n }\n\n destroy(): void {\n this.close()\n this.backdrop.remove()\n this.root.remove()\n }\n}\n","import { clamp, el, formatTime } from '../core/dom'\nimport type { NormalizedChapter } from '../features/chapters'\nimport { chapterAt } from '../features/chapters'\nimport { heatmapPath } from '../features/heatmap'\nimport type { ThumbnailTrack } from '../features/thumbnails'\n\ninterface ProgressCallbacks {\n onSeek(time: number): void\n onScrubStart(): void\n onScrubEnd(): void\n}\n\ninterface Segment {\n chapter: NormalizedChapter\n root: HTMLElement\n fill: HTMLElement\n}\n\n/**\n * Seek bar. Chapters render as flex segments (YouTube-style), each with its\n * own fill — no absolutely positioned tick marks.\n */\nexport class ProgressBar {\n readonly root: HTMLElement\n private track: HTMLElement\n private buffered: HTMLElement\n private segments: Segment[] = []\n private handle: HTMLElement\n private heatmap!: HTMLElement\n private tooltip: HTMLElement\n private tooltipThumb: HTMLElement\n private tooltipCaption: HTMLElement\n private tooltipChapter: HTMLElement\n private tooltipTime: HTMLElement\n\n private duration = 0\n private chapters: NormalizedChapter[] = []\n private thumbnails: ThumbnailTrack | null = null\n private scrubbing = false\n\n constructor(private cb: ProgressCallbacks, ariaLabel = 'Seek') {\n this.root = el('div', 'imp-progress', { role: 'slider', 'aria-label': ariaLabel, tabindex: '-1' })\n this.buffered = el('div', 'imp-progress__buffered')\n this.track = el('div', 'imp-progress__track')\n this.handle = el('div', 'imp-progress__handle')\n\n this.tooltipThumb = el('div', 'imp-progress__thumb')\n this.tooltipChapter = el('div', 'imp-progress__tooltip-chapter')\n this.tooltipTime = el('div', 'imp-progress__tooltip-time')\n // Caption row overlaid on the preview: scene name left, time right.\n this.tooltipCaption = el('div', 'imp-progress__caption')\n this.tooltipCaption.append(this.tooltipChapter, this.tooltipTime)\n this.tooltip = el('div', 'imp-progress__tooltip')\n this.tooltip.append(this.tooltipThumb, this.tooltipCaption)\n\n this.heatmap = el('div', 'imp-progress__heatmap')\n this.heatmap.hidden = true\n\n this.root.append(this.heatmap, this.buffered, this.track, this.handle, this.tooltip)\n this.setChapters([])\n this.bindPointer()\n }\n\n /** Render the popularity curve (empty array hides it). */\n setHeatmap(values: number[], smoothing = 1): void {\n if (values.length === 0) {\n this.heatmap.hidden = true\n this.heatmap.textContent = ''\n return\n }\n this.heatmap.innerHTML =\n `<svg viewBox=\"0 0 1000 100\" preserveAspectRatio=\"none\" aria-hidden=\"true\">` +\n `<path d=\"${heatmapPath(values, smoothing)}\"/></svg>`\n this.heatmap.hidden = false\n }\n\n setDuration(duration: number): void {\n this.duration = Number.isFinite(duration) ? duration : 0\n this.root.classList.toggle('imp-progress--live', !Number.isFinite(duration))\n if (this.chapters.length === 0) this.setChapters([])\n }\n\n setChapters(chapters: NormalizedChapter[]): void {\n this.chapters = chapters\n this.track.textContent = ''\n this.segments = []\n const list: NormalizedChapter[] = chapters.length > 0\n ? chapters\n : [{ start: 0, end: this.duration || 1, title: '' }]\n // Fill gaps between chapters so the bar always spans the full duration.\n const filled: NormalizedChapter[] = []\n let cursor = 0\n for (const ch of list) {\n if (ch.start > cursor) filled.push({ start: cursor, end: ch.start, title: '' })\n filled.push(ch)\n cursor = ch.end\n }\n if (this.duration > 0 && cursor < this.duration) {\n filled.push({ start: cursor, end: this.duration, title: '' })\n }\n for (const chapter of filled) {\n const root = el('div', 'imp-progress__segment')\n root.style.flexGrow = String(Math.max(chapter.end - chapter.start, 0.01))\n const fill = el('div', 'imp-progress__fill')\n root.append(fill)\n this.track.append(root)\n this.segments.push({ chapter, root, fill })\n }\n }\n\n setThumbnails(track: ThumbnailTrack | null): void {\n this.thumbnails = track\n }\n\n update(currentTime: number, duration: number, bufferedEnd: number): void {\n if (duration !== this.duration && Number.isFinite(duration)) this.setDuration(duration)\n if (this.scrubbing) return\n this.render(currentTime)\n const ratio = this.duration > 0 ? clamp(bufferedEnd / this.duration, 0, 1) : 0\n this.buffered.style.width = `${ratio * 100}%`\n this.root.setAttribute('aria-valuemin', '0')\n this.root.setAttribute('aria-valuemax', String(Math.round(this.duration)))\n this.root.setAttribute('aria-valuenow', String(Math.round(currentTime)))\n this.root.setAttribute('aria-valuetext', `${formatTime(currentTime)} / ${formatTime(this.duration)}`)\n }\n\n private render(time: number): void {\n for (const { chapter, fill } of this.segments) {\n const span = chapter.end - chapter.start\n const progress = span > 0 ? clamp((time - chapter.start) / span, 0, 1) : 0\n fill.style.transform = `scaleX(${progress})`\n }\n const ratio = this.duration > 0 ? clamp(time / this.duration, 0, 1) : 0\n this.handle.style.left = `${ratio * 100}%`\n }\n\n private timeFromEvent(e: PointerEvent): number {\n const rect = this.root.getBoundingClientRect()\n const ratio = rect.width > 0 ? clamp((e.clientX - rect.left) / rect.width, 0, 1) : 0\n return ratio * this.duration\n }\n\n private bindPointer(): void {\n this.root.addEventListener('pointerdown', (e) => {\n if (this.duration <= 0) return\n e.preventDefault()\n this.scrubbing = true\n this.root.classList.add('imp-progress--scrubbing')\n this.root.setPointerCapture(e.pointerId)\n this.cb.onScrubStart()\n this.render(this.timeFromEvent(e))\n this.showTooltip(e)\n })\n this.root.addEventListener('pointermove', (e) => {\n if (this.duration <= 0) return\n this.showTooltip(e)\n if (this.scrubbing) this.render(this.timeFromEvent(e))\n })\n this.root.addEventListener('pointerup', (e) => {\n if (!this.scrubbing) return\n this.scrubbing = false\n this.root.classList.remove('imp-progress--scrubbing')\n this.cb.onSeek(this.timeFromEvent(e))\n this.cb.onScrubEnd()\n })\n this.root.addEventListener('pointercancel', () => {\n this.scrubbing = false\n this.root.classList.remove('imp-progress--scrubbing')\n this.cb.onScrubEnd()\n })\n this.root.addEventListener('pointerleave', () => this.hideTooltip())\n }\n\n private showTooltip(e: PointerEvent): void {\n const time = this.timeFromEvent(e)\n this.tooltipTime.textContent = formatTime(time)\n const chapter = chapterAt(this.chapters, time)\n const hasChapter = Boolean(chapter?.title)\n this.tooltipChapter.textContent = chapter?.title ?? ''\n this.tooltipChapter.hidden = !hasChapter\n // No scene name → center the time alone.\n this.tooltipCaption.classList.toggle('imp-progress__caption--time-only', !hasChapter)\n\n const cue = this.thumbnails?.cueAt(time) ?? null\n // Caption overlays the preview frame only when there is one.\n this.tooltip.classList.toggle('imp-progress__tooltip--thumb', Boolean(cue))\n if (cue) {\n this.tooltipThumb.hidden = false\n this.tooltipThumb.style.backgroundImage = `url(\"${cue.src}\")`\n if (cue.xywh) {\n this.tooltipThumb.style.width = `${cue.xywh.w}px`\n this.tooltipThumb.style.height = `${cue.xywh.h}px`\n this.tooltipThumb.style.backgroundPosition = `-${cue.xywh.x}px -${cue.xywh.y}px`\n this.tooltipThumb.style.backgroundSize = 'auto'\n } else {\n this.tooltipThumb.style.width = '160px'\n this.tooltipThumb.style.height = '90px'\n this.tooltipThumb.style.backgroundPosition = 'center'\n this.tooltipThumb.style.backgroundSize = 'cover'\n }\n } else {\n this.tooltipThumb.hidden = true\n }\n\n this.tooltip.classList.add('imp-progress__tooltip--visible')\n const rect = this.root.getBoundingClientRect()\n const x = clamp(e.clientX - rect.left, 0, rect.width)\n const half = this.tooltip.offsetWidth / 2\n this.tooltip.style.left = `${clamp(x, half, Math.max(half, rect.width - half))}px`\n }\n\n private hideTooltip(): void {\n this.tooltip.classList.remove('imp-progress__tooltip--visible')\n }\n}\n","import { applyIcon, coverImg, el, formatTime, iconButton, previewMetaEl, setIcon } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\nimport type { IconSource, NextPreviewOptions, SeekLabelFormat, SettingPlacement } from '../types'\nimport { Menu, type MenuSection, type SettingEntry } from './menu'\nimport { ProgressBar } from './progress'\n\n/**\n * A control that can be collapsed into the ⋯ overflow menu when the bar runs\n * out of room. `el` is the element hidden in the bar; `section()` returns what\n * the ⋯ menu shows in its place (or null when currently unavailable).\n */\ninterface Collapsible {\n key: string\n el: HTMLElement\n /** Lower = collapses first (least important to keep as a dedicated button). */\n priority: number\n available(): boolean\n /** What the ⋯ menu shows in its place — one section, several, or none. */\n section(): MenuSection | MenuSection[] | null\n}\n\n/**\n * Control bar. A single flex row inside the overlay grid. When the player is\n * too narrow, lower-priority controls collapse into the ⋯ menu dynamically\n * (ResizeObserver-driven) so nothing ever overflows the player edge.\n */\nexport class Controls {\n readonly root: HTMLElement\n readonly progress: ProgressBar\n\n private playBtn!: HTMLButtonElement\n private muteBtn!: HTMLButtonElement\n private volumeSlider!: HTMLInputElement\n private timeLabel!: HTMLElement\n private timeCurrent!: HTMLElement\n private timeTotal!: HTMLElement\n private chapterLabel!: HTMLElement\n private liveBadge!: HTMLElement\n private fullscreenBtn!: HTMLButtonElement\n private row!: HTMLElement\n private rightGroup!: HTMLElement\n private seekLabelFn?: SeekLabelFormat\n /** Right-cluster controls keyed for ordering; appended in `controls.order`. */\n private rightItems = new Map<string, HTMLElement>()\n\n private subtitlesBtn: HTMLButtonElement | null = null\n private settingsBtn: HTMLButtonElement | null = null\n private settingsMenu: Menu | null = null\n private subtitlesMenu: Menu | null = null\n private moreMenu: Menu | null = null\n private moreWrap: HTMLElement | null = null\n private qualityBtn: HTMLButtonElement | null = null\n private qualityMenu: Menu | null = null\n /** Unified ⚙ dropdown holding the settings placed in `\"gear\"`. */\n private gearBtn: HTMLButtonElement | null = null\n private gearMenu: Menu | null = null\n /** Which settings live in the gear menu (vs their own bar button / off). */\n private gear = { speed: false, quality: false, subtitles: false }\n /** Next-up hover preview (desktop). */\n private nextPreviewEl: HTMLElement | null = null\n private nextPreviewOpts: Required<NextPreviewOptions> | null = null\n private scenesBtn: HTMLButtonElement | null = null\n private sceneTypeBtn: HTMLButtonElement | null = null\n private sceneTypeMenu: Menu | null = null\n private likeBtn: HTMLButtonElement | null = null\n private dislikeBtn: HTMLButtonElement | null = null\n private likeCountEl: HTMLElement | null = null\n private dislikeCountEl: HTMLElement | null = null\n private prevBtn: HTMLButtonElement | null = null\n private nextBtn: HTMLButtonElement | null = null\n private seekBackBtn: HTMLButtonElement | null = null\n private seekFwdBtn: HTMLButtonElement | null = null\n\n /** Center overlay cluster (prev · −Ns · play · +Ns · next), shown on mobile. */\n readonly center: HTMLElement\n private centerPlayBtn: HTMLButtonElement | null = null\n private centerPrevBtn: HTMLButtonElement | null = null\n private centerNextBtn: HTMLButtonElement | null = null\n\n private collapsibles: Collapsible[] = []\n private overflowed = new Set<string>()\n private resizeObserver: ResizeObserver | null = null\n private reflowScheduled = false\n /** Consumer actions (share/report/…) always live in the ⋯ menu. */\n private actionItems: Array<{ value: string; label: string; icon?: IconSource; run(): void }> = []\n\n private wasPlayingBeforeScrub = false\n private disposers: Array<() => void> = []\n\n constructor(private player: Player) {\n const { labels, icons } = player\n const c = player.controlsOptions\n\n this.root = el('div', 'imp-controls')\n\n this.progress = new ProgressBar({\n onSeek: (t) => player.seek(t),\n onScrubStart: () => {\n // The flag keeps the pause handler from showing the pause screen\n // mid-drag (the scrub pauses the video under the hood).\n player.scrubbing = true\n this.wasPlayingBeforeScrub = !player.paused\n player.pause()\n },\n onScrubEnd: () => {\n player.scrubbing = false\n // skipAds: releasing the scrubber must never trigger a pre-roll.\n if (this.wasPlayingBeforeScrub) void player.play(true)\n // Before the first play the bar is seeded with the declared duration —\n // releasing the scrubber there means \"start from this spot\" (this one\n // is a real start, so ads run as usual).\n else if (!player.started) void player.play()\n },\n }, player.labels.seek)\n if (c.progress) this.root.append(this.progress.root)\n\n const row = el('div', 'imp-controls__row')\n this.row = row\n this.root.append(row)\n\n const left = el('div', 'imp-controls__group')\n const right = el('div', 'imp-controls__group')\n this.rightGroup = right\n const spacer = el('div', 'imp-controls__spacer')\n\n this.chapterLabel = el('div', 'imp-controls__chapter')\n row.append(left, spacer, right)\n spacer.append(this.chapterLabel)\n\n // Seek step values (used by both buttons and the ⋯ fallback items).\n const seekCfg = typeof c.seekButtons === 'object' ? c.seekButtons : {}\n const seekBack = seekCfg.back ?? player.seekStep\n const seekFwd = seekCfg.forward ?? player.seekStep\n this.seekLabelFn = seekCfg.label\n\n // --- left cluster: prev · −Ns · play · +Ns · next · volume · time ---\n // The bar prev button is opt-in (hidePrev defaults true → bar shows only\n // next). The mobile center cluster still gets its own prev regardless.\n if (c.playlist && player.hasPlaylist && !c.hidePrev) {\n this.prevBtn = iconButton('imp-btn--prev', labels.previous, icons.previous)\n this.prevBtn.addEventListener('click', () => player.previous())\n left.append(this.prevBtn)\n }\n\n if (c.seekButtons) {\n this.seekBackBtn = iconButton('imp-btn--seek-back', `${labels.seekBack} ${seekBack}s`, icons.seekBack)\n this.addStepBadge(this.seekBackBtn, seekBack, 'back')\n this.seekBackBtn.addEventListener('click', () => player.skip(-seekBack))\n left.append(this.seekBackBtn)\n }\n\n if (c.play) {\n this.playBtn = iconButton('imp-btn--play', labels.play, icons.play)\n this.playBtn.addEventListener('click', () => player.togglePlay())\n left.append(this.playBtn)\n }\n\n if (c.seekButtons) {\n this.seekFwdBtn = iconButton('imp-btn--seek-forward', `${labels.seekForward} ${seekFwd}s`, icons.seekForward)\n this.addStepBadge(this.seekFwdBtn, seekFwd, 'forward')\n this.seekFwdBtn.addEventListener('click', () => player.skip(seekFwd))\n left.append(this.seekFwdBtn)\n }\n\n if (c.playlist && player.hasPlaylist && !c.hideNext) {\n this.nextBtn = iconButton('imp-btn--next', labels.next, icons.next)\n this.nextBtn.addEventListener('click', () => player.next())\n left.append(this.nextBtn)\n }\n\n if (c.volume) {\n const volumeWrap = el('div', 'imp-volume')\n this.muteBtn = iconButton('imp-btn--mute', labels.mute, icons.volumeHigh)\n this.muteBtn.addEventListener('click', () => player.toggleMute())\n this.volumeSlider = el('input', 'imp-volume__slider', {\n type: 'range', min: '0', max: '1', step: '0.05', 'aria-label': labels.volume,\n })\n this.volumeSlider.addEventListener('input', () => {\n player.setVolume(Number(this.volumeSlider.value))\n })\n volumeWrap.append(this.muteBtn, this.volumeSlider)\n left.append(volumeWrap)\n }\n\n if (c.time) {\n this.timeLabel = el('div', 'imp-controls__time')\n // Split so the total (slash + duration) can be de-emphasized vs current time.\n this.timeCurrent = el('span', 'imp-controls__time-current')\n this.timeTotal = el('span', 'imp-controls__time-total')\n this.timeLabel.append(this.timeCurrent, this.timeTotal)\n this.liveBadge = el('span', 'imp-controls__live')\n this.liveBadge.textContent = labels.live\n this.liveBadge.hidden = true\n left.append(this.timeLabel, this.liveBadge)\n }\n\n // --- right cluster -------------------------------------------------\n this.buildLikeDislike()\n\n // Consumer actions placed directly in the bar (icon button, tooltip = title).\n for (const ca of (player.actionsOptions.custom ?? []).filter((a) => a.placement === 'bar')) {\n const btn = iconButton(`imp-btn--custom imp-btn--custom-${ca.id}`, ca.title, ca.icon ?? icons.more)\n btn.addEventListener('click', () => player.emit('customaction', { id: ca.id }))\n this.rightItems.set(`custom:${ca.id}`, btn)\n this.registerCollapsible({\n key: `custom:${ca.id}`, el: btn, priority: 55, available: () => true,\n section: () => this.simpleSection(`custom:${ca.id}`, ca.title, ca.icon ?? icons.more, () => player.emit('customaction', { id: ca.id })),\n })\n }\n\n // Settings placement: subtitles / quality / speed each live in the gear\n // dropdown ('gear', default), as their own bar button ('bar'), or off.\n const place = (v: SettingPlacement | undefined): 'gear' | 'bar' | 'off' =>\n v === false ? 'off' : v === 'bar' ? 'bar' : 'gear'\n const subsPlace = place(c.subtitles)\n const qualityPlace = place(c.quality)\n const speedPlace = place(c.speed)\n this.gear = { speed: speedPlace === 'gear', quality: qualityPlace === 'gear', subtitles: subsPlace === 'gear' }\n\n // Standalone (bar) settings buttons — only when explicitly pulled out.\n if (subsPlace === 'bar') {\n this.subtitlesBtn = iconButton('imp-btn--subtitles', labels.subtitles, icons.subtitles)\n this.subtitlesMenu = new Menu(this.subtitlesBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.subtitlesBtn, this.subtitlesMenu.root)\n this.subtitlesBtn.addEventListener('click', () => this.toggleSubtitlesMenu())\n this.rightItems.set('subtitles', wrap)\n this.registerCollapsible({\n key: 'subtitles', el: wrap, priority: 30,\n available: () => player.subtitleTracks.length > 0,\n section: () => this.subtitlesSection(),\n })\n }\n if (qualityPlace === 'bar') {\n this.qualityBtn = iconButton('imp-btn--quality', labels.quality, icons.settings)\n this.qualityMenu = new Menu(this.qualityBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.qualityBtn, this.qualityMenu.root)\n this.qualityBtn.addEventListener('click', () => this.toggleQualityMenu())\n this.rightItems.set('quality', wrap)\n this.registerCollapsible({\n key: 'quality', el: wrap, priority: 10,\n available: () => player.qualityLevels.length > 0,\n section: () => this.qualitySection(),\n })\n }\n if (speedPlace === 'bar') {\n this.settingsBtn = iconButton('imp-btn--speed', labels.speed, icons.speed)\n this.settingsMenu = new Menu(this.settingsBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.settingsBtn, this.settingsMenu.root)\n this.settingsBtn.addEventListener('click', () => this.toggleSettingsMenu())\n this.rightItems.set('speed', wrap)\n this.registerCollapsible({\n key: 'speed', el: wrap, priority: 40,\n available: () => true,\n section: () => this.speedSection(),\n })\n }\n\n if (c.scenes) {\n this.scenesBtn = iconButton('imp-btn--scenes', labels.scenes, icons.scenes)\n this.scenesBtn.addEventListener('click', () => player.toggleScenesPanel())\n this.rightItems.set('scenes', this.scenesBtn)\n this.registerCollapsible({\n key: 'scenes', el: this.scenesBtn, priority: 25,\n available: () => player.chapterList.length > 0,\n section: () => this.simpleSection('scenes', labels.scenes, icons.scenes, () => player.toggleScenesPanel()),\n })\n }\n\n // Scene-type selector (icon + current type label, dropdown). Visible only\n // when the source carries typed scene groups.\n if (c.sceneTypes !== false) {\n this.sceneTypeBtn = el('button', 'imp-btn imp-btn--scene-types', { type: 'button' })\n this.sceneTypeMenu = new Menu(this.sceneTypeBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.sceneTypeBtn, this.sceneTypeMenu.root)\n this.sceneTypeBtn.addEventListener('click', () => this.toggleSceneTypeMenu())\n this.rightItems.set('sceneTypes', wrap)\n this.refreshSceneTypeButton()\n // High priority: the scene-type selector is a headline feature — on a\n // narrow mobile bar it must NOT be an early collapse into ⋯ (users read\n // that as \"the button is missing\"). Only prev/next/seek outrank it.\n this.registerCollapsible({\n key: 'sceneTypes', el: wrap, priority: 68,\n available: () => player.sceneTypes.length > 0,\n section: () => this.sceneTypeSection(),\n })\n }\n\n if (c.playlist && player.hasPlaylist && c.playlistButton !== false) {\n const listBtn = iconButton('imp-btn--list', labels.playlist, icons.list)\n listBtn.addEventListener('click', () => player.togglePlaylistPanel())\n this.rightItems.set('playlist', listBtn)\n this.registerCollapsible({\n key: 'list', el: listBtn, priority: 50,\n available: () => true,\n section: () => this.simpleSection('list', labels.playlist, icons.list, () => player.togglePlaylistPanel()),\n })\n }\n\n if (c.pip && 'requestPictureInPicture' in HTMLVideoElement.prototype) {\n const pipBtn = iconButton('imp-btn--pip', labels.pip, icons.pip)\n pipBtn.addEventListener('click', () => void player.togglePip())\n this.rightItems.set('pip', pipBtn)\n this.registerCollapsible({\n key: 'pip', el: pipBtn, priority: 20,\n available: () => true,\n section: () => this.simpleSection('pip', labels.pip, icons.pip, () => void player.togglePip()),\n })\n }\n\n if (c.fullscreen) {\n this.fullscreenBtn = iconButton('imp-btn--fullscreen', labels.fullscreen, icons.fullscreen)\n this.fullscreenBtn.addEventListener('click', () => void player.toggleFullscreen())\n this.rightItems.set('fullscreen', this.fullscreenBtn)\n }\n\n // Playlist nav and seek can also collapse (lower priority kept than submenus).\n if (this.prevBtn) {\n this.registerCollapsible({\n key: 'prev', el: this.prevBtn, priority: 70,\n available: () => true,\n section: () => player.hasPrevious ? this.simpleSection('prev', labels.previous, icons.previous, () => player.previous()) : null,\n })\n }\n if (this.nextBtn) {\n this.registerCollapsible({\n key: 'next', el: this.nextBtn, priority: 72,\n available: () => true,\n section: () => player.hasNext ? this.simpleSection('next', labels.next, icons.next, () => player.next()) : null,\n })\n }\n if (this.seekBackBtn) {\n const b = this.seekBackBtn\n this.registerCollapsible({\n key: 'seekBack', el: b, priority: 80,\n available: () => Number.isFinite(player.duration) && player.duration > 0,\n section: () => this.simpleSection('seekBack', `${labels.seekBack} ${seekBack}s`, icons.seekBack, () => player.skip(-seekBack)),\n })\n }\n if (this.seekFwdBtn) {\n const f = this.seekFwdBtn\n this.registerCollapsible({\n key: 'seekFwd', el: f, priority: 82,\n available: () => Number.isFinite(player.duration) && player.duration > 0,\n section: () => this.simpleSection('seekFwd', `${labels.seekForward} ${seekFwd}s`, icons.seekForward, () => player.skip(seekFwd)),\n })\n }\n\n // --- center overlay cluster (prev · −Ns · play · +Ns · next) -----------\n // Shown on mobile in the middle of the video; the matching bottom-bar\n // buttons are hidden there. Big tap targets, less crowded bar.\n this.center = el('div', 'imp-center-controls')\n if (c.playlist && player.hasPlaylist) {\n this.centerPrevBtn = this.makeCenterButton('prev', labels.previous, icons.previous)\n this.centerPrevBtn.addEventListener('click', () => player.previous())\n this.center.append(this.centerPrevBtn)\n }\n if (c.seekButtons) {\n const b = this.makeCenterButton('seek-back', `${labels.seekBack} ${seekBack}s`, icons.seekBack)\n this.addStepBadge(b, seekBack, 'back')\n b.addEventListener('click', () => player.skip(-seekBack))\n this.center.append(b)\n }\n if (c.play) {\n this.centerPlayBtn = this.makeCenterButton('play', labels.play, icons.play)\n this.centerPlayBtn.addEventListener('click', () => player.togglePlay())\n this.center.append(this.centerPlayBtn)\n }\n if (c.seekButtons) {\n const f = this.makeCenterButton('seek-forward', `${labels.seekForward} ${seekFwd}s`, icons.seekForward)\n this.addStepBadge(f, seekFwd, 'forward')\n f.addEventListener('click', () => player.skip(seekFwd))\n this.center.append(f)\n }\n if (c.playlist && player.hasPlaylist) {\n this.centerNextBtn = this.makeCenterButton('next', labels.next, icons.next)\n this.centerNextBtn.addEventListener('click', () => player.next())\n this.center.append(this.centerNextBtn)\n }\n this.center.style.display = 'none'\n\n // Next-up hover preview (desktop). Wired to whichever next buttons exist.\n this.setupNextPreview()\n\n // Unified ⚙ dropdown — placed immediately left of the ⋯ button. Holds\n // whatever is set to 'gear'; hidden entirely when it has no available\n // section (handled by `available()`).\n if (this.gear.speed || this.gear.quality || this.gear.subtitles) {\n this.gearBtn = iconButton('imp-btn--settings', labels.settings, icons.settings)\n this.gearMenu = new Menu(this.gearBtn)\n const wrap = el('div', 'imp-controls__menu-anchor')\n wrap.append(this.gearBtn, this.gearMenu.root)\n this.gearBtn.addEventListener('click', () => this.toggleGearMenu())\n this.rightItems.set('gear', wrap)\n this.registerCollapsible({\n key: 'gear', el: wrap, priority: 40,\n available: () => this.gearSections().length > 0,\n section: () => this.gearSections(),\n })\n }\n\n // Append the right-cluster controls in the configured (or built-in) order.\n this.layoutRightCluster(right)\n\n // ⋯ dropdown — overflow target + consumer actions. Always last.\n this.buildMoreDropdown(right)\n\n this.bind()\n this.syncVolume()\n this.syncPlayState()\n this.setLikeState(player.actionsOptions.likeState ?? null)\n\n // React to the player resizing (fullscreen, layout) ...\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => this.scheduleReflow())\n this.resizeObserver.observe(player.container)\n }\n // ... and to viewport changes crossing the 767px mobile breakpoint.\n this.onWindowResize = () => this.scheduleReflow()\n window.addEventListener('resize', this.onWindowResize)\n }\n\n private onWindowResize: (() => void) | null = null\n\n private registerCollapsible(c: Collapsible): void {\n this.collapsibles.push(c)\n }\n\n /**\n * Append the right-cluster controls. Built-in order = creation order; if\n * `controls.order` is set, its keys go first (in that order), then any\n * unlisted controls keep their default position. (⋯ is appended separately.)\n */\n private layoutRightCluster(right: HTMLElement): void {\n const insertion = [...this.rightItems.keys()]\n const requested = this.player.controlsOptions.order as string[]\n let keys = insertion\n if (requested.length > 0) {\n const seen = new Set<string>()\n keys = []\n for (const k of requested) {\n if (this.rightItems.has(k) && !seen.has(k)) {\n keys.push(k)\n seen.add(k)\n }\n }\n for (const k of insertion) if (!seen.has(k)) keys.push(k)\n }\n for (const k of keys) {\n const node = this.rightItems.get(k)\n if (node) right.append(node)\n }\n }\n\n /** Center-cluster button — deliberately NOT `.imp-btn` (own sizing/look). */\n private makeCenterButton(modifier: string, label: string, icon: IconSource): HTMLButtonElement {\n const btn = el('button', `imp-center-btn imp-center-btn--${modifier}`, { type: 'button', 'aria-label': label, title: label })\n applyIcon(btn, icon)\n return btn\n }\n\n /** Caption under the seek arrow. Default \"−15s\"/\"+15s\"; overridable via `controls.seekButtons.label`. */\n private addStepBadge(btn: HTMLButtonElement, step: number, dir: 'back' | 'forward'): void {\n const num = el('span', 'imp-seek-num')\n num.textContent = this.seekLabelFn\n ? this.seekLabelFn(step, dir)\n : `${dir === 'back' ? '−' : '+'}${step}${this.player.labels.secondsShort}`\n btn.append(num)\n }\n\n // === gear (unified settings) ==========================================\n\n /** Sections for the gear when overflowed into ⋯ (expanded, no drill-down). */\n private gearSections(): MenuSection[] {\n const p = this.player\n const out: MenuSection[] = []\n if (this.gear.quality && p.qualityLevels.length > 0) out.push(this.qualitySection())\n if (this.gear.speed) out.push(this.speedSection())\n if (this.gear.subtitles && p.subtitleTracks.length > 0) out.push(this.subtitlesSection())\n return out\n }\n\n /** Gear drill-down rows: one per setting, each showing its current value. */\n private gearEntries(): SettingEntry[] {\n const p = this.player\n const out: SettingEntry[] = []\n // Autoplay toggle — only meaningful with a playlist (next item to advance to).\n if (p.hasPlaylist) {\n out.push({\n key: 'autoplay',\n label: p.labels.autoplay,\n toggle: { value: p.autoAdvance, onChange: (v) => p.setAutoAdvance(v) },\n })\n }\n if (this.gear.quality && p.qualityLevels.length > 0) {\n const value = p.currentQuality === -1\n ? p.labels.qualityAuto\n : p.qualityLevels.find((l) => l.index === p.currentQuality)?.label ?? p.labels.qualityAuto\n out.push({ key: 'quality', label: p.labels.quality, value, section: () => this.qualitySection() })\n }\n if (this.gear.speed) {\n const r = p.playbackRate\n out.push({ key: 'speed', label: p.labels.speed, value: r === 1 ? p.labels.speedNormal : `${r}×`, section: () => this.speedSection() })\n }\n if (this.gear.subtitles && p.subtitleTracks.length > 0) {\n const value = p.activeSubtitle === -1\n ? p.labels.subtitlesOff\n : p.subtitleTracks[p.activeSubtitle]?.label ?? p.labels.subtitlesOff\n out.push({ key: 'subtitles', label: p.labels.subtitles, value, section: () => this.subtitlesSection() })\n }\n return out\n }\n\n // === scene types ======================================================\n\n /** Update the scene-type button to the active group's icon + title. */\n private refreshSceneTypeButton(): void {\n const btn = this.sceneTypeBtn\n if (!btn) return\n const active = this.player.activeSceneType\n btn.textContent = ''\n if (!active) return\n if (active.icon) {\n const iconBox = el('span', 'imp-scene-type__icon')\n applyIcon(iconBox, active.icon)\n btn.append(iconBox)\n }\n const label = el('span', 'imp-scene-type__label')\n label.textContent = active.title\n btn.append(label)\n btn.setAttribute('aria-label', `${this.player.labels.sceneTypes}: ${active.title}`)\n btn.title = active.title\n }\n\n private sceneTypeSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.sceneTypes,\n items: p.sceneTypes.map((g) => ({\n label: g.title,\n value: g.id,\n icon: g.icon,\n active: g.id === (p.activeSceneType?.id ?? ''),\n })),\n onSelect: (value) => {\n p.setSceneGroup(value)\n this.refreshSceneTypeButton()\n },\n }\n }\n\n private toggleSceneTypeMenu(): void {\n if (!this.sceneTypeMenu || this.player.sceneTypes.length === 0) return\n this.closeMenus(this.sceneTypeMenu)\n this.sceneTypeMenu.toggle([this.sceneTypeSection()])\n }\n\n private toggleGearMenu(): void {\n if (!this.gearMenu) return\n const entries = this.gearEntries()\n if (entries.length === 0) return\n this.closeMenus(this.gearMenu)\n this.gearMenu.toggleSettings(this.player.labels.settings, entries)\n }\n\n // === next-up hover preview ============================================\n\n private setupNextPreview(): void {\n const p = this.player\n const c = p.controlsOptions\n if (c.nextPreview === false || !c.playlist || !p.hasPlaylist) return\n const o = typeof c.nextPreview === 'object' ? c.nextPreview : {}\n this.nextPreviewOpts = {\n thumbnail: o.thumbnail ?? true,\n title: o.title ?? true,\n duration: o.duration ?? true,\n meta: o.meta ?? true,\n }\n this.nextPreviewEl = el('div', 'imp-next-preview')\n this.nextPreviewEl.hidden = true\n p.container.append(this.nextPreviewEl)\n if (this.nextBtn) this.bindNextPreview(this.nextBtn)\n if (this.centerNextBtn) this.bindNextPreview(this.centerNextBtn)\n }\n\n private bindNextPreview(btn: HTMLButtonElement): void {\n // Mouse only — touch has no hover, and a tap should just go to the next item.\n btn.addEventListener('pointerenter', (e) => {\n if (e.pointerType === 'mouse') this.showNextPreview(btn)\n })\n btn.addEventListener('pointerleave', () => this.hideNextPreview())\n btn.addEventListener('click', () => this.hideNextPreview())\n }\n\n private showNextPreview(btn: HTMLButtonElement): void {\n const card = this.nextPreviewEl\n const opts = this.nextPreviewOpts\n if (!card || !opts) return\n const src = this.player.nextSource\n if (!src) {\n // Nothing playable ahead (e.g. the tail got marked dead) — drop the card.\n this.hideNextPreview()\n return\n }\n\n card.textContent = ''\n const kicker = el('div', 'imp-next-preview__kicker')\n kicker.textContent = this.player.labels.next\n card.append(kicker)\n\n const previewPoster = posterSrc(src.poster)\n if (opts.thumbnail && previewPoster) {\n const thumb = el('div', 'imp-next-preview__thumb')\n const img = coverImg(previewPoster)\n // Poster came back 4xx/5xx → the video won't load either. Mark the source\n // dead and re-render the card with the next alive candidate.\n img.addEventListener('error', () => {\n this.player.markSourceDead(src)\n if (this.nextPreviewEl && !this.nextPreviewEl.hidden) this.showNextPreview(btn)\n }, { once: true })\n thumb.append(img)\n if (opts.duration && src.duration) {\n const d = el('span', 'imp-next-preview__duration')\n d.textContent = formatTime(src.duration)\n thumb.append(d)\n }\n card.append(thumb)\n }\n if (opts.title && src.title) {\n const t = el('div', 'imp-next-preview__title')\n t.textContent = src.title\n card.append(t)\n }\n if (opts.meta && src.previewMeta?.length) {\n card.append(previewMetaEl(src.previewMeta, 'imp-next-preview'))\n }\n\n // Anchor centered above the hovered button, then clamp inside the player so\n // the card never spills past (and gets clipped by) the player edges.\n card.hidden = false\n const cr = this.player.container.getBoundingClientRect()\n const r = btn.getBoundingClientRect()\n const pad = 8\n const cardW = card.offsetWidth\n const centered = r.left - cr.left + r.width / 2 - cardW / 2\n const left = Math.max(pad, Math.min(centered, cr.width - cardW - pad))\n card.style.left = `${left}px`\n card.style.bottom = `${cr.height - (r.top - cr.top) + 10}px`\n }\n\n private hideNextPreview(): void {\n if (this.nextPreviewEl) this.nextPreviewEl.hidden = true\n }\n\n /** like/dislike — visible buttons (collapsible), highlightable via `setLikeState`. */\n private buildLikeDislike(): void {\n const p = this.player\n const a = p.actionsOptions\n const { labels, icons } = p\n\n if (a.like) {\n this.likeBtn = iconButton('imp-btn--like', labels.like, icons.like)\n this.likeBtn.addEventListener('click', () => p.emit('action', { id: 'like' }))\n this.likeCountEl = this.attachCountTooltip(this.likeBtn, a.likeCount)\n const wrap = this.wrapWithTooltip(this.likeBtn, this.likeCountEl)\n this.rightItems.set('like', wrap)\n this.registerCollapsible({\n key: 'like', el: wrap, priority: 62, available: () => true,\n section: () => this.simpleSection('like', labels.like, icons.like, () => p.emit('action', { id: 'like' })),\n })\n }\n if (a.dislike) {\n this.dislikeBtn = iconButton('imp-btn--dislike', labels.dislike, icons.dislike)\n this.dislikeBtn.addEventListener('click', () => p.emit('action', { id: 'dislike' }))\n this.dislikeCountEl = this.attachCountTooltip(this.dislikeBtn, a.dislikeCount)\n const wrap = this.wrapWithTooltip(this.dislikeBtn, this.dislikeCountEl)\n this.rightItems.set('dislike', wrap)\n this.registerCollapsible({\n key: 'dislike', el: wrap, priority: 60, available: () => true,\n section: () => this.simpleSection('dislike', labels.dislike, icons.dislike, () => p.emit('action', { id: 'dislike' })),\n })\n }\n }\n\n private attachCountTooltip(_btn: HTMLButtonElement, initial: number | string | undefined): HTMLElement {\n const tip = el('span', 'imp-count-tooltip')\n this.setCountText(tip, initial)\n return tip\n }\n\n private wrapWithTooltip(btn: HTMLButtonElement, tip: HTMLElement): HTMLElement {\n const wrap = el('div', 'imp-action')\n wrap.append(btn, tip)\n return wrap\n }\n\n private setCountText(tip: HTMLElement, value: number | string | undefined): void {\n const text = value === undefined || value === null || value === '' ? '' : String(value)\n tip.textContent = text\n tip.hidden = text === ''\n }\n\n setLikeCounts(likeCount?: number | string, dislikeCount?: number | string): void {\n if (this.likeCountEl && likeCount !== undefined) this.setCountText(this.likeCountEl, likeCount)\n if (this.dislikeCountEl && dislikeCount !== undefined) this.setCountText(this.dislikeCountEl, dislikeCount)\n }\n\n setLikeState(state: 'like' | 'dislike' | null): void {\n this.likeBtn?.classList.toggle('imp-btn--active', state === 'like')\n this.dislikeBtn?.classList.toggle('imp-btn--active', state === 'dislike')\n }\n\n // === ⋯ menu ===========================================================\n\n private buildMoreDropdown(right: HTMLElement): void {\n const p = this.player\n const a = p.actionsOptions\n const { labels, icons } = p\n\n if (a.addTo) this.actionItems.push({ value: 'addTo', label: labels.addTo, icon: icons.addTo, run: () => p.emit('action', { id: 'addTo' }) })\n if (a.share) this.actionItems.push({ value: 'share', label: labels.share, icon: icons.share, run: () => void p.share() })\n if (a.report) this.actionItems.push({ value: 'report', label: labels.report, icon: icons.report, run: () => p.emit('action', { id: 'report' }) })\n // Bar-placed custom actions render as their own buttons; only menu ones here.\n for (const custom of (a.custom ?? []).filter((ca) => ca.placement !== 'bar')) {\n this.actionItems.push({ value: `custom:${custom.id}`, label: custom.title, icon: custom.icon, run: () => p.emit('customaction', { id: custom.id }) })\n }\n\n const moreBtn = iconButton('imp-btn--more', labels.more, icons.more)\n this.moreMenu = new Menu(moreBtn)\n const wrap = el('div', 'imp-controls__menu-anchor imp-controls__more')\n wrap.append(moreBtn, this.moreMenu.root)\n moreBtn.addEventListener('click', () => {\n this.closeMenus(this.moreMenu)\n this.moreMenu?.toggle(this.buildMoreSections())\n })\n right.append(wrap)\n this.moreWrap = wrap\n }\n\n /** ⋯ menu = overflowed controls (in display order) + consumer actions. */\n private buildMoreSections(): MenuSection[] {\n const sections: MenuSection[] = []\n const runMap = new Map<string, () => void>()\n const simpleItems: Array<{ label: string; value: string; icon?: IconSource; active: boolean }> = []\n\n // Overflowed controls, ordered for reading (nav → toggles → submenus).\n const order = ['prev', 'next', 'seekBack', 'seekFwd', 'like', 'dislike', 'scenes', 'sceneTypes', 'pip', 'list', 'subtitles', 'speed', 'quality', 'gear']\n const rank = (key: string) => {\n const i = order.indexOf(key)\n return i === -1 ? order.length : i // custom:* etc. sort after known keys\n }\n const overflowedSorted = this.collapsibles\n .filter((c) => this.overflowed.has(c.key) && c.available())\n .sort((a, b) => rank(a.key) - rank(b.key))\n\n for (const c of overflowedSorted) {\n const result = c.section()\n if (!result) continue\n // A collapsible may contribute one section or several (the gear menu).\n for (const section of Array.isArray(result) ? result : [result]) {\n // Single-item, no-title contributions merge into one block; titled ones stand alone.\n if (!section.title && section.items.length === 1) {\n const it = section.items[0]\n simpleItems.push(it)\n runMap.set(it.value, () => section.onSelect(it.value))\n } else {\n sections.push(section)\n }\n }\n }\n if (simpleItems.length > 0) {\n sections.unshift({ title: '', items: simpleItems, onSelect: (v) => runMap.get(v)?.() })\n }\n\n if (this.actionItems.length > 0) {\n const map = new Map(this.actionItems.map((a) => [a.value, a.run]))\n sections.push({\n title: '',\n items: this.actionItems.map(({ value, label, icon }) => ({ value, label, icon, active: false })),\n onSelect: (v) => map.get(v)?.(),\n })\n }\n return sections\n }\n\n private simpleSection(value: string, label: string, icon: IconSource, run: () => void): MenuSection {\n return { title: '', items: [{ value, label, icon, active: false }], onSelect: () => run() }\n }\n\n private speedSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.speed,\n items: p.playbackRates.map((rate) => ({\n label: rate === 1 ? p.labels.speedNormal : `${rate}×`,\n value: String(rate),\n active: p.playbackRate === rate,\n })),\n onSelect: (value) => p.setPlaybackRate(Number(value)),\n }\n }\n\n private qualitySection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.quality,\n items: [\n ...(p.qualityAutoAvailable ? [{ label: p.labels.qualityAuto, value: '-1', active: p.currentQuality === -1 }] : []),\n ...p.qualityLevels.map((level) => ({ label: level.label, value: String(level.index), active: p.currentQuality === level.index })),\n ],\n onSelect: (value) => p.setQuality(Number(value)),\n }\n }\n\n private subtitlesSection(): MenuSection {\n const p = this.player\n return {\n title: p.labels.subtitles,\n items: [\n { label: p.labels.subtitlesOff, value: '-1', active: p.activeSubtitle === -1 },\n ...p.subtitleTracks.map((track, index) => ({ label: track.label, value: String(index), active: p.activeSubtitle === index })),\n ],\n onSelect: (value) => p.setSubtitle(Number(value)),\n }\n }\n\n // === dynamic overflow =================================================\n\n private scheduleReflow(): void {\n if (this.reflowScheduled) return\n this.reflowScheduled = true\n requestAnimationFrame(() => {\n this.reflowScheduled = false\n this.reflow()\n })\n }\n\n /**\n * Hide controls that don't fit (lowest priority first) and remember them so\n * the ⋯ menu can offer them. Unavailable controls are hidden outright.\n */\n private reflow(): void {\n // Mobile layout up to the site's 767px breakpoint (viewport-based).\n const compact = window.innerWidth <= 767\n // Legacy mobile layout: the full prev · −Ns · play · +Ns · next cluster\n // over the video. Default ('bar') keeps only ±N there — play and next live\n // in the bottom bar, same as desktop.\n const centerLayout = compact && this.player.controlsOptions.mobileLayout === 'center'\n\n // The seek ±N (and center play) live over the video when seekPlacement is\n // 'overlay' (default, every viewport) or always on mobile. In 'bar' mode on\n // desktop they stay in the bottom bar instead.\n const overlay = this.player.controlsOptions.seekPlacement !== 'bar'\n const useCenter = overlay || compact\n this.center.style.display = useCenter ? 'flex' : 'none'\n\n // prev/next sit in the center cluster only in the legacy mobile layout; on\n // desktop (and in the default mobile layout) they live in the bottom bar\n // next to the bar play button.\n if (this.centerPrevBtn) this.centerPrevBtn.style.display = centerLayout ? '' : 'none'\n if (this.centerNextBtn) this.centerNextBtn.style.display = centerLayout ? '' : 'none'\n\n // Bar play/pause: hidden only in the legacy mobile layout, where the\n // center cluster owns playback. The default mobile layout mirrors desktop:\n // big center play (with ±N) over the video AND a bar play bottom-left.\n if (this.playBtn) this.playBtn.style.display = centerLayout ? 'none' : ''\n\n // Which bar buttons are currently relocated into the center cluster:\n // - seek ±N → whenever the cluster is used (overlay desktop or mobile);\n // - prev/next → legacy mobile layout only.\n const inCenter = (key: string): boolean => {\n if (key === 'seekBack' || key === 'seekFwd') return useCenter\n if (key === 'prev' || key === 'next') return centerLayout\n return false\n }\n\n // Reset: availability first, clear forced-overflow hides.\n this.overflowed.clear()\n for (const c of this.collapsibles) {\n if (inCenter(c.key)) {\n // Relocated into the center cluster — hide its bottom-bar twin.\n c.el.style.display = 'none'\n continue\n }\n const ok = c.available()\n c.el.style.display = ok ? '' : 'none'\n c.el.classList.remove('imp-collapsed')\n }\n\n const byPriority = this.collapsibles\n .filter((c) => c.available() && !inCenter(c.key))\n .sort((a, b) => a.priority - b.priority)\n\n // Greedily collapse while the right cluster spills past the row's edge.\n let guard = byPriority.length\n while (guard-- > 0 && this.overflowsRow()) {\n const next = byPriority.find((c) => !this.overflowed.has(c.key))\n if (!next) break\n next.el.style.display = 'none'\n next.el.classList.add('imp-collapsed')\n this.overflowed.add(next.key)\n }\n\n // ⋯ visible if it has anything to show.\n if (this.moreWrap) {\n const hasContent = this.actionItems.length > 0 || this.overflowed.size > 0\n this.moreWrap.style.display = hasContent ? '' : 'none'\n }\n }\n\n private overflowsRow(): boolean {\n const rowRight = this.row.getBoundingClientRect().right\n const groupRight = this.rightGroup.getBoundingClientRect().right\n return groupRight > rowRight + 1\n }\n\n private bind(): void {\n const p = this.player\n this.disposers.push(\n p.on('timeupdate', ({ currentTime, duration }) => {\n this.progress.update(currentTime, duration, p.bufferedEnd)\n if (this.timeLabel) {\n if (p.live) {\n this.timeLabel.hidden = true\n this.liveBadge.hidden = false\n } else {\n this.timeLabel.hidden = false\n this.liveBadge.hidden = true\n this.timeCurrent.textContent = formatTime(currentTime)\n this.timeTotal.textContent = ` / ${formatTime(duration)}`\n }\n }\n }),\n p.on('play', () => this.syncPlayState()),\n p.on('pause', () => this.syncPlayState()),\n p.on('ended', () => this.syncPlayState()),\n p.on('volumechange', () => this.syncVolume()),\n p.on('chapterchange', ({ chapter }) => {\n this.chapterLabel.textContent = chapter?.title ?? ''\n }),\n p.on('fullscreenchange', ({ active }) => {\n if (this.fullscreenBtn) {\n setIcon(this.fullscreenBtn, active ? p.icons.fullscreenExit : p.icons.fullscreen, active ? p.labels.exitFullscreen : p.labels.fullscreen)\n }\n this.scheduleReflow()\n }),\n p.on('playlistitemchange', () => {\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }),\n p.on('sourcechange', () => {\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }),\n )\n this.syncPlaylistButtons()\n this.reflow()\n }\n\n private syncPlayState(): void {\n const p = this.player\n const [icon, label] = p.ended\n ? [p.icons.replay, p.labels.replay]\n : p.paused\n ? [p.icons.play, p.labels.play]\n : [p.icons.pause, p.labels.pause]\n if (this.playBtn) setIcon(this.playBtn, icon, label)\n if (this.centerPlayBtn) setIcon(this.centerPlayBtn, icon, label)\n }\n\n private syncVolume(): void {\n if (!this.muteBtn) return\n const p = this.player\n const effective = p.muted ? 0 : p.volume\n const icon = effective === 0 ? p.icons.volumeMute : effective < 0.5 ? p.icons.volumeLow : p.icons.volumeHigh\n setIcon(this.muteBtn, icon, p.muted ? p.labels.unmute : p.labels.mute)\n this.volumeSlider.value = String(effective)\n this.volumeSlider.style.setProperty('--imp-volume-fill', `${effective * 100}%`)\n }\n\n /** Called by the player when per-source data (chapters/quality/subtitles) changes. */\n syncFeatureButtons(): void {\n this.refreshSceneTypeButton()\n // hasNext can flip when a source is marked dead — keep nav buttons honest.\n this.syncPlaylistButtons()\n this.scheduleReflow()\n }\n\n private syncPlaylistButtons(): void {\n const p = this.player\n if (this.prevBtn) this.prevBtn.disabled = !p.hasPrevious\n if (this.nextBtn) this.nextBtn.disabled = !p.hasNext\n if (this.centerPrevBtn) this.centerPrevBtn.disabled = !p.hasPrevious\n if (this.centerNextBtn) this.centerNextBtn.disabled = !p.hasNext\n }\n\n private closeMenus(except?: Menu | null): void {\n for (const menu of [this.gearMenu, this.settingsMenu, this.subtitlesMenu, this.qualityMenu, this.sceneTypeMenu, this.moreMenu]) {\n if (menu && menu !== except) menu.close()\n }\n }\n\n /** True while any control-bar dropdown (gear / quality / scene type / ⋯ / …) is open. */\n anyMenuOpen(): boolean {\n return [this.gearMenu, this.settingsMenu, this.subtitlesMenu, this.qualityMenu, this.sceneTypeMenu, this.moreMenu]\n .some((menu) => menu?.open)\n }\n\n private toggleSettingsMenu(): void {\n if (!this.settingsMenu) return\n this.closeMenus(this.settingsMenu)\n this.settingsMenu.toggle([this.speedSection()])\n }\n\n private toggleQualityMenu(): void {\n if (!this.qualityMenu || this.player.qualityLevels.length === 0) return\n this.closeMenus(this.qualityMenu)\n this.qualityMenu.toggle([this.qualitySection()])\n }\n\n private toggleSubtitlesMenu(): void {\n if (!this.subtitlesMenu || this.player.subtitleTracks.length === 0) return\n this.closeMenus(this.subtitlesMenu)\n this.subtitlesMenu.toggle([this.subtitlesSection()])\n }\n\n destroy(): void {\n for (const dispose of this.disposers) dispose()\n this.disposers = []\n this.resizeObserver?.disconnect()\n if (this.onWindowResize) window.removeEventListener('resize', this.onWindowResize)\n this.gearMenu?.destroy()\n this.sceneTypeMenu?.destroy()\n this.settingsMenu?.destroy()\n this.subtitlesMenu?.destroy()\n this.qualityMenu?.destroy()\n this.moreMenu?.destroy()\n this.nextPreviewEl?.remove()\n this.root.remove()\n }\n}\n","import { applyIcon, coverImg, el, formatTime, iconButton, previewMetaEl } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\nimport type { PlayerLabels, RelatedItem, RelatedOptions, VideoSource } from '../types'\n\n/** Preview poster: cover image + big play button, shown until the first play. */\nexport class PosterOverlay {\n readonly root: HTMLElement\n /** Wrapper <div> holding a real <img> — same markup as the lazy placeholder\n * (no swap on mount), and the <img> stays LCP-eligible. */\n private image: HTMLElement\n private img: HTMLImageElement\n\n constructor(player: Player) {\n this.root = el('div', 'imp-poster')\n this.image = el('div', 'imp-poster__image')\n this.image.hidden = true\n this.img = coverImg('')\n this.image.append(this.img)\n const big = iconButton('imp-poster__play', player.labels.play, player.icons.bigPlay)\n big.addEventListener('click', () => void player.play())\n this.root.append(this.image, big)\n // Background tap (not the play button). On touch, reveal the control bar\n // instead of starting playback (mouse click still plays as before).\n this.root.addEventListener('pointerup', (e) => {\n if (e.target !== this.root && e.target !== this.image && e.target !== this.img) return\n if (e.pointerType === 'touch' && player.posterTapReveal()) return\n void player.play()\n })\n }\n\n setSource(source: VideoSource | null): void {\n const p = source?.poster\n if (p) {\n const full = typeof p === 'string' ? { src: p } : p\n this.img.src = full.src\n // LCP-grade attributes when the consumer provides the full descriptor.\n if (full.srcset) this.img.srcset = full.srcset\n else this.img.removeAttribute('srcset')\n if (full.sizes) this.img.sizes = full.sizes\n else this.img.removeAttribute('sizes')\n if (full.width) this.img.width = full.width\n else this.img.removeAttribute('width')\n if (full.height) this.img.height = full.height\n else this.img.removeAttribute('height')\n this.img.alt = full.alt ?? ''\n // First poster is the page's hero — hint the browser to load it eagerly.\n this.img.fetchPriority = 'high'\n this.image.hidden = false\n } else {\n this.img.removeAttribute('src')\n this.image.hidden = true\n }\n }\n\n show(): void {\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/**\n * Pause screen \"slot\". Default content is title + description of the current\n * source; consumers can replace it with any element (or a factory), and the\n * Vue wrapper feeds a real <slot> through the same API.\n */\nexport class PauseScreen {\n readonly root: HTMLElement\n private defaultContent: HTMLElement\n private channel: HTMLElement\n private channelAvatar: HTMLElement\n private channelName: HTMLElement\n private channelUrl: string | null = null\n private title: HTMLElement\n private description: HTMLElement\n private sponsor: HTMLAnchorElement\n private sponsorLabel: HTMLElement\n private sponsorText: HTMLElement\n private custom: HTMLElement | null = null\n /** Which parts the consumer allows (a hidden part stays hidden even with data). */\n private visible = { title: true, description: true, sponsor: true }\n\n constructor(private labels: PlayerLabels) {\n this.root = el('div', 'imp-pause-screen')\n this.root.hidden = true\n this.defaultContent = el('div', 'imp-pause-screen__default')\n\n this.channel = el('button', 'imp-channel', { type: 'button' })\n this.channel.hidden = true\n this.channelAvatar = el('div', 'imp-channel__avatar')\n this.channelName = el('div', 'imp-channel__name')\n // The plate sits at the right edge — text first, avatar at the outer edge.\n this.channel.append(this.channelName, this.channelAvatar)\n this.channel.addEventListener('click', () => {\n if (this.channelUrl) window.open(this.channelUrl, '_blank', 'noopener')\n })\n\n const heading = el('div', 'imp-pause-screen__heading')\n this.title = el('div', 'imp-pause-screen__title')\n this.description = el('div', 'imp-pause-screen__description')\n\n // Sponsor / promo link. Sits ABOVE the title (the promo slot comes first),\n // shown only when set on the source.\n this.sponsor = el('a', 'imp-sponsor', { target: '_blank', rel: 'nofollow noopener' }) as HTMLAnchorElement\n this.sponsor.hidden = true\n this.sponsorLabel = el('span', 'imp-sponsor__label')\n this.sponsorText = el('span', 'imp-sponsor__text')\n this.sponsor.append(this.sponsorLabel, this.sponsorText)\n // Don't let a click on the link bubble to the pause-screen play toggle.\n this.sponsor.addEventListener('click', (e) => e.stopPropagation())\n\n // Order on the overlay: sponsor (ad link) → title → description.\n heading.append(this.sponsor, this.title, this.description)\n // Title block on the left, channel pinned to the right edge.\n this.defaultContent.append(heading, this.channel)\n this.root.append(this.defaultContent)\n }\n\n setCustomContent(element: HTMLElement | null): void {\n this.custom?.remove()\n this.custom = element\n if (element) {\n element.classList.add('imp-pause-screen__custom')\n this.root.append(element)\n }\n this.defaultContent.hidden = element !== null\n // Custom content renders as a full-cover centered overlay (ad use-case),\n // not the top info strip.\n this.root.classList.toggle('imp-pause-screen--custom', element !== null)\n }\n\n /** True while consumer-provided custom content is mounted. */\n get hasCustom(): boolean {\n return this.custom !== null\n }\n\n /** Hide specific parts of the default overlay (consumer renders them itself). */\n setVisibility(parts: { title?: boolean; description?: boolean; sponsor?: boolean }): void {\n this.visible = {\n title: parts.title ?? this.visible.title,\n description: parts.description ?? this.visible.description,\n sponsor: parts.sponsor ?? this.visible.sponsor,\n }\n }\n\n setSource(source: VideoSource | null): void {\n this.title.textContent = source?.title ?? ''\n this.description.textContent = source?.description ?? ''\n this.title.hidden = !this.visible.title || !source?.title\n this.description.hidden = !this.visible.description || !source?.description\n\n // Sponsor link — only for sponsored videos (i.e. when provided) and allowed.\n const sponsor = this.visible.sponsor ? source?.sponsor : undefined\n this.sponsor.hidden = !sponsor\n if (sponsor) {\n this.sponsor.href = sponsor.url\n this.sponsorText.textContent = sponsor.text\n this.sponsorLabel.textContent = sponsor.label ?? this.labels.sponsored\n }\n\n const channel = source?.channel\n this.channel.hidden = !channel\n this.channelUrl = channel?.url ?? null\n this.channel.classList.toggle('imp-channel--link', Boolean(channel?.url))\n if (channel) {\n this.channelName.textContent = channel.name\n this.channelAvatar.className = `imp-channel__avatar imp-channel__avatar--${channel.avatarShape ?? 'circle'}`\n if (channel.avatar) {\n this.channelAvatar.textContent = ''\n this.channelAvatar.append(coverImg(channel.avatar, channel.name))\n } else {\n // Styled first-letter placeholder when there is no avatar.\n this.channelAvatar.textContent = (channel.name.trim()[0] ?? '?').toUpperCase()\n }\n }\n }\n\n show(): void {\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/** Grid of related videos, shown on pause and/or ended. */\nexport class RelatedOverlay {\n readonly root: HTMLElement\n private grid: HTMLElement\n private heading: HTMLElement\n private clickBehavior: NonNullable<RelatedOptions['clickBehavior']> = 'player'\n\n constructor(private player: Player) {\n this.root = el('div', 'imp-related')\n this.root.hidden = true\n this.heading = el('div', 'imp-related__title')\n this.grid = el('div', 'imp-related__grid')\n const close = iconButton('imp-related__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n this.root.append(close, this.heading, this.grid)\n }\n\n setOptions(options: RelatedOptions | undefined): void {\n this.grid.textContent = ''\n if (!options) return\n this.clickBehavior = options.clickBehavior ?? 'player'\n this.heading.textContent = options.title ?? this.player.labels.related\n for (const item of options.items) {\n this.grid.append(this.buildCard(item))\n }\n }\n\n private buildCard(item: RelatedItem): HTMLElement {\n const card = el('button', 'imp-related__card', { type: 'button' })\n const thumb = el('div', 'imp-related__thumb')\n if (item.poster) thumb.append(coverImg(item.poster))\n if (item.duration) {\n const badge = el('span', 'imp-related__duration')\n badge.textContent = item.duration\n thumb.append(badge)\n }\n const title = el('div', 'imp-related__card-title')\n title.textContent = item.title\n card.append(thumb, title)\n card.addEventListener('click', () => {\n // The event always fires — consumers can fully drive navigation themselves.\n this.player.emit('relatedclick', { item })\n this.hide()\n this.activate(item)\n })\n return card\n }\n\n /** Apply the configured click behavior. */\n private activate(item: RelatedItem): void {\n const url = item.url ?? item.source?.src\n switch (this.clickBehavior) {\n case 'newWindow':\n if (url) window.open(url, '_blank', 'noopener')\n break\n case 'currentTab':\n if (url) window.location.href = url\n break\n case 'player':\n default:\n if (item.source) {\n this.player.load(item.source)\n void this.player.play()\n } else if (item.url) {\n window.open(item.url, '_blank', 'noopener')\n }\n }\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n if (this.grid.childElementCount === 0) return\n this.root.hidden = false\n this.player.emit('relatedshow', undefined)\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n\n/**\n * End-of-video \"up next\" overlay shown when autoplay is OFF and a next item\n * exists: a preview card of the next video + a \"Play next\" button.\n */\nexport class UpNextOverlay {\n readonly root: HTMLElement\n\n constructor(private player: Player) {\n this.root = el('div', 'imp-upnext')\n this.root.hidden = true\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n const src = this.player.nextSource\n if (!src) {\n // Nothing playable ahead (tail marked dead) — don't leave a stale card.\n this.hide()\n return\n }\n const { labels } = this.player\n\n this.root.textContent = ''\n const card = el('div', 'imp-upnext__card')\n\n const kicker = el('div', 'imp-upnext__kicker')\n kicker.textContent = labels.nextVideo\n card.append(kicker)\n\n const upPoster = posterSrc(src.poster)\n if (upPoster) {\n const thumb = el('button', 'imp-upnext__thumb', { type: 'button', 'aria-label': labels.playNext })\n const img = coverImg(upPoster)\n // Dead poster → dead video: skip it and re-render with the next candidate.\n img.addEventListener('error', () => {\n this.player.markSourceDead(src)\n if (!this.root.hidden) this.show()\n }, { once: true })\n thumb.append(img)\n if (src.duration) {\n const dur = el('span', 'imp-upnext__duration')\n dur.textContent = formatTime(src.duration)\n thumb.append(dur)\n }\n const play = el('span', 'imp-upnext__thumb-play')\n applyIcon(play, this.player.icons.bigPlay)\n thumb.append(play)\n thumb.addEventListener('click', () => this.player.next())\n card.append(thumb)\n }\n\n if (src.title) {\n const title = el('div', 'imp-upnext__title')\n title.textContent = src.title\n card.append(title)\n }\n if (src.previewMeta?.length) {\n card.append(previewMetaEl(src.previewMeta, 'imp-upnext'))\n }\n\n const btn = el('button', 'imp-upnext__btn', { type: 'button' })\n btn.textContent = labels.playNext\n btn.addEventListener('click', () => this.player.next())\n card.append(btn)\n\n this.root.append(card)\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n}\n","import { coverImg, el, formatTime, iconButton } from '../core/dom'\nimport { posterSrc } from '../placeholder'\nimport type { Player } from '../player'\n\n/** Slide-in panel listing playlist items; survives source changes. */\nexport class PlaylistPanel {\n readonly root: HTMLElement\n private list: HTMLElement\n private shuffleBtn: HTMLButtonElement\n private repeatBtn: HTMLButtonElement\n\n constructor(private player: Player, layout: 'sidebar' | 'bottom' = 'sidebar', playlistTitle?: string) {\n this.root = el('div', `imp-playlist imp-playlist--${layout}`)\n this.root.hidden = true\n const header = el('div', 'imp-playlist__header')\n const title = el('div', 'imp-playlist__heading')\n if (playlistTitle) {\n // Named playlist: small \"Playlist\" kicker + the name in the primary font.\n const kicker = el('div', 'imp-playlist__kicker')\n kicker.textContent = player.labels.playlist\n const name = el('div', 'imp-playlist__name')\n name.textContent = playlistTitle\n title.append(kicker, name)\n } else {\n title.textContent = player.labels.playlist\n }\n\n const tools = el('div', 'imp-playlist__tools')\n this.shuffleBtn = iconButton('imp-playlist__shuffle', player.labels.shuffle, player.icons.shuffle)\n this.shuffleBtn.addEventListener('click', () => {\n player.setShuffle(!player.shuffle)\n this.syncModes()\n })\n this.repeatBtn = iconButton('imp-playlist__repeat', player.labels.repeat, player.icons.repeat)\n this.repeatBtn.addEventListener('click', () => {\n player.setRepeat(!player.repeat)\n this.syncModes()\n })\n const close = iconButton('imp-playlist__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n tools.append(this.shuffleBtn, this.repeatBtn, close)\n\n header.append(title, tools)\n this.list = el('div', 'imp-playlist__list')\n this.root.append(header, this.list)\n this.rebuild()\n this.syncModes()\n }\n\n private syncModes(): void {\n this.shuffleBtn.classList.toggle('imp-btn--active', this.player.shuffle)\n this.repeatBtn.classList.toggle('imp-btn--active', this.player.repeat)\n }\n\n rebuild(): void {\n this.list.textContent = ''\n this.player.playlist.forEach((source, index) => {\n const item = el('button', 'imp-playlist__item', { type: 'button' })\n if (index === this.player.index) item.classList.add('imp-playlist__item--active')\n const thumb = el('div', 'imp-playlist__thumb')\n const thumbSrc = posterSrc(source.poster)\n if (thumbSrc) thumb.append(coverImg(thumbSrc))\n const meta = el('div', 'imp-playlist__meta')\n const itemTitle = el('div', 'imp-playlist__title')\n itemTitle.textContent = source.title ?? `#${index + 1}`\n meta.append(itemTitle)\n if (source.duration) {\n const dur = el('div', 'imp-playlist__duration')\n dur.textContent = formatTime(source.duration)\n meta.append(dur)\n }\n item.append(thumb, meta)\n item.addEventListener('click', () => {\n this.player.playItem(index)\n // Mobile full-cover sheet: close on pick so the video shows at once.\n if (window.innerWidth <= 767) this.hide()\n })\n this.list.append(item)\n })\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n this.rebuild()\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n\n toggle(): void {\n if (this.visible) this.hide()\n else this.show()\n }\n}\n","import { coverImg, el, formatTime, iconButton } from '../core/dom'\nimport type { Player } from '../player'\n\n/**\n * Scene list — the chapters of the current video as a clickable panel, each\n * with a preview cropped from the VTT sprite at the scene start.\n */\nexport class ScenesPanel {\n readonly root: HTMLElement\n private list: HTMLElement\n private items: HTMLButtonElement[] = []\n\n constructor(private player: Player, layout: 'sidebar' | 'bottom' = 'bottom') {\n this.root = el('div', `imp-playlist imp-scenes imp-playlist--${layout}`)\n this.root.hidden = true\n const header = el('div', 'imp-playlist__header')\n const title = el('span')\n title.textContent = player.labels.scenes\n const close = iconButton('imp-playlist__close', player.labels.close, player.icons.close)\n close.addEventListener('click', () => this.hide())\n header.append(title, close)\n this.list = el('div', 'imp-playlist__list')\n this.root.append(header, this.list)\n\n player.on('chapterchange', () => this.syncActive())\n }\n\n /** Re-render from the player's current chapters + thumbnail track. */\n rebuild(): void {\n this.list.textContent = ''\n this.items = []\n const thumbs = this.player.thumbnails\n for (const chapter of this.player.chapterList) {\n const item = el('button', 'imp-playlist__item', { type: 'button' })\n const thumb = el('div', 'imp-playlist__thumb')\n\n const cue = thumbs?.cueAt(chapter.start + 0.01) ?? null\n if (cue?.xywh) {\n // Crop the sprite region and scale it into the thumb box.\n const inner = el('div', 'imp-scenes__sprite')\n inner.style.width = `${cue.xywh.w}px`\n inner.style.height = `${cue.xywh.h}px`\n inner.style.backgroundImage = `url(\"${cue.src}\")`\n inner.style.backgroundPosition = `-${cue.xywh.x}px -${cue.xywh.y}px`\n inner.style.setProperty('--imp-sprite-w', String(cue.xywh.w))\n thumb.append(inner)\n } else if (cue) {\n // Whole-image cue (no sprite crop) → a real <img>; sprite crops above\n // keep the background-position mechanism.\n thumb.append(coverImg(cue.src))\n }\n\n const meta = el('div', 'imp-playlist__meta')\n const itemTitle = el('div', 'imp-playlist__title')\n itemTitle.textContent = chapter.title || formatTime(chapter.start)\n const time = el('div', 'imp-playlist__duration')\n time.textContent = formatTime(chapter.start)\n meta.append(itemTitle, time)\n\n item.append(thumb, meta)\n item.addEventListener('click', () => {\n this.player.seek(chapter.start)\n void this.player.play()\n // On mobile the panel is a full-cover sheet — close it so the chosen\n // scene is visible immediately (no manual close). Desktop sidebar/strip\n // stays open: the video is visible alongside it.\n if (window.innerWidth <= 767) this.hide()\n })\n this.list.append(item)\n this.items.push(item)\n }\n this.syncActive()\n }\n\n private syncActive(): void {\n const current = this.player.chapter\n const chapters = this.player.chapterList\n this.items.forEach((item, i) => {\n item.classList.toggle('imp-playlist__item--active', current !== null && chapters[i] === current)\n })\n }\n\n get visible(): boolean {\n return !this.root.hidden\n }\n\n show(): void {\n this.rebuild()\n this.root.hidden = false\n }\n\n hide(): void {\n this.root.hidden = true\n }\n\n toggle(): void {\n if (this.visible) this.hide()\n else this.show()\n }\n}\n","import { clamp, el } from './core/dom'\nimport { posterSrc } from './placeholder'\nimport { Emitter } from './core/events'\nimport { defaultIcons } from './core/icons'\nimport { defaultLabels } from './core/labels'\nimport { getLocale } from './core/localeRegistry'\nimport { buildHeatmapValues, buildHeatmapValuesDense } from './features/heatmap'\nimport { AdManager } from './features/ads/manager'\nimport { chapterAt, loadChaptersVtt, normalizeChapters, type NormalizedChapter } from './features/chapters'\nimport { attachSource, type Level, type SourceController } from './features/hls'\nimport { ThumbnailTrack } from './features/thumbnails'\nimport { Controls } from './ui/controls'\nimport { PauseScreen, PosterOverlay, RelatedOverlay, UpNextOverlay } from './ui/overlays'\nimport { PlaylistPanel } from './ui/playlistPanel'\nimport { ScenesPanel } from './ui/scenesPanel'\nimport type {\n ActionsOptions,\n ControlsOptions,\n IconName,\n IconSource,\n PlayerEventMap,\n PlayerLabels,\n PlayerOptions,\n PlaylistOptions,\n SceneGroup,\n SubtitleTrack,\n VideoSource,\n} from './types'\n\n/**\n * Page-level valve for the benign \"play() was interrupted by a new load\n * request / a call to pause()\" AbortError. Interrupting a pending `play()`\n * REJECTS its promise by spec — unavoidable when sources switch — and if the\n * pending promise lives in consumer code (a bare `video.play()` in the app),\n * the library cannot attach a catch to it. These exact messages are produced\n * only by media-element play interruptions and are pure console noise, so we\n * suppress precisely them while at least one player instance exists.\n */\nlet playValveCount = 0\nfunction onBenignPlayAbort(e: PromiseRejectionEvent): void {\n const r = e.reason as { name?: string; message?: string } | null | undefined\n if (r?.name === 'AbortError' && /interrupted by a (new load request|call to pause)/i.test(r.message ?? '')) {\n e.preventDefault()\n }\n}\nfunction acquirePlayValve(): void {\n if (typeof window !== 'undefined' && ++playValveCount === 1) {\n window.addEventListener('unhandledrejection', onBenignPlayAbort)\n }\n}\nfunction releasePlayValve(): void {\n if (typeof window !== 'undefined' && --playValveCount === 0) {\n window.removeEventListener('unhandledrejection', onBenignPlayAbort)\n }\n}\n\n/**\n * A rejected `play()` we recover from is still worth a line in the console:\n * silent `.catch(() => {})` made gesture/autoplay-policy problems (e.g. the\n * mobile double-tap-to-start bug) undiagnosable from the field.\n */\nfunction logPlayRejection(err: unknown): void {\n console.debug('[itube-player] play() rejected:', err)\n}\n\n/** `subtitles` accepts a URL, one track, or an array — normalize to an array. */\nfunction normalizeSubtitles(input: VideoSource['subtitles']): SubtitleTrack[] {\n if (!input) return []\n if (typeof input === 'string') return [{ src: input, label: 'Subtitles' }]\n return Array.isArray(input) ? input : [input]\n}\n\nconst defaultControls: Required<ControlsOptions> = {\n play: true,\n progress: true,\n time: true,\n volume: true,\n fullscreen: true,\n pip: true,\n // Settings-type controls default into the unified ⚙ dropdown.\n speed: 'gear',\n settings: 'gear', // legacy alias for `speed`; resolved in the constructor\n quality: 'gear',\n scenes: true,\n sceneTypes: true,\n heatmap: true,\n subtitles: 'gear',\n seekButtons: true,\n // Prev/seek/play/next as a centered overlay on every viewport.\n seekPlacement: 'overlay',\n // Mobile: only ±N seek over the video; play/next live in the bottom bar.\n mobileLayout: 'bar',\n order: [], // empty → built-in right-cluster order\n playlist: true,\n hidePrev: true,\n hideNext: false,\n playlistButton: true,\n nextPreview: true,\n hideDelay: 2500,\n revealBeforePlay: true,\n}\n\n/**\n * Framework-agnostic video player.\n *\n * ```ts\n * import { Player } from 'itube-modern-player'\n * import 'itube-modern-player/style.css'\n *\n * const player = new Player('#mount', { source: { src: 'video.m3u8', title: 'Demo' } })\n * ```\n *\n * Design rules this codebase follows (deliberately unlike fluid-player):\n * - no globals, no instance registry — `new Player()` as many times as you like;\n * - one overlay layer laid out with grid/flex, not a pile of absolute elements;\n * - `destroy()` returns the mount node to its original state;\n * - every event is typed, every string and icon is replaceable.\n */\nexport class Player {\n readonly container: HTMLElement\n readonly video: HTMLVideoElement\n\n readonly labels: PlayerLabels\n readonly icons: Record<IconName, IconSource>\n readonly controlsOptions: Required<ControlsOptions>\n readonly actionsOptions: ActionsOptions\n readonly playbackRates: number[]\n readonly seekStep: number\n\n /** Set by the controls while the user drags the seek bar. */\n scrubbing = false\n\n private emitter = new Emitter<PlayerEventMap>()\n private mount: HTMLElement\n private options: PlayerOptions\n private playlistOptions: Required<PlaylistOptions>\n /** External navigation mode: advance intents emit `advancerequest` instead of switching. */\n private externalAdvance = false\n private abort = new AbortController()\n\n private sources: VideoSource[] = []\n /** Sources detected as broken (poster failed to load) — \"next\" skips them. */\n private deadSources = new WeakSet<VideoSource>()\n private currentIndex = -1\n private sourceController: SourceController | null = null\n private loadToken = 0\n\n private chapters: NormalizedChapter[] = []\n private currentChapter: NormalizedChapter | null = null\n /** Typed scene breakdowns; the active one drives `chapters`. */\n private sceneGroupList: SceneGroup[] = []\n private activeSceneGroupId = ''\n private progressiveQuality = -1\n private thumbTrack: ThumbnailTrack | null = null\n private shuffleMode = false\n\n private controls: Controls\n private poster: PosterOverlay\n private pauseScreen: PauseScreen\n private related: RelatedOverlay\n private upNext: UpNextOverlay\n /** Runtime autoplay-next toggle (gear switch); seeds from playlist.autoAdvance. */\n private autoAdvanceEnabled = true\n /** localStorage key for persisted prefs, or null when persistence is off. */\n private persistKey: string | null = null\n private persistVolume = false\n private persistAutoAdvance = false\n private playlistPanel: PlaylistPanel\n private scenesPanel: ScenesPanel\n private spinner: HTMLElement\n private errorBox!: HTMLElement\n private errorText!: HTMLElement\n private retryBtn!: HTMLButtonElement\n private errorNextBtn!: HTMLButtonElement\n private decodeRecoveries = 0\n private adManager: AdManager | null = null\n\n private playedOnce = false\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n /** Auto-hide timer for the before-play control-bar \"peek\" (touch). */\n private peekTimer: ReturnType<typeof setTimeout> | null = null\n /** Retry-button throttle: last press time + the re-enable timer. */\n private lastRetryAt = 0\n private retryTimer: ReturnType<typeof setTimeout> | null = null\n /** Playhead position to restore when retrying after a mid-video error. */\n private errorResumeTime = 0\n /** Last known good playhead time (for the retry-resume above). */\n private lastPlayheadTime = 0\n /** Seek requested before metadata (pre-start scrub) — applied on loadedmetadata. */\n private pendingSeek: number | null = null\n private destroyed = false\n\n constructor(target: string | HTMLElement, options: PlayerOptions = {}) {\n const mount = typeof target === 'string' ? document.querySelector<HTMLElement>(target) : target\n if (!mount) throw new Error(`[itube-player] mount target not found: ${String(target)}`)\n this.mount = mount\n this.options = options\n\n this.labels = { ...defaultLabels, ...getLocale(options.language), ...options.labels }\n this.icons = { ...defaultIcons, ...options.icons }\n // Resolve the deprecated `settings` alias from the raw options (an explicit\n // `speed` wins; else legacy `settings`; else the 'gear' default).\n const rawControls = options.controls ?? {}\n const speed = rawControls.speed ?? rawControls.settings ?? 'gear'\n this.controlsOptions = { ...defaultControls, ...rawControls, speed, settings: speed }\n this.actionsOptions = options.actions ?? {}\n this.playbackRates = options.playbackRates ?? [0.5, 1, 1.5, 2]\n this.seekStep = options.seekStep ?? 15\n this.playlistOptions = { title: '', autoAdvance: true, loop: false, shuffle: false, startIndex: 0, layout: 'sidebar', externalAdvance: false, ...options.playlist }\n this.shuffleMode = this.playlistOptions.shuffle\n this.externalAdvance = this.playlistOptions.externalAdvance\n\n // Persistence: set up before applying volume/autoAdvance so saved prefs win.\n if (options.persist) {\n const p = options.persist === true ? {} : options.persist\n this.persistKey = p.key ?? 'itube-player'\n this.persistVolume = p.volume !== false\n this.persistAutoAdvance = p.autoAdvance !== false\n }\n const saved = this.readPersisted()\n\n this.autoAdvanceEnabled = this.persistAutoAdvance && typeof saved?.autoAdvance === 'boolean'\n ? saved.autoAdvance\n : this.playlistOptions.autoAdvance\n\n this.sources = options.source ? (Array.isArray(options.source) ? [...options.source] : [options.source]) : []\n\n // --- DOM ----------------------------------------------------------\n this.container = el('div', 'imp-player', { tabindex: '0' })\n if (options.className) this.container.classList.add(options.className)\n\n // Custom styling → CSS variables on the container (overridable per instance).\n const styling = options.styling\n if (styling?.themeColor) this.container.style.setProperty('--imp-accent', styling.themeColor)\n if (styling?.likeColor) this.container.style.setProperty('--imp-like', styling.likeColor)\n if (styling?.dislikeColor) this.container.style.setProperty('--imp-dislike', styling.dislikeColor)\n if (styling?.fontFamily) this.container.style.setProperty('--imp-font', styling.fontFamily)\n if (styling?.borderRadius !== undefined) {\n const r = typeof styling.borderRadius === 'number' ? `${styling.borderRadius}px` : styling.borderRadius\n this.container.style.setProperty('--imp-radius', r)\n }\n if (styling?.playButtonStyle === 'inverted') this.container.classList.add('imp-player--play-inverted')\n\n // A consumer-provided element (the lazy entries pass one unlocked inside\n // the click gesture — see gesture.ts) is adopted as-is, just classed.\n this.video = options.videoElement ?? el('video', 'imp-video')\n this.video.classList.add('imp-video')\n // A provided element may arrive mid-\"phantom playback\" (a gesture-unlock\n // play() without a source leaves `paused === false`). Normalize: without a\n // clean paused state the later play() produces no paused→playing\n // transition — no `play` event, broken poster/UI/rendering chain.\n if (options.videoElement && !options.videoElement.paused) {\n try { options.videoElement.pause() } catch { /* nothing to pause */ }\n }\n if (options.playsInline !== false) this.video.setAttribute('playsinline', '')\n if (options.crossOrigin !== undefined) this.video.crossOrigin = options.crossOrigin\n this.video.preload = 'metadata'\n if (options.loop) this.video.loop = true\n const savedAV = this.persistVolume ? saved : null\n this.video.volume = clamp(savedAV?.volume ?? options.volume ?? 1, 0, 1)\n if (savedAV?.muted ?? options.muted) this.video.muted = true\n\n const layer = el('div', 'imp-layer')\n this.spinner = el('div', 'imp-spinner')\n this.spinner.hidden = true\n this.errorBox = el('div', 'imp-error')\n this.errorBox.hidden = true\n this.errorText = el('div', 'imp-error__text')\n this.retryBtn = el('button', 'imp-error__retry', { type: 'button' })\n const retryLabel = el('span', 'imp-error__retry-label')\n retryLabel.textContent = this.labels.tryAgain\n // Shown (spinning) instead of the label while a retry is in flight.\n this.retryBtn.append(retryLabel, el('span', 'imp-error__retry-spinner'))\n this.retryBtn.addEventListener('click', () => this.retry())\n // \"Next video\" escape hatch: a broken item mid-playlist (e.g. auto-advance\n // landed on a dead stream) must not trap the user on retry alone.\n this.errorNextBtn = el('button', 'imp-error__next', { type: 'button' })\n this.errorNextBtn.textContent = this.labels.nextVideo\n this.errorNextBtn.addEventListener('click', () => {\n // Unlike retry, this leaves the broken source behind — drop the overlay\n // right away and let the next video load with the normal spinner UX.\n this.clearError()\n this.next()\n })\n const errorActions = el('div', 'imp-error__actions')\n errorActions.append(this.retryBtn, this.errorNextBtn)\n this.errorBox.append(this.errorText, errorActions)\n\n this.pauseScreen = new PauseScreen(this.labels)\n this.poster = new PosterOverlay(this)\n this.related = new RelatedOverlay(this)\n this.related.setOptions(options.related)\n this.upNext = new UpNextOverlay(this)\n this.controls = new Controls(this)\n this.playlistPanel = new PlaylistPanel(this, this.playlistOptions.layout, this.playlistOptions.title || undefined)\n this.scenesPanel = new ScenesPanel(this, options.scenes?.layout ?? 'bottom')\n\n const middle = el('div', 'imp-layer__middle')\n middle.append(this.spinner, this.errorBox, this.controls.center)\n const bottom = el('div', 'imp-layer__bottom')\n bottom.append(this.controls.root)\n layer.append(this.pauseScreen.root, this.related.root, this.upNext.root, middle, bottom)\n\n this.container.append(this.video, layer, this.poster.root, this.playlistPanel.root, this.scenesPanel.root)\n this.mount.append(this.container)\n\n if (options.pauseScreen instanceof HTMLElement) {\n this.pauseScreen.setCustomContent(options.pauseScreen)\n } else if (typeof options.pauseScreen === 'function') {\n this.pauseScreen.setCustomContent(options.pauseScreen(this))\n } else if (options.pauseScreen && typeof options.pauseScreen === 'object') {\n // Plain config object: default overlay with some parts hidden.\n this.pauseScreen.setVisibility(options.pauseScreen)\n }\n\n const adConfig = options.adConfig ?? options.ads\n if (adConfig && adConfig.adList.length > 0) {\n this.adManager = new AdManager(\n { container: this.container, contentVideo: this.video, emitter: this.emitter, labels: this.labels, closeIcon: this.icons.close },\n adConfig,\n )\n }\n\n this.emitter.on('error', ({ fatal }) => {\n // Only a fatal playback error takes over the UI (a failed thumbnail /\n // chapter track must not blank the player). Show a friendly localized\n // message + a retry button, and hide play/seek controls.\n if (!fatal) return\n // Remember where playback broke so \"try again\" resumes from there. Prefer\n // the last tracked playhead; fall back to the live currentTime. A zero\n // capture (failed reload never played) must NOT wipe a previous position.\n const at = this.lastPlayheadTime || this.video.currentTime\n if (Number.isFinite(at) && at > 0) this.errorResumeTime = at\n this.errorText.textContent = this.labels.videoNotFound\n // Offer \"next video\" only when there is somewhere to go (always true in\n // external-advance mode — the intent is emitted to the app).\n this.errorNextBtn.hidden = !this.hasNext\n this.errorBox.hidden = false\n this.spinner.hidden = true\n this.poster.hide()\n this.container.classList.add('imp-player--error')\n })\n\n acquirePlayValve()\n this.bindVideoEvents()\n this.bindIdleHide()\n if (options.keyboard !== false) this.bindKeyboard()\n\n // Tap/click on the video surface. Desktop (mouse): toggles playback, as\n // before. Touch (phones): a tap toggles *controls* visibility instead —\n // play/pause lives on the on-screen center button. This is the standard\n // mobile model and avoids the iOS problem where one tap fired both a\n // play-toggle (click) and a show-controls (pointerdown) at once.\n // We use pointerup (not click) so iOS doesn't also deliver a synthesized\n // mouse click that double-fires the handler.\n this.video.addEventListener('pointerup', (e) => {\n if (this.adPlaying || !this.playedOnce) return\n if (e.pointerType === 'mouse') {\n this.togglePlay()\n return\n }\n // Touch / pen: reveal controls if hidden, hide them if shown while\n // playing. When paused the controls (and pause screen) stay up.\n if (this.container.classList.contains('imp-player--idle')) {\n this.showControlsNow()\n this.scheduleIdle()\n } else if (!this.video.paused) {\n this.container.classList.add('imp-player--idle')\n }\n }, { signal: this.abort.signal })\n this.pauseScreen.root.addEventListener('click', (e) => {\n // With custom content (e.g. an ad block) the backdrop must not resume —\n // the consumer's own \"close & continue\" control owns that.\n if (this.pauseScreen.hasCustom) return\n if (e.target === this.pauseScreen.root) this.togglePlay()\n }, { signal: this.abort.signal })\n\n if (this.sources.length > 0) {\n // `playOnInit` is the current name; `autoplay` stays as a deprecated alias.\n void this.loadItem(clamp(this.playlistOptions.startIndex, 0, this.sources.length - 1), Boolean(options.playOnInit ?? options.autoplay))\n }\n // Deferred so code running right after `new Player()` can actually\n // subscribe — a synchronous emit here would fire before any `.on('ready')`.\n queueMicrotask(() => {\n if (!this.destroyed) this.emitter.emit('ready', { player: this })\n })\n }\n\n // === events ===========================================================\n\n on<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): () => void {\n return this.emitter.on(event, fn)\n }\n\n once<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): () => void {\n return this.emitter.once(event, fn)\n }\n\n off<K extends keyof PlayerEventMap>(event: K, fn: (payload: PlayerEventMap[K]) => void): void {\n this.emitter.off(event, fn)\n }\n\n /** @internal — UI modules emit through the player. */\n emit<K extends keyof PlayerEventMap>(event: K, payload: PlayerEventMap[K]): void {\n this.emitter.emit(event, payload)\n }\n\n // === playback =========================================================\n\n get paused(): boolean {\n return this.video.paused\n }\n\n get ended(): boolean {\n return this.video.ended\n }\n\n get currentTime(): number {\n return this.video.currentTime\n }\n\n get duration(): number {\n return this.video.duration || 0\n }\n\n get live(): boolean {\n return this.video.duration === Infinity\n }\n\n get bufferedEnd(): number {\n const ranges = this.video.buffered\n return ranges.length > 0 ? ranges.end(ranges.length - 1) : 0\n }\n\n get adPlaying(): boolean {\n return this.adManager?.adPlaying ?? false\n }\n\n async play(skipAds = false): Promise<void> {\n if (this.destroyed || this.adPlaying) return\n // `skipAds` — used by retry(): a broken main video must not trigger a VAST.\n if (this.adManager && !skipAds) {\n await this.adManager.playPreRoll()\n if (this.destroyed) return\n // The pre-roll resumes content itself; if there was none, fall through.\n if (!this.video.paused) return\n }\n try {\n await this.video.play()\n } catch (err) {\n const name = (err as { name?: string })?.name\n // Switching sources (esp. hls.js mid-attach) can momentarily leave the\n // element with no decodable source: retry once when it's ready.\n if (name === 'NotSupportedError' && this.video.readyState === 0) {\n // Token-guarded: if another source loads meanwhile, don't autostart it.\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token === this.loadToken) void this.video.play().catch(logPlayRejection)\n }, { once: true, signal: this.abort.signal })\n return\n }\n // AbortError = a newer load interrupted this play (benign). Otherwise it's\n // typically an autoplay-policy block — restore the poster so the user can\n // tap play. Genuine media failures surface via the `error` event.\n if (name !== 'AbortError') {\n logPlayRejection(err)\n if (!this.playedOnce) this.poster.show()\n }\n }\n }\n\n pause(): void {\n if (this.adPlaying) return\n this.video.pause()\n }\n\n togglePlay(): void {\n if (this.video.paused || this.video.ended) void this.play()\n else this.pause()\n }\n\n /**\n * Clear a fatal-error overlay and reload the current source, resuming from\n * where playback broke. Rate-limited by `options.retryThrottle` (default 5s):\n * the button is disabled for that window after each press.\n */\n retry(): void {\n const throttle = this.options.retryThrottle ?? 5000\n const now = Date.now()\n if (now - this.lastRetryAt < throttle) return\n this.lastRetryAt = now\n // Disabled + a spinner in place of the label while the reload is in flight.\n this.retryBtn.disabled = true\n this.retryBtn.classList.add('imp-error__retry--loading')\n if (this.retryTimer) clearTimeout(this.retryTimer)\n this.retryTimer = setTimeout(() => {\n if (this.destroyed) return\n this.retryBtn.disabled = false\n this.retryBtn.classList.remove('imp-error__retry--loading')\n }, throttle)\n\n const resumeAt = this.errorResumeTime\n // Keep the error overlay (now showing the spinner) up during the reload —\n // clearing it here would flash the controls before the outcome is known.\n // A successful (re)load clears it via the canplay/playing/seeked handler.\n // skipAds: don't run a VAST when re-attempting a broken main video.\n void this.loadItem(this.currentIndex, true, true)\n // loadItem cleared errorResumeTime (normal-navigation hygiene) — restore it\n // so a SECOND retry after another failure still resumes from the same spot.\n this.errorResumeTime = resumeAt\n // Resume from the position the error interrupted (mid-video errors).\n // Token-guarded: if the user switches videos before metadata arrives, the\n // stale listener must not seek the NEW source to the old position.\n if (resumeAt > 0) {\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token !== this.loadToken) return\n try { this.video.currentTime = resumeAt } catch { /* range not ready */ }\n }, { once: true, signal: this.abort.signal })\n }\n }\n\n private clearError(): void {\n this.errorBox.hidden = true\n this.container.classList.remove('imp-player--error')\n }\n\n /** True once the current source has started playing at least once. */\n get started(): boolean {\n return this.playedOnce\n }\n\n seek(time: number): void {\n if (Number.isFinite(this.video.duration)) {\n this.video.currentTime = clamp(time, 0, this.video.duration)\n return\n }\n // Metadata not in yet (scrubbing the bar before the first play, using the\n // declared `source.duration`): stash the target, applied on loadedmetadata.\n const declared = this.source?.duration\n if (typeof declared === 'number' && declared > 0) {\n this.pendingSeek = clamp(time, 0, declared)\n }\n }\n\n /** Relative seek, e.g. `skip(-10)`. */\n skip(delta: number): void {\n this.seek(this.video.currentTime + delta)\n }\n\n // === volume ===========================================================\n\n get volume(): number {\n return this.video.volume\n }\n\n get muted(): boolean {\n return this.video.muted\n }\n\n setVolume(volume: number): void {\n this.video.volume = clamp(volume, 0, 1)\n if (volume > 0) this.video.muted = false\n }\n\n setMuted(muted: boolean): void {\n this.video.muted = muted\n }\n\n toggleMute(): void {\n this.video.muted = !this.video.muted\n }\n\n // === persistence ======================================================\n\n /** Read persisted prefs (safe against private mode / SSR / bad JSON). */\n private readPersisted(): { volume?: number; muted?: boolean; autoAdvance?: boolean } | null {\n if (!this.persistKey) return null\n try {\n const raw = localStorage.getItem(this.persistKey)\n const data = raw ? JSON.parse(raw) : null\n return data && typeof data === 'object' ? data : null\n } catch {\n return null\n }\n }\n\n private persistTimer: ReturnType<typeof setTimeout> | null = null\n\n /** Debounced persistState — for high-frequency triggers (volume drag). */\n private schedulePersist(): void {\n if (this.persistTimer) clearTimeout(this.persistTimer)\n this.persistTimer = setTimeout(() => {\n this.persistTimer = null\n if (!this.destroyed) this.persistState()\n }, 300)\n }\n\n /** Write the enabled prefs to localStorage. No-op when persistence is off. */\n private persistState(): void {\n if (!this.persistKey) return\n try {\n const data: { volume?: number; muted?: boolean; autoAdvance?: boolean } = {}\n if (this.persistVolume) {\n data.volume = this.video.volume\n data.muted = this.video.muted\n }\n if (this.persistAutoAdvance) data.autoAdvance = this.autoAdvanceEnabled\n localStorage.setItem(this.persistKey, JSON.stringify(data))\n } catch {\n // private mode / quota / SSR — persistence is best-effort.\n }\n }\n\n // === rate / quality ===================================================\n\n get playbackRate(): number {\n return this.video.playbackRate\n }\n\n setPlaybackRate(rate: number): void {\n this.video.playbackRate = rate\n this.emitter.emit('ratechange', { rate })\n }\n\n get qualityLevels(): Level[] {\n if (this.sourceController?.kind === 'hls') return this.sourceController.levels\n const qualities = this.source?.qualities\n return qualities\n ? qualities.map((q, index) => ({ index, label: q.label ?? String(q.quality ?? index) }))\n : []\n }\n\n get qualityAutoAvailable(): boolean {\n return this.sourceController?.kind === 'hls'\n }\n\n get currentQuality(): number {\n if (this.sourceController?.kind === 'hls') return this.sourceController.selected\n return this.progressiveQuality\n }\n\n setQuality(index: number): void {\n if (this.sourceController?.kind === 'hls') {\n this.sourceController.setLevel(index)\n const label = index === -1\n ? this.labels.qualityAuto\n : this.sourceController.levels.find((l) => l.index === index)?.label ?? String(index)\n this.emitter.emit('qualitychange', { label })\n return\n }\n const qualities = this.source?.qualities\n if (!qualities || !qualities[index] || index === this.progressiveQuality) return\n const time = this.video.currentTime\n const wasPlaying = !this.video.paused && !this.video.ended\n this.progressiveQuality = index\n this.video.src = qualities[index].src\n // Restore the position only once metadata is in — a seek on readyState 0\n // is silently dropped by some engines (the switch would reset to 0:00).\n const token = this.loadToken\n this.video.addEventListener('loadedmetadata', () => {\n if (token !== this.loadToken) return\n try { this.video.currentTime = time } catch { /* range not ready */ }\n if (wasPlaying) void this.video.play().catch(logPlayRejection)\n }, { once: true, signal: this.abort.signal })\n this.emitter.emit('qualitychange', { label: qualities[index].label ?? String(qualities[index].quality ?? index) })\n }\n\n // === subtitles ========================================================\n\n get subtitleTracks(): SubtitleTrack[] {\n return normalizeSubtitles(this.source?.subtitles)\n }\n\n get activeSubtitle(): number {\n const tracks = this.video.textTracks\n for (let i = 0; i < tracks.length; i++) {\n if (tracks[i].mode === 'showing') return i\n }\n return -1\n }\n\n setSubtitle(index: number): void {\n const tracks = this.video.textTracks\n for (let i = 0; i < tracks.length; i++) {\n tracks[i].mode = i === index ? 'showing' : 'disabled'\n }\n this.emitter.emit('subtitlechange', { track: this.subtitleTracks[index] ?? null })\n }\n\n // === playlist =========================================================\n\n get playlist(): VideoSource[] {\n return this.sources\n }\n\n get index(): number {\n return this.currentIndex\n }\n\n get source(): VideoSource | null {\n return this.sources[this.currentIndex] ?? null\n }\n\n /**\n * Mark a source as broken (e.g. its poster came back 4xx/5xx while building\n * the next-up preview — the video itself won't load either). Every \"next\"\n * computation (hover preview, end overlay, `next()`, auto-advance) skips\n * dead sources from then on. The playlist panel still lists them.\n */\n markSourceDead(source: VideoSource): void {\n this.deadSources.add(source)\n this.controls.syncFeatureButtons()\n }\n\n /** Linear index of the next non-dead item; -1 when nothing playable is ahead. */\n private nextAliveIndex(): number {\n const n = this.sources.length\n if (n < 2) return -1\n for (let step = 1; step < n; step++) {\n const raw = this.currentIndex + step\n if (raw >= n && !this.playlistOptions.loop) return -1\n const idx = raw % n\n if (!this.deadSources.has(this.sources[idx])) return idx\n }\n return -1\n }\n\n /**\n * The item that `next()` would play in linear order (for the next-up preview).\n * `null` in shuffle mode (the pick is random) or when there is no next item.\n * Skips sources marked dead via `markSourceDead`.\n */\n get nextSource(): VideoSource | null {\n if (this.shuffleMode || this.sources.length < 2) return null\n const i = this.nextAliveIndex()\n return i === -1 ? null : this.sources[i]\n }\n\n get hasPlaylist(): boolean {\n // External mode: the app owns the \"playlist\" — render the nav UI even for\n // a single source (next intents are emitted, not executed).\n return this.sources.length > 1 || this.externalAdvance\n }\n\n get hasNext(): boolean {\n if (this.externalAdvance) return true\n if (this.shuffleMode) return this.sources.length > 1\n return this.nextAliveIndex() !== -1\n }\n\n get hasPrevious(): boolean {\n if (this.externalAdvance) return true\n return this.playlistOptions.loop ? this.sources.length > 1 : this.currentIndex > 0\n }\n\n /** Shuffle mode: `next()` and auto-advance pick a random item. */\n get shuffle(): boolean {\n return this.shuffleMode\n }\n\n setShuffle(on: boolean): void {\n this.shuffleMode = on\n }\n\n /** Repeat mode: after the last item the list starts over instead of ending. */\n get repeat(): boolean {\n return this.playlistOptions.loop\n }\n\n setRepeat(on: boolean): void {\n this.playlistOptions.loop = on\n }\n\n /** External mode: emit the intent instead of switching. Returns true when handled. */\n private emitAdvanceRequest(direction: 'next' | 'previous' | 'select' | 'ended', index: number | null): boolean {\n if (!this.externalAdvance) return false\n const known = index !== null && index >= 0 && index < this.sources.length\n this.emitter.emit('advancerequest', {\n direction,\n index: known ? index : null,\n source: known ? this.sources[index] : null,\n })\n return true\n }\n\n next(): void {\n if (!this.hasNext) return\n if (this.emitAdvanceRequest('next', this.sources.length > 1 ? (this.currentIndex + 1) % this.sources.length : null)) return\n if (this.shuffleMode && this.sources.length > 1) {\n // Random pick among non-dead candidates (falls back to any other item\n // when everything else is marked dead — better than a dead end).\n const alive = this.sources\n .map((s, i) => ({ s, i }))\n .filter(({ s, i }) => i !== this.currentIndex && !this.deadSources.has(s))\n const pool = alive.length > 0 ? alive.map(({ i }) => i) : this.sources.map((_, i) => i).filter((i) => i !== this.currentIndex)\n this.playItem(pool[Math.floor(Math.random() * pool.length)])\n return\n }\n const i = this.nextAliveIndex()\n if (i !== -1) this.playItem(i)\n }\n\n previous(): void {\n if (!this.hasPrevious) return\n if (this.emitAdvanceRequest('previous', this.sources.length > 1 ? (this.currentIndex - 1 + this.sources.length) % this.sources.length : null)) return\n this.playItem((this.currentIndex - 1 + this.sources.length) % this.sources.length)\n }\n\n /** Jump to a playlist item and start playback (external mode: emits the intent instead). */\n playItem(index: number): void {\n if (index < 0 || index >= this.sources.length) return\n if (index !== this.currentIndex && this.emitAdvanceRequest('select', index)) return\n void this.loadItem(index, true).then(() => {\n this.emitter.emit('playlistitemchange', { source: this.sources[index], index })\n })\n }\n\n togglePlaylistPanel(): void {\n this.scenesPanel.hide()\n this.playlistPanel.toggle()\n }\n\n toggleScenesPanel(): void {\n this.playlistPanel.hide()\n this.scenesPanel.toggle()\n }\n\n /** Thumbnail track of the current source (used by the seek tooltip and the scenes panel). */\n get thumbnails(): ThumbnailTrack | null {\n return this.thumbTrack\n }\n\n /**\n * Replace what the player is playing. A single source resets the playlist\n * to one item; an array installs a new playlist.\n */\n load(source: VideoSource | VideoSource[], startIndex = 0): void {\n this.sources = Array.isArray(source) ? [...source] : [source]\n this.playlistPanel.rebuild()\n void this.loadItem(clamp(startIndex, 0, this.sources.length - 1), false)\n }\n\n // === fullscreen / pip =================================================\n\n get isFullscreen(): boolean {\n return document.fullscreenElement === this.container\n }\n\n async toggleFullscreen(): Promise<void> {\n try {\n if (this.isFullscreen) {\n await document.exitFullscreen()\n } else if (this.container.requestFullscreen) {\n await this.container.requestFullscreen()\n } else {\n // iOS Safari: only the video element can go fullscreen.\n const v = this.video as HTMLVideoElement & { webkitEnterFullscreen?: () => void }\n v.webkitEnterFullscreen?.()\n }\n } catch {\n // Rejected by policy (no gesture) or unsupported — not an app error.\n }\n }\n\n async togglePip(): Promise<void> {\n try {\n if (document.pictureInPictureElement === this.video) {\n await document.exitPictureInPicture()\n } else {\n await this.video.requestPictureInPicture()\n }\n } catch {\n // PiP rejected (policy/unsupported/disabled) — ignore.\n }\n }\n\n // === chapters =========================================================\n\n /** Normalized chapters of the current source (after metadata is known). */\n get chapterList(): NormalizedChapter[] {\n return this.chapters\n }\n\n get chapter(): NormalizedChapter | null {\n return this.currentChapter\n }\n\n // === scene types (groups) =============================================\n\n /** Typed scene breakdowns attached to the current source (may be empty). */\n get sceneTypes(): SceneGroup[] {\n return this.sceneGroupList\n }\n\n /** Currently selected scene type, or null when there are no groups. */\n get activeSceneType(): SceneGroup | null {\n return this.sceneGroupList.find((g) => g.id === this.activeSceneGroupId) ?? null\n }\n\n /** Switch the active scene type — rebuilds the timeline segments + scenes panel. */\n setSceneGroup(id: string): void {\n if (id === this.activeSceneGroupId || !this.sceneGroupList.some((g) => g.id === id)) return\n this.activeSceneGroupId = id\n this.applyActiveSceneGroup()\n this.emitter.emit('scenetypechange', { group: this.activeSceneType })\n }\n\n /**\n * Best duration currently known: the media's real one once metadata is in,\n * else the declared `source.duration` (lets scene groups and the seek bar\n * render before the first play), else NaN.\n */\n private get knownDuration(): number {\n const d = this.video.duration\n if (Number.isFinite(d) && d > 0) return d\n const declared = this.source?.duration\n return typeof declared === 'number' && declared > 0 ? declared : NaN\n }\n\n /** Normalize the active group's scenes into `chapters` and refresh the UI. */\n private applyActiveSceneGroup(): void {\n this.chapters = normalizeChapters(this.activeSceneType?.scenes ?? [], this.knownDuration)\n this.currentChapter = null\n this.controls.progress.setChapters(this.chapters)\n this.controls.syncFeatureButtons()\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n }\n\n // === actions ==========================================================\n\n /**\n * Share the current video: native share sheet when the device has one,\n * otherwise the `action` event with `id: \"share\"` so the app can show\n * its own dialog.\n */\n async share(): Promise<void> {\n const data = {\n title: this.source?.title ?? document.title,\n url: window.location.href,\n }\n if (typeof navigator.share === 'function') {\n try {\n await navigator.share(data)\n } catch {\n /* user dismissed the sheet — not an error */\n }\n return\n }\n this.emitter.emit('action', { id: 'share' })\n }\n\n /** Highlight the like or dislike button (`null` clears both). */\n setLikeState(state: 'like' | 'dislike' | null): void {\n this.controls.setLikeState(state)\n }\n\n /** Update like/dislike counters shown as tooltips above the buttons (`undefined` leaves one untouched). */\n setLikeCounts(likeCount?: number | string, dislikeCount?: number | string): void {\n this.controls.setLikeCounts(likeCount, dislikeCount)\n }\n\n // === pause screen =====================================================\n\n /** Inject custom pause-screen content (used by the Vue wrapper's slot). */\n setPauseScreenContent(element: HTMLElement | null): void {\n this.pauseScreen.setCustomContent(element)\n }\n\n // === teardown =========================================================\n\n destroy(): void {\n if (this.destroyed) return\n this.destroyed = true\n this.loadToken++\n this.adManager?.destroy()\n this.controls.destroy()\n this.sourceController?.destroy()\n this.abort.abort()\n if (this.idleTimer) clearTimeout(this.idleTimer)\n if (this.peekTimer) clearTimeout(this.peekTimer)\n if (this.retryTimer) clearTimeout(this.retryTimer)\n if (this.persistTimer) {\n // Flush a pending debounced write so the last volume isn't lost.\n clearTimeout(this.persistTimer)\n this.persistTimer = null\n this.persistState()\n }\n this.container.remove()\n releasePlayValve()\n this.emitter.emit('destroy', undefined)\n this.emitter.removeAll()\n }\n\n // === internals ========================================================\n\n private async loadItem(index: number, autoplay: boolean, skipAds = false): Promise<void> {\n const token = ++this.loadToken\n const source = this.sources[index]\n if (!source) return\n this.currentIndex = index\n\n // Tear down the previous source.\n this.sourceController?.destroy()\n this.sourceController = null\n this.chapters = []\n this.currentChapter = null\n this.sceneGroupList = []\n this.activeSceneGroupId = ''\n this.progressiveQuality = -1\n this.playedOnce = false\n this.lastPlayheadTime = 0\n this.errorResumeTime = 0\n this.pendingSeek = null\n this.endPeek()\n for (const track of [...this.video.querySelectorAll('track')]) track.remove()\n this.adManager?.resetForNewSource()\n this.related.hide()\n this.upNext.hide()\n this.pauseScreen.hide()\n this.scenesPanel.hide()\n this.thumbTrack = null\n // NB: don't clear the error overlay here — during a retry the reload must\n // keep it visible (spinner) until playback actually starts (canplay/playing\n // /seeked handler clears it), otherwise the controls flash for a frame.\n this.decodeRecoveries = 0\n\n // Per-source UI state.\n this.video.poster = posterSrc(source.poster) ?? ''\n this.poster.setSource(source)\n this.pauseScreen.setSource(source)\n this.controls.progress.setChapters([])\n this.controls.progress.setThumbnails(null)\n this.controls.progress.setHeatmap([])\n // Seed the seek bar with the declared duration so it is interactive BEFORE\n // the first play — a pre-start scrub stores a pending seek and starts\n // playback from that spot. The real duration replaces it on loadedmetadata.\n if (typeof source.duration === 'number' && source.duration > 0) {\n this.controls.progress.update(0, source.duration, 0)\n }\n if (autoplay) this.poster.hide()\n else this.poster.show()\n this.playlistPanel.rebuild()\n\n // Subtitle <track> elements (native rendering). Disabled by default —\n // a track only starts visible when it carries `default: true`.\n for (const sub of normalizeSubtitles(source.subtitles)) {\n const track = el('track', '', {\n kind: 'subtitles',\n src: sub.src,\n label: sub.label,\n ...(sub.srclang ? { srclang: sub.srclang } : {}),\n })\n if (sub.default) track.setAttribute('default', '')\n this.video.append(track)\n }\n\n // Effective media URL: progressive quality list wins over plain src.\n let src = source.src\n let type = source.type\n if (source.qualities && source.qualities.length > 0) {\n const initial = Math.max(0, source.qualities.findIndex((q) => q.src === source.src))\n this.progressiveQuality = initial\n src = source.qualities[initial].src\n type = source.qualities[initial].type ?? type\n }\n\n // While a pre-roll occupies the screen, let the browser buffer the first\n // fragments of the content in the background (hls.js does this on its\n // own; `auto` covers progressive sources).\n this.video.preload = this.adManager?.hasPendingPreRoll ? 'auto' : 'metadata'\n\n const controller = await attachSource(this.video, src, type, {\n onLevels: () => {\n if (token === this.loadToken) this.controls.syncFeatureButtons()\n },\n onLevelSwitch: (label) => this.emitter.emit('qualitychange', { label }),\n onError: (message, cause) => this.emitter.emit('error', { message, cause, fatal: true }),\n })\n if (token !== this.loadToken) {\n controller.destroy()\n return\n }\n this.sourceController = controller\n\n this.emitter.emit('sourcechange', { source, index })\n\n // Async extras — each guarded by the load token.\n if (source.thumbnails) {\n ThumbnailTrack.load(source.thumbnails)\n .then((track) => {\n if (token !== this.loadToken) return\n this.thumbTrack = track\n this.controls.progress.setThumbnails(track)\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n })\n .catch((cause) => this.emitter.emit('error', { message: 'Failed to load thumbnails track', cause }))\n }\n if (source.sceneGroups && source.sceneGroups.length > 0) {\n // Typed scene breakdowns: pick the first group, build the timeline from it.\n this.sceneGroupList = source.sceneGroups\n this.activeSceneGroupId = source.sceneGroups[0].id\n // Apply right away — the type-selector button and the segments must not\n // wait for metadata (with a not-yet-started HLS stream it never arrives\n // until play, and the button looked missing). Explicit scene ends and/or\n // the declared `source.duration` are enough to render; once the real\n // duration lands, re-apply to clamp against it.\n this.applyActiveSceneGroup()\n if (!(Number.isFinite(this.video.duration) && this.video.duration > 0)) {\n this.video.addEventListener('loadedmetadata', () => {\n if (token === this.loadToken) this.applyActiveSceneGroup()\n }, { once: true, signal: this.abort.signal })\n }\n } else if (source.chapters) {\n const apply = (raw: Parameters<typeof normalizeChapters>[0]) => {\n const set = () => {\n if (token !== this.loadToken) return\n this.chapters = normalizeChapters(raw, this.knownDuration)\n this.controls.progress.setChapters(this.chapters)\n this.controls.syncFeatureButtons()\n if (this.scenesPanel.visible) this.scenesPanel.rebuild()\n }\n if (Number.isFinite(this.video.duration) && this.video.duration > 0) set()\n else this.video.addEventListener('loadedmetadata', set, { once: true, signal: this.abort.signal })\n }\n if (typeof source.chapters === 'string') {\n loadChaptersVtt(source.chapters)\n .then((parsed) => apply(parsed))\n .catch((cause) => this.emitter.emit('error', { message: 'Failed to load chapters track', cause }))\n } else {\n apply(source.chapters)\n }\n }\n\n const hm = source.heatmap\n const dense = hm && !Array.isArray(hm)\n const hasHeatmap = dense ? hm.values.length > 0 : Boolean(hm && hm.length > 0)\n if (hm && hasHeatmap && this.controlsOptions.heatmap) {\n const applyHeatmap = () => {\n if (token !== this.loadToken) return\n const values = dense\n ? buildHeatmapValuesDense(hm.values, hm.mode)\n : buildHeatmapValues(hm, this.video.duration)\n this.controls.progress.setHeatmap(values, dense ? (hm.smoothing ?? 1) : 1)\n }\n // Dense data is self-contained (bucket = duration/length), so it needs no\n // duration; sparse points need duration to bucket by time.\n if (dense || (Number.isFinite(this.video.duration) && this.video.duration > 0)) applyHeatmap()\n else this.video.addEventListener('loadedmetadata', applyHeatmap, { once: true, signal: this.abort.signal })\n }\n\n // `play()` handles its own failures (autoplay block → poster, hls attach\n // race → retry), so a bare call is safe here.\n if (autoplay) void this.play(skipAds)\n }\n\n private bindVideoEvents(): void {\n const { signal } = this.abort\n const v = this.video\n\n v.addEventListener('play', () => {\n this.playedOnce = true\n this.endPeek()\n this.poster.hide()\n this.pauseScreen.hide()\n if (!this.related.visible) this.related.hide()\n this.related.hide()\n this.upNext.hide()\n // Playback requested but not enough buffered yet (e.g. switching to the\n // next playlist item on a slow connection): show the loader instead of a\n // frozen poster. 'playing' hides it once real frames arrive.\n if (v.readyState < 3 /* HAVE_FUTURE_DATA */) this.spinner.hidden = false\n this.emitter.emit('play', undefined)\n this.scheduleIdle()\n }, { signal })\n\n v.addEventListener('pause', () => {\n this.spinner.hidden = true\n this.emitter.emit('pause', undefined)\n this.showControlsNow()\n if (this.video.ended || this.scrubbing || this.adPlaying || !this.playedOnce) return\n if (this.options.pauseScreen !== false) this.pauseScreen.show()\n if (this.options.related?.showOn?.includes('pause')) this.related.show()\n }, { signal })\n\n v.addEventListener('ended', () => {\n void this.handleEnded()\n }, { signal })\n\n v.addEventListener('timeupdate', () => {\n if (!this.adPlaying && v.currentTime > 0) this.lastPlayheadTime = v.currentTime\n this.emitter.emit('timeupdate', { currentTime: v.currentTime, duration: v.duration || 0 })\n this.adManager?.checkMidRolls(v.currentTime)\n const chapter = chapterAt(this.chapters, v.currentTime)\n if (chapter !== this.currentChapter) {\n this.currentChapter = chapter\n this.emitter.emit('chapterchange', { chapter })\n }\n }, { signal })\n\n v.addEventListener('progress', () => {\n this.emitter.emit('progress', { buffered: this.bufferedEnd })\n }, { signal })\n\n v.addEventListener('volumechange', () => {\n this.emitter.emit('volumechange', { volume: v.volume, muted: v.muted })\n // Debounced: dragging the slider fires dozens of volumechange per second —\n // no need to hit localStorage on every step.\n if (this.persistVolume) this.schedulePersist()\n }, { signal })\n\n v.addEventListener('loadedmetadata', () => {\n // A pre-start scrub (seek bar seeded with the declared duration) lands here.\n if (this.pendingSeek !== null) {\n const t = clamp(this.pendingSeek, 0, Number.isFinite(v.duration) ? v.duration : this.pendingSeek)\n this.pendingSeek = null\n try { v.currentTime = t } catch { /* range not ready */ }\n }\n // Feed the real duration to the seek bar before the first timeupdate so\n // it is interactive pre-start even without a declared `source.duration`.\n if (!this.playedOnce && Number.isFinite(v.duration)) {\n this.controls.progress.update(v.currentTime, v.duration, this.bufferedEnd)\n }\n }, { signal })\n\n v.addEventListener('seeking', () => this.emitter.emit('seeking', { currentTime: v.currentTime }), { signal })\n v.addEventListener('seeked', () => this.emitter.emit('seeked', { currentTime: v.currentTime }), { signal })\n\n v.addEventListener('waiting', () => {\n this.spinner.hidden = false\n }, { signal })\n for (const eventName of ['playing', 'canplay', 'seeked'] as const) {\n v.addEventListener(eventName, () => {\n this.spinner.hidden = true\n this.decodeRecoveries = 0\n // Only *actual playback* dismisses the fatal-error overlay. A transient\n // `canplay`/`seeked` during a still-failing reload must not flash it away\n // (that caused the controls to blink before the error re-appeared).\n if (eventName === 'playing') this.clearError()\n }, { signal })\n }\n\n v.addEventListener('error', () => {\n const mediaError = v.error\n if (!mediaError) return\n // While hls.js drives the element (MSE), the native <video> can emit\n // transient decode errors (e.g. DEMUXER_ERROR_COULD_NOT_PARSE on a\n // viewport/visibility change) that hls.js recovers from on its own via\n // its ERROR event. Surfacing those as fatal here is wrong — let the HLS\n // controller own error handling and only react to native sources.\n if (this.sourceController?.kind === 'hls') return\n // No src attribute → the error came from our own teardown, ignore it.\n if (v.getAttribute('src') === null && !v.currentSrc) return\n\n // Native playback (incl. Safari/iOS HLS) can throw transient decode/parse\n // errors when seeking backward (DEMUXER_ERROR_COULD_NOT_PARSE). Try a\n // silent reload-and-restore a couple of times before surfacing an error.\n const decodeish = mediaError.code === MediaError.MEDIA_ERR_DECODE || /DEMUXER|PARSE|DECODE/i.test(mediaError.message || '')\n if (decodeish && this.decodeRecoveries < 2 && Number.isFinite(v.duration)) {\n this.decodeRecoveries++\n const at = v.currentTime\n const wasPlaying = !v.paused\n v.load()\n const restore = () => {\n try { v.currentTime = at } catch { /* range not ready yet */ }\n if (wasPlaying) void v.play().catch(logPlayRejection)\n }\n v.readyState >= 1 ? restore() : v.addEventListener('loadedmetadata', restore, { once: true, signal: this.abort.signal })\n return\n }\n\n this.emitter.emit('error', { message: mediaError.message || `Media error (code ${mediaError.code})`, cause: mediaError, fatal: true })\n }, { signal })\n\n v.addEventListener('enterpictureinpicture', () => this.emitter.emit('pipchange', { active: true }), { signal })\n v.addEventListener('leavepictureinpicture', () => this.emitter.emit('pipchange', { active: false }), { signal })\n\n document.addEventListener('fullscreenchange', () => {\n this.emitter.emit('fullscreenchange', { active: this.isFullscreen })\n this.container.classList.toggle('imp-player--fullscreen', this.isFullscreen)\n }, { signal })\n\n // iOS Safari video-element fullscreen (webkitEnterFullscreen) never touches\n // document.fullscreenElement — sync state/icon from the webkit events.\n v.addEventListener('webkitbeginfullscreen', () => {\n this.emitter.emit('fullscreenchange', { active: true })\n this.container.classList.add('imp-player--fullscreen')\n }, { signal })\n v.addEventListener('webkitendfullscreen', () => {\n this.emitter.emit('fullscreenchange', { active: false })\n this.container.classList.remove('imp-player--fullscreen')\n }, { signal })\n }\n\n private async handleEnded(): Promise<void> {\n this.emitter.emit('ended', undefined)\n this.showControlsNow()\n if (this.adManager) {\n await this.adManager.playPostRoll()\n if (this.destroyed) return\n }\n if (this.autoAdvanceEnabled && this.hasNext) {\n // External mode: the end-of-video advance is an intent too ('ended'\n // direction so the app can distinguish it from a next-button click).\n if (this.emitAdvanceRequest('ended', this.sources.length > 1 ? (this.currentIndex + 1) % this.sources.length : null)) return\n this.next()\n return\n }\n // Autoplay off: offer the next item with a \"play next\" card instead.\n if (!this.autoAdvanceEnabled && this.hasNext) {\n this.upNext.show()\n return\n }\n if (this.options.related?.showOn?.includes('ended') ?? Boolean(this.options.related)) {\n this.related.show()\n }\n }\n\n /** Autoplay-next state (gear switch). Off → show the up-next card on end. */\n get autoAdvance(): boolean {\n return this.autoAdvanceEnabled\n }\n\n setAutoAdvance(on: boolean): void {\n this.autoAdvanceEnabled = on\n if (on) this.upNext.hide()\n if (this.persistAutoAdvance) this.persistState()\n }\n\n // === controls visibility ==============================================\n\n private bindIdleHide(): void {\n const { signal } = this.abort\n const reset = () => {\n this.showControlsNow()\n this.scheduleIdle()\n }\n // Mouse/keyboard reveal-on-activity. Touch is deliberately excluded here:\n // a tap is handled by the pointerup tap-toggle on the video, so letting\n // pointerdown also fire reset() would make one tap both show *and* (via the\n // tap handler) hide the controls — they'd never actually toggle.\n this.container.addEventListener('pointermove', (e) => {\n if (e.pointerType === 'touch') return\n reset()\n // Before the first play the poster covers the bar — lift it on hover.\n if (!this.playedOnce) this.revealPeek(false)\n }, { signal })\n this.container.addEventListener('pointerdown', (e) => {\n if (e.pointerType !== 'touch') reset()\n }, { signal })\n this.container.addEventListener('keydown', reset, { signal })\n this.container.addEventListener('pointerleave', (e) => {\n if (e.pointerType === 'touch') return\n // Keep the controls (and their open dropdown) up while a menu is open.\n if (this.controls.anyMenuOpen()) return\n if (!this.playedOnce) this.endPeek()\n else if (!this.video.paused) this.container.classList.add('imp-player--idle')\n }, { signal })\n }\n\n private showControlsNow(): void {\n this.container.classList.remove('imp-player--idle')\n }\n\n /**\n * Before the first play, lift the control bar above the poster (\"peek\").\n * `autoHide` (touch) hides it after 3s of no interaction; on mouse the caller\n * clears it on pointer-leave instead. No-op once playback has started or when\n * the behavior is disabled via `controls.revealBeforePlay: false`.\n */\n private revealPeek(autoHide: boolean): void {\n if (this.playedOnce || !this.controlsOptions.revealBeforePlay) return\n this.container.classList.add('imp-player--peek')\n if (this.peekTimer) clearTimeout(this.peekTimer)\n this.peekTimer = null\n if (autoHide) {\n this.peekTimer = setTimeout(() => {\n // Don't auto-hide out from under an open dropdown.\n if (!this.playedOnce && !this.destroyed && !this.controls.anyMenuOpen()) {\n this.container.classList.remove('imp-player--peek')\n }\n }, 3000)\n }\n }\n\n private endPeek(): void {\n if (this.peekTimer) clearTimeout(this.peekTimer)\n this.peekTimer = null\n this.container.classList.remove('imp-player--peek')\n }\n\n /**\n * Poster background tapped on touch: reveal the control bar instead of\n * starting playback. Returns true when handled (so the poster skips `play()`).\n */\n posterTapReveal(): boolean {\n if (this.playedOnce || !this.controlsOptions.revealBeforePlay) return false\n this.revealPeek(true)\n return true\n }\n\n private scheduleIdle(): void {\n if (this.idleTimer) clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => {\n if (!this.video.paused && !this.destroyed) {\n this.container.classList.add('imp-player--idle')\n }\n }, this.controlsOptions.hideDelay)\n }\n\n // === keyboard =========================================================\n\n private bindKeyboard(): void {\n this.container.addEventListener('keydown', (e) => {\n const target = e.target as HTMLElement\n const interactive = target.closest('input, select, textarea, [contenteditable]')\n if (interactive) return\n const isButton = target.closest('button') !== null\n\n switch (e.key) {\n case ' ':\n case 'k':\n if (e.key === ' ' && isButton) return\n e.preventDefault()\n this.togglePlay()\n break\n case 'ArrowLeft':\n e.preventDefault()\n this.skip(-this.seekStep)\n break\n case 'ArrowRight':\n e.preventDefault()\n this.skip(this.seekStep)\n break\n case 'ArrowUp':\n e.preventDefault()\n this.setVolume(this.volume + 0.1)\n break\n case 'ArrowDown':\n e.preventDefault()\n this.setVolume(this.volume - 0.1)\n break\n case 'm':\n this.toggleMute()\n break\n case 'f':\n void this.toggleFullscreen()\n break\n case 'Home':\n this.seek(0)\n break\n case 'End':\n this.seek(this.duration)\n break\n default:\n if (/^[0-9]$/.test(e.key) && Number.isFinite(this.video.duration)) {\n this.seek((Number(e.key) / 10) * this.video.duration)\n }\n }\n }, { signal: this.abort.signal })\n }\n}\n"],"names":["el","tag","className","attrs","node","key","value","applyIcon","target","icon","coverImg","src","alt","img","iconButton","label","btn","previewMetaEl","items","base","row","item","i","text","chunk","ic","setIcon","clamp","min","max","formatTime","seconds","s","m","h","mm","ss","parseVttTime","raw","ms","parseVttCues","cues","blocks","block","lines","l","timingIndex","rawStart","rawEnd","start","end","cueText","resolveUrl","url","Emitter","event","fn","set","off","payload","err","svg","body","viewBox","defaultIcons","registry","defaultLabels","registerLocale","code","labels","registerLocales","map","getLocale","registeredLanguages","smooth","kernel","n","_","acc","k","j","idx","percentile","sorted","p","normalize","mode","v","sm","a","b","FLOOR","buildHeatmapValues","points","duration","buckets","hasData","point","buildHeatmapValuesDense","values","maxBuckets","out","counts","sum","heatmapPath","smoothing","width","height","step","at","c","y0","d","x0p","y0p","x1","y1","x2","y2","x3p","y3p","cp1x","cp1y","cp2x","cp2y","resolveVast","roll","opts","fetchAndParse","tagUrl","depth","controller","timer","xmlText","res","doc","ad","collectTracking","wrapper","nextTag","textOf","inline","linear","media","pickMediaFile","parseDuration","parseSkipOffset","_a","_b","playable","f","progressive","pool","pick","pct","firePixels","urls","AdManager","host","options","currentTime","due","kind","rolls","run","content","wasPlaying","cause","error","resolve","layer","video","ac","signal","spinner","hud","badge","countdown","actions","visitBtn","skipBtn","skipAfter","fired","fireOnce","lastProgress","lastTime","watchdog","cleanup","finish","skipped","t","remaining","onAdClick","adStarted","pauseEmitted","e","normalizeChapters","chapters","result","loadChaptersVtt","cue","chapterAt","time","chapter","isHlsSource","type","hlsModule","loadHls","globalHls","attachSource","cb","native","HlsCtor","attachHls","nativeController","hls","index","level","_e","data","mediaRecoveries","networkRecoveries","otherRecovered","ThumbnailTrack","vttUrl","rawSrc","fragment","entry","lo","hi","mid","CHEVRON_RIGHT","CHEVRON_LEFT","Menu","anchor","sections","section","title","entries","_title","chevron","toggle","sw","next","back","iconBox","ProgressBar","ariaLabel","list","filled","cursor","ch","root","fill","track","bufferedEnd","ratio","span","progress","rect","hasChapter","x","half","Controls","player","icons","left","right","spacer","seekCfg","seekBack","seekFwd","volumeWrap","ca","place","subsPlace","qualityPlace","speedPlace","wrap","listBtn","pipBtn","insertion","requested","keys","seen","modifier","dir","num","r","active","g","o","card","kicker","previewPoster","posterSrc","thumb","cr","pad","cardW","centered","_btn","initial","tip","likeCount","dislikeCount","state","custom","moreBtn","runMap","simpleItems","order","rank","overflowedSorted","it","rate","compact","centerLayout","useCenter","inCenter","ok","byPriority","guard","hasContent","rowRight","effective","except","menu","dispose","PosterOverlay","big","source","full","PauseScreen","heading","element","parts","sponsor","channel","RelatedOverlay","close","UpNextOverlay","upPoster","dur","play","PlaylistPanel","layout","playlistTitle","header","name","tools","thumbSrc","meta","itemTitle","ScenesPanel","thumbs","inner","current","playValveCount","onBenignPlayAbort","acquirePlayValve","releasePlayValve","logPlayRejection","normalizeSubtitles","input","defaultControls","Player","mount","rawControls","speed","saved","styling","savedAV","retryLabel","errorActions","middle","bottom","adConfig","fatal","ranges","skipAds","token","throttle","now","resumeAt","declared","delta","volume","muted","qualities","q","tracks","on","direction","known","alive","startIndex","id","autoplay","sub","message","apply","parsed","hm","dense","hasHeatmap","applyHeatmap","eventName","mediaError","restore","reset","autoHide","isButton"],"mappings":"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"}
|