diff --git a/packages/core/src/runtime/adapters/video.ts b/packages/core/src/runtime/adapters/video.ts new file mode 100644 index 000000000..a7d5051de --- /dev/null +++ b/packages/core/src/runtime/adapters/video.ts @@ -0,0 +1,49 @@ +import type { RuntimeDeterministicAdapter } from "../types"; + +export function createVideoAdapter(): RuntimeDeterministicAdapter { + let videos: HTMLVideoElement[] = []; + return { + name: "video", + discover(): void { + videos = Array.from( + document.querySelectorAll("video.clip, video[class*='clip']"), + ); + }, + pause(): void { + for (const el of videos) { + if (el.isConnected && !el.paused) { + try { + el.pause(); + } catch {} + } + } + }, + seek({ time }: { time: number }): void { + for (const el of videos) { + if (!el.isConnected) continue; + const hasStart = el.hasAttribute("data-start"); + const start = hasStart ? parseFloat(el.dataset.start ?? "") || 0 : 0; + const mediaStart = + parseFloat(el.dataset.playbackStart ?? el.dataset.mediaStart ?? "0") || 0; + const rawDuration = parseFloat(el.dataset.duration ?? ""); + const sourceDuration = isFinite(el.duration) && el.duration > 0 ? el.duration : null; + const duration = + isFinite(rawDuration) && rawDuration > 0 + ? rawDuration + : sourceDuration != null + ? Math.max(0, sourceDuration - mediaStart) + : Infinity; + const end = isFinite(duration) && duration > 0 ? start + duration : Infinity; + const relTime = time - start + mediaStart; + const isActive = time >= start && time < end && relTime >= 0; + if (!isActive) continue; + const drift = Math.abs((el.currentTime || 0) - relTime); + if (drift > 0.05) { + try { + el.currentTime = relTime; + } catch {} + } + } + }, + }; +} diff --git a/packages/core/src/runtime/init.ts b/packages/core/src/runtime/init.ts index 50be98dd7..c460d6933 100644 --- a/packages/core/src/runtime/init.ts +++ b/packages/core/src/runtime/init.ts @@ -5,6 +5,7 @@ import { createGsapAdapter } from "./adapters/gsap"; import { createLottieAdapter } from "./adapters/lottie"; import { createThreeAdapter } from "./adapters/three"; import { createWaapiAdapter } from "./adapters/waapi"; +import { createVideoAdapter } from "./adapters/video"; import { refreshRuntimeMediaCache, syncRuntimeMedia } from "./media"; import { createPickerModule } from "./picker"; import { createRuntimePlayer } from "./player"; @@ -1553,6 +1554,7 @@ export function initSandboxRuntimeModular(): void { createLottieAdapter(), createThreeAdapter(), createGsapAdapter({ getTimeline: () => state.capturedTimeline }), + createVideoAdapter(), ] as RuntimeDeterministicAdapter[]; installRuntimeErrorDiagnostics(); runAdapters("discover");