three-vr-player 0.9.0 → 0.10.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"three-vr-player.js","sources":["../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["import type { Projection } from '../types.js';\r\nimport { PROJECTIONS } from '../core/projections.js';\r\nimport { formatTime } from './format.js';\r\n\r\n/** What the ControlsUI needs from the Player to drive playback + view. */\r\nexport interface PlayerBridge {\r\n video: HTMLVideoElement;\r\n surface: HTMLElement; // canvas — for wheel-zoom\r\n fullscreenTarget: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n enterVR(): Promise<void>;\r\n exitVR(): void;\r\n isPresenting(): boolean;\r\n onVrChange(cb: (presenting: boolean) => void): void;\r\n getProjection(): Projection | 'off';\r\n setProjection(p: Projection | 'off'): void;\r\n setSwapEyes(v: boolean): void;\r\n setFov(deg: number): void;\r\n setSupersampling(x: number): void;\r\n initial: { swapEyes: boolean; fov: number; supersampling: number };\r\n proxy: { url: string; apiPassword: string; enabled: boolean };\r\n setProxy(p: { url: string; apiPassword: string; enabled: boolean }): void;\r\n}\r\n\r\ntype Props = Record<string, unknown> & { class?: string };\r\nfunction h<K extends keyof HTMLElementTagNameMap>(tag: K, props: Props = {}, children: (Node | string)[] = []): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tag);\r\n const { class: cls, ...rest } = props;\r\n if (cls) el.className = cls;\r\n Object.assign(el, rest);\r\n for (const c of children) el.append(c);\r\n return el;\r\n}\r\n\r\n/** Builds the controls bar + settings/menus inside `root` and wires them to `bridge`. */\r\nexport class ControlsUI {\r\n private readonly nodes: HTMLElement[] = [];\r\n private readonly disposers: (() => void)[] = [];\r\n\r\n constructor(root: ParentNode, bridge: PlayerBridge) {\r\n const v = bridge.video;\r\n\r\n // --- playback ---\r\n const play = h('button', { class: 'tvp-btn tvp-play', title: 'Play/Pause', textContent: '▶' });\r\n const seek = h('input', { class: 'tvp-seek', type: 'range', min: '0', max: '1000', value: '0' });\r\n const time = h('span', { class: 'tvp-time', textContent: '0:00 / 0:00' });\r\n\r\n // --- volume ---\r\n const mute = h('button', { class: 'tvp-btn tvp-mute', title: 'Volume', textContent: '🔊' });\r\n const volume = h('input', { class: 'tvp-volume', type: 'range', min: '0', max: '1', step: '0.01', value: '1' });\r\n const volPopup = h('div', { class: 'tvp-volpopup', hidden: true }, [volume]);\r\n const volWrap = h('span', { class: 'tvp-volwrap' }, [mute, volPopup]);\r\n\r\n // --- VR ---\r\n const vrBtn = h('button', { class: 'tvp-btn tvp-vrbtn', title: 'Enter VR', textContent: '🥽', hidden: true });\r\n\r\n // --- projection menu ---\r\n const projBtn = h('button', { class: 'tvp-btn tvp-projbtn', title: 'Projection', textContent: '🌐' });\r\n const projMenu = h('div', { class: 'tvp-projmenu', hidden: true });\r\n for (const p of PROJECTIONS) {\r\n const b = h('button', { textContent: p.label });\r\n b.dataset.mode = p.value;\r\n projMenu.append(b);\r\n }\r\n const offBtn = h('button', { textContent: 'Off (native player)' });\r\n offBtn.dataset.mode = 'off';\r\n projMenu.append(h('hr', { class: 'tvp-sep' }), offBtn);\r\n const projWrap = h('span', { class: 'tvp-projwrap' }, [projBtn, projMenu]);\r\n\r\n // --- settings ---\r\n const settingsBtn = h('button', { class: 'tvp-btn tvp-settingsbtn', title: 'Settings', textContent: '⚙' });\r\n const fullscreen = h('button', { class: 'tvp-btn tvp-fullscreen', title: 'Fullscreen', textContent: '⛶' });\r\n\r\n const controls = h('footer', { class: 'tvp-controls' }, [play, seek, time, volWrap, vrBtn, projWrap, settingsBtn, fullscreen]);\r\n\r\n const swapCb = h('input', { type: 'checkbox', checked: bridge.initial.swapEyes });\r\n const fovRange = h('input', { type: 'range', min: '30', max: '100', step: '1', value: String(bridge.initial.fov) });\r\n const fovVal = h('span', { textContent: String(bridge.initial.fov) });\r\n const ssRange = h('input', { type: 'range', min: '1', max: '2', step: '0.25', value: String(bridge.initial.supersampling) });\r\n const ssVal = h('span', { textContent: String(bridge.initial.supersampling) });\r\n const useProxyCb = h('input', { type: 'checkbox', checked: bridge.proxy.enabled });\r\n const proxyUrlIn = h('input', { type: 'text', value: bridge.proxy.url, placeholder: 'http://localhost:8888', spellcheck: false });\r\n const proxyPwIn = h('input', { type: 'password', value: bridge.proxy.apiPassword, placeholder: 'API password' });\r\n const settings = h('section', { class: 'tvp-settings' }, [\r\n h('label', { class: 'row' }, [swapCb, 'Swap eyes (if depth looks wrong)']),\r\n h('label', {}, [makeText('Field of view (zoom): ', fovVal, '°'), fovRange]),\r\n h('label', {}, [makeText('Supersampling: ', ssVal, '× (sharpness)'), ssRange]),\r\n h('hr', { class: 'tvp-sep' }),\r\n h('label', { class: 'row' }, [useProxyCb, 'Use CORS proxy']),\r\n h('label', {}, ['Proxy URL', proxyUrlIn]),\r\n h('label', {}, ['API password', proxyPwIn]),\r\n ]);\r\n\r\n const toast = h('div', { class: 'tvp-toast' });\r\n\r\n this.nodes.push(controls, settings, toast);\r\n for (const n of this.nodes) root.append(n);\r\n\r\n // ---- wiring ----\r\n const on = <T extends EventTarget>(t: T, ev: string, fn: (e: any) => void, opts?: AddEventListenerOptions) => {\r\n t.addEventListener(ev, fn as EventListener, opts);\r\n this.disposers.push(() => t.removeEventListener(ev, fn as EventListener, opts));\r\n };\r\n const setSeekFill = (pct: number) => seek.style.setProperty('--seek', `${pct}%`);\r\n // Only one popup (volume / projection / settings) open at a time.\r\n const closeMenus = () => { volPopup.hidden = true; projMenu.hidden = true; settings.classList.remove('open'); };\r\n let toastTimer = 0;\r\n const showToast = (msg: string) => {\r\n toast.textContent = msg;\r\n toast.classList.add('show');\r\n clearTimeout(toastTimer);\r\n toastTimer = window.setTimeout(() => toast.classList.remove('show'), 4500);\r\n };\r\n\r\n // Fire on a real pointer tap. The Meta Quest controller ray often fails to\r\n // synthesize `click` on <button> (tiny ray movement between press/release exceeds\r\n // the click threshold), while pointerup always fires. We trigger on pointerup and\r\n // keep `click` for keyboard/assistive tech, deduped so each activation fires once.\r\n // (pointerup carries transient activation too, so WebXR requestSession still works.)\r\n const onTap = (el: HTMLElement, fn: (e: Event) => void) => {\r\n let downOn = false;\r\n let firedAt = -1e9;\r\n const run = (e: Event) => { firedAt = e.timeStamp; fn(e); };\r\n on(el, 'pointerdown', () => { downOn = true; });\r\n on(el, 'pointercancel', () => { downOn = false; });\r\n on(el, 'pointerup', (e: Event) => { if (downOn) { downOn = false; run(e); } });\r\n on(el, 'click', (e: Event) => { if (e.timeStamp - firedAt > 700) run(e); });\r\n };\r\n\r\n onTap(play, () => { v.paused ? void v.play() : v.pause(); });\r\n on(v, 'play', () => { play.textContent = '⏸'; });\r\n on(v, 'pause', () => { play.textContent = '▶'; });\r\n\r\n on(v, 'timeupdate', () => {\r\n if (v.duration) {\r\n const pct = (v.currentTime / v.duration) * 100;\r\n seek.value = String(pct * 10);\r\n setSeekFill(pct);\r\n time.textContent = `${formatTime(v.currentTime)} / ${formatTime(v.duration)}`;\r\n }\r\n });\r\n on(seek, 'input', () => {\r\n setSeekFill(Number(seek.value) / 10);\r\n if (v.duration) v.currentTime = (Number(seek.value) / 1000) * v.duration;\r\n });\r\n\r\n onTap(mute, () => { const open = volPopup.hidden; closeMenus(); volPopup.hidden = !open; });\r\n on(volume, 'input', () => { v.volume = Number(volume.value); v.muted = v.volume === 0; mute.textContent = v.muted ? '🔇' : '🔊'; });\r\n\r\n const updateProjection = () => {\r\n const mode = bridge.getProjection();\r\n let label = 'Projection';\r\n projMenu.querySelectorAll('button').forEach((b) => {\r\n const on2 = b.dataset.mode === mode;\r\n b.classList.toggle('active', on2);\r\n if (on2) label = `Projection: ${b.textContent}`;\r\n });\r\n projBtn.title = label;\r\n };\r\n onTap(projBtn, () => { const open = projMenu.hidden; closeMenus(); projMenu.hidden = !open; });\r\n onTap(projMenu, (e: Event) => {\r\n const b = (e.target as HTMLElement).closest('button[data-mode]') as HTMLElement | null;\r\n if (b) { bridge.setProjection(b.dataset.mode as Projection | 'off'); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n onTap(settingsBtn, () => { const open = !settings.classList.contains('open'); closeMenus(); settings.classList.toggle('open', open); });\r\n on(swapCb, 'change', () => bridge.setSwapEyes(swapCb.checked));\r\n on(fovRange, 'input', () => { const d = Number(fovRange.value); fovVal.textContent = String(d); bridge.setFov(d); });\r\n on(ssRange, 'input', () => { const x = Number(ssRange.value); ssVal.textContent = String(x); bridge.setSupersampling(x); });\r\n const applyProxy = () => bridge.setProxy({ url: proxyUrlIn.value.trim(), apiPassword: proxyPwIn.value, enabled: useProxyCb.checked });\r\n on(useProxyCb, 'change', applyProxy);\r\n on(proxyUrlIn, 'change', applyProxy);\r\n on(proxyPwIn, 'change', applyProxy);\r\n on(bridge.surface, 'wheel', (e: WheelEvent) => {\r\n e.preventDefault();\r\n const next = Math.max(30, Math.min(100, Number(fovRange.value) + Math.sign(e.deltaY) * 3));\r\n fovRange.value = String(next); fovVal.textContent = String(next); bridge.setFov(next);\r\n }, { passive: false });\r\n\r\n onTap(fullscreen, () => {\r\n const t = bridge.fullscreenTarget;\r\n if (!document.fullscreenElement) void t.requestFullscreen?.();\r\n else void document.exitFullscreen?.();\r\n });\r\n\r\n // outside-tap closes any open popup (pointerdown fires reliably on Quest; click may not)\r\n on(document, 'pointerdown', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n const inside = (el: Node) => path.includes(el);\r\n if (![volPopup, mute, projMenu, projBtn, settings, settingsBtn].some(inside)) closeMenus();\r\n });\r\n\r\n // Auto-hide the control bar after 5s of inactivity; show on movement over the player.\r\n let hideTimer = 0;\r\n const popupOpen = () => !volPopup.hidden || !projMenu.hidden || settings.classList.contains('open');\r\n const hideBar = () => { if (!popupOpen()) controls.classList.add('tvp-hidden'); };\r\n const showBar = () => { controls.classList.remove('tvp-hidden'); clearTimeout(hideTimer); hideTimer = window.setTimeout(hideBar, 5000); };\r\n on(bridge.fullscreenTarget, 'pointermove', showBar);\r\n on(bridge.fullscreenTarget, 'pointerdown', showBar);\r\n on(bridge.fullscreenTarget, 'pointerleave', () => { clearTimeout(hideTimer); hideBar(); });\r\n this.disposers.push(() => clearTimeout(hideTimer));\r\n showBar();\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => { vrBtn.hidden = !ok; });\r\n onTap(vrBtn, () => {\r\n if (bridge.isPresenting()) { bridge.exitVR(); return; }\r\n // requestSession is the first thing off the tap so it keeps the user gesture;\r\n // any rejection is surfaced instead of silently swallowed.\r\n bridge.enterVR().catch((err: unknown) => showToast(err instanceof Error ? err.message : 'Could not enter VR.'));\r\n });\r\n bridge.onVrChange((presenting) => { vrBtn.classList.toggle('active', presenting); vrBtn.title = presenting ? 'Exit VR' : 'Enter VR'; });\r\n }\r\n\r\n dispose() {\r\n for (const d of this.disposers) d();\r\n for (const n of this.nodes) n.remove();\r\n }\r\n}\r\n\r\nfunction makeText(prefix: string, valueSpan: HTMLElement, suffix: string): DocumentFragment {\r\n const frag = document.createDocumentFragment();\r\n frag.append(prefix, valueSpan, suffix);\r\n return frag;\r\n}\r\n","import { StereoScene } from './core/StereoScene.js';\r\nimport { LookControls } from './core/LookControls.js';\r\nimport { VideoSource } from './core/VideoSource.js';\r\nimport { buildProxyUrl, type ProxyConfig } from './core/proxy.js';\r\nimport { detectProjection } from './core/projections.js';\r\nimport { ControlsUI } from './ui/ControlsUI.js';\r\nimport css from './ui/controls.css?inline';\r\nimport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\n\r\ntype ViewSettings = { projection: Projection; swapEyes: boolean; fov: number; supersampling: number };\r\nconst STORE_KEY = 'three-vr-player:settings';\r\n\r\n/**\r\n * A drop-in 3D/VR video player. Mounts a canvas + controls (into a Shadow DOM by\r\n * default) inside `container`, composing the headless core.\r\n */\r\nexport class Player {\r\n readonly video: HTMLVideoElement;\r\n\r\n private readonly opts: PlayerOptions;\r\n private readonly wrap: HTMLElement;\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly scene: StereoScene;\r\n private readonly look: LookControls;\r\n private readonly source = new VideoSource();\r\n private readonly ui?: ControlsUI;\r\n private readonly listeners = new Map<PlayerEvent, Set<(p?: unknown) => void>>();\r\n private view: ViewSettings;\r\n private readyEmitted = false;\r\n private native = false;\r\n private tainted = false; // current video loaded via CORS-fallback (crossOrigin removed)\r\n private displayMode: Projection | 'off';\r\n private loading = false;\r\n private proxyConfig?: ProxyConfig;\r\n private useProxy = false;\r\n private currentSrc?: string;\r\n\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\r\n this.proxyConfig = options.proxy;\r\n this.useProxy = !!options.proxy;\r\n const stored = options.persistSettings ? this.loadSettings() : null;\r\n this.view = {\r\n projection: options.projection ?? stored?.projection ?? '180-sbs',\r\n swapEyes: options.swapEyes ?? stored?.swapEyes ?? false,\r\n fov: options.fov ?? stored?.fov ?? 70,\r\n supersampling: options.supersampling ?? stored?.supersampling ?? 1.5,\r\n };\r\n this.displayMode = this.view.projection;\r\n\r\n this.wrap = document.createElement('div');\r\n this.wrap.className = 'tvp';\r\n this.wrap.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden;';\r\n container.appendChild(this.wrap);\r\n\r\n let root: ParentNode;\r\n if (options.shadowDom !== false) {\r\n const shadow = this.wrap.attachShadow({ mode: 'open' });\r\n const style = document.createElement('style'); style.textContent = css; shadow.appendChild(style);\r\n root = shadow;\r\n } else {\r\n if (!document.getElementById('tvp-styles')) {\r\n const style = document.createElement('style'); style.id = 'tvp-styles'; style.textContent = css; document.head.appendChild(style);\r\n }\r\n root = this.wrap;\r\n }\r\n\r\n this.canvas = document.createElement('canvas'); this.canvas.className = 'tvp-canvas';\r\n this.video = document.createElement('video'); this.video.className = 'tvp-video'; this.video.playsInline = true;\r\n if (options.crossOrigin !== null) this.video.crossOrigin = options.crossOrigin ?? 'anonymous';\r\n root.append(this.canvas, this.video);\r\n\r\n this.scene = new StereoScene({\r\n canvas: this.canvas, video: this.video,\r\n projection: this.view.projection, swapEyes: this.view.swapEyes,\r\n fov: this.view.fov, supersampling: this.view.supersampling,\r\n });\r\n if (options.title) this.scene.setVRTitle(options.title);\r\n this.look = new LookControls(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting });\r\n this.scene.onFrame(() => this.look.update());\r\n this.look.setEnabled(!this.scene.isFlat());\r\n\r\n if (options.controls !== false) {\r\n this.ui = new ControlsUI(root, {\r\n video: this.video,\r\n surface: this.canvas,\r\n fullscreenTarget: this.wrap,\r\n vrSupported: () => this.vrSupported(),\r\n enterVR: () => this.enterVR(),\r\n exitVR: () => this.scene.exitVR(),\r\n isPresenting: () => this.scene.renderer.xr.isPresenting,\r\n onVrChange: (cb) => { this.on('enterxr', () => cb(true)); this.on('exitxr', () => cb(false)); },\r\n getProjection: () => this.displayMode,\r\n setProjection: (p) => this.setProjection(p),\r\n setSwapEyes: (v) => this.setSwapEyes(v),\r\n setFov: (d) => this.setFov(d),\r\n setSupersampling: (x) => this.setSupersampling(x),\r\n initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },\r\n proxy: { url: this.proxyConfig?.url ?? '', apiPassword: this.proxyConfig?.apiPassword ?? '', enabled: this.useProxy },\r\n setProxy: (p) => this.setProxy(p),\r\n });\r\n }\r\n\r\n this.video.addEventListener('play', () => this.emit('play'));\r\n this.video.addEventListener('pause', () => this.emit('pause'));\r\n this.video.addEventListener('ended', () => this.emit('ended'));\r\n this.video.addEventListener('timeupdate', () => this.emit('timeupdate', this.video.currentTime));\r\n this.video.addEventListener('error', () => { if (!this.loading) this.emit('error', this.video.error); });\r\n this.video.addEventListener('loadedmetadata', () => { if (!this.readyEmitted) { this.readyEmitted = true; this.emit('ready'); } });\r\n this.scene.renderer.xr.addEventListener('sessionstart', () => this.emit('enterxr'));\r\n this.scene.renderer.xr.addEventListener('sessionend', () => this.emit('exitxr'));\r\n // Arm the headset/browser's own \"Enter VR\" affordance when immersive VR is available.\r\n void this.vrSupported().then((ok) => { if (ok) this.scene.offerVR(); });\r\n\r\n if (options.src) void this.load(options.src, { projection: options.projection });\r\n }\r\n\r\n private vrSupported(): Promise<boolean> {\r\n if (this.opts.vrButton === false || !navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-vr').catch(() => false);\r\n }\r\n\r\n /** Whether immersive-ar (passthrough) is available — used for alpha/fisheye content. */\r\n arSupported(): Promise<boolean> {\r\n if (!navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-ar').catch(() => false);\r\n }\r\n\r\n async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n this.currentSrc = src;\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.applyProjectionGeometry(proj);\r\n // Packed alpha matte (fisheye passthrough): explicit option, else DeoVR's `_ALPHA` filename marker.\r\n this.scene.setAlphaMatte(this.opts.alpha ?? /_alpha/i.test(src));\r\n if (this.displayMode !== 'off') this.displayMode = this.view.projection;\r\n const { url, format } = buildProxyUrl(src, this.useProxy ? this.proxyConfig : undefined);\r\n const primaryCO = this.opts.crossOrigin === undefined ? 'anonymous' : this.opts.crossOrigin;\r\n this.loading = true;\r\n try {\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: primaryCO });\r\n this.tainted = false;\r\n this.applyDisplay();\r\n await this.video.play().catch(() => { /* autoplay may be blocked until a user gesture */ });\r\n } catch (err) {\r\n // A cross-origin progressive source that isn't CORS-clean (and isn't proxied) can't be a\r\n // WebGL texture. Retry without crossOrigin and show plain 2D <video> playback if allowed.\r\n if (this.opts.nativeFallback !== false && format === 'progressive' && primaryCO !== null) {\r\n try {\r\n this.tainted = true;\r\n this.setNativeFallback(true); // stop WebGL rendering before the tainted video loads\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: null });\r\n await this.video.play().catch(() => {});\r\n this.emit('fallback');\r\n return;\r\n } catch (err2) {\r\n this.tainted = false;\r\n this.setNativeFallback(false);\r\n this.emit('error', err2);\r\n throw err2;\r\n }\r\n }\r\n this.setNativeFallback(false);\r\n this.emit('error', err);\r\n throw err;\r\n } finally {\r\n this.loading = false;\r\n }\r\n }\r\n\r\n /** Swap between WebGL (3D) rendering and plain 2D `<video>` playback. */\r\n private setNativeFallback(on: boolean) {\r\n if (on === this.native) return;\r\n this.native = on;\r\n if (on) {\r\n this.scene.pauseRendering();\r\n this.canvas.style.display = 'none';\r\n Object.assign(this.video.style, {\r\n display: 'block', position: 'absolute', inset: '0',\r\n width: '100%', height: '100%', objectFit: 'contain', background: '#000', zIndex: '1',\r\n });\r\n } else {\r\n this.canvas.style.display = '';\r\n this.video.style.cssText = '';\r\n this.scene.resumeRendering();\r\n }\r\n }\r\n\r\n async play() { await this.video.play(); }\r\n pause() { this.video.pause(); }\r\n /** Set the display mode: a geometry projection (WebGL 3D) or 'off' for plain 2D `<video>`. */\r\n setProjection(p: Projection | 'off') {\r\n if (p === 'off') {\r\n this.displayMode = 'off';\r\n this.setNativeFallback(true);\r\n this.emit('projectionchange', p);\r\n return;\r\n }\r\n this.displayMode = p;\r\n this.applyProjectionGeometry(p);\r\n // A CORS-tainted (native-fallback) video can't be a WebGL texture — reload to re-attempt 3D.\r\n if (this.tainted && this.currentSrc) void this.load(this.currentSrc);\r\n else this.setNativeFallback(false);\r\n this.emit('projectionchange', p);\r\n }\r\n private applyProjectionGeometry(p: Projection) {\r\n this.scene.setProjection(p);\r\n this.look.setEnabled(!this.scene.isFlat());\r\n this.look.reset();\r\n this.view.projection = p; this.persist();\r\n }\r\n private applyDisplay() { this.setNativeFallback(this.displayMode === 'off'); }\r\n setSwapEyes(v: boolean) { this.scene.setSwapEyes(v); this.view.swapEyes = v; this.persist(); }\r\n setFov(deg: number) { this.scene.setFov(deg); this.view.fov = deg; this.persist(); }\r\n setSupersampling(x: number) { this.scene.setSupersampling(x); this.view.supersampling = x; this.persist(); }\r\n /** Update the CORS proxy config / toggle it, and reload the current source if any. */\r\n setProxy(p: { url: string; apiPassword?: string; enabled: boolean }) {\r\n this.proxyConfig = p.url ? { url: p.url, apiPassword: p.apiPassword || undefined } : undefined;\r\n this.useProxy = p.enabled;\r\n if (this.currentSrc) void this.load(this.currentSrc);\r\n }\r\n\r\n /** Enter immersive VR. Rejects if the WebXR session request fails (e.g. no headset,\r\n * or a non-secure origin) — the built-in controls surface the reason as a toast. */\r\n async enterVR() { await this.scene.enterVR(); }\r\n /** Enter immersive AR (passthrough) — for alpha/fisheye content. */\r\n async enterAR() { await this.scene.enterAR(); }\r\n exitVR() { this.scene.exitVR(); }\r\n\r\n get three() { return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera }; }\r\n\r\n on(e: PlayerEvent, cb: (p?: unknown) => void) {\r\n let s = this.listeners.get(e); if (!s) { s = new Set(); this.listeners.set(e, s); } s.add(cb); return this;\r\n }\r\n off(e: PlayerEvent, cb: (p?: unknown) => void) { this.listeners.get(e)?.delete(cb); return this; }\r\n private emit(e: PlayerEvent, p?: unknown) { this.listeners.get(e)?.forEach((cb) => cb(p)); }\r\n\r\n private persist() {\r\n if (!this.opts.persistSettings) return;\r\n try { localStorage.setItem(STORE_KEY, JSON.stringify(this.view)); } catch { /* private mode */ }\r\n }\r\n private loadSettings(): Partial<ViewSettings> | null {\r\n try { const raw = localStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) as Partial<ViewSettings> : null; }\r\n catch { return null; }\r\n }\r\n\r\n dispose() {\r\n this.ui?.dispose(); this.look.dispose(); this.source.dispose(); this.scene.dispose();\r\n this.video.remove(); this.canvas.remove(); this.wrap.remove(); this.listeners.clear();\r\n }\r\n}\r\n","import { Player } from './Player.js';\r\nimport type { Projection } from './types.js';\r\n\r\n/**\r\n * `<three-video src=\"…\" projection=\"180-sbs\" controls>` — a framework-agnostic\r\n * custom element wrapping {@link Player}. Give it a size via CSS.\r\n */\r\nexport class ThreeVideoElement extends HTMLElement {\r\n private player?: Player;\r\n\r\n static get observedAttributes() { return ['src', 'projection']; }\r\n\r\n connectedCallback() {\r\n if (this.player) return;\r\n if (!this.style.display) this.style.display = 'block';\r\n this.player = new Player(this, {\r\n src: this.getAttribute('src') ?? undefined,\r\n projection: (this.getAttribute('projection') as Projection) ?? undefined,\r\n controls: this.hasAttribute('controls'),\r\n swapEyes: this.hasAttribute('swap-eyes'),\r\n autoDetect: this.getAttribute('auto-detect') !== 'false',\r\n fov: this.numAttr('fov'),\r\n supersampling: this.numAttr('supersampling'),\r\n proxy: this.getAttribute('proxy-url')\r\n ? { url: this.getAttribute('proxy-url')!, apiPassword: this.getAttribute('proxy-password') ?? undefined }\r\n : undefined,\r\n });\r\n }\r\n\r\n attributeChangedCallback(name: string, _old: string | null, value: string | null) {\r\n if (!this.player || value == null) return;\r\n if (name === 'src') void this.player.load(value);\r\n if (name === 'projection') this.player.setProjection(value as Projection);\r\n }\r\n\r\n disconnectedCallback() { this.player?.dispose(); this.player = undefined; }\r\n\r\n private numAttr(n: string): number | undefined {\r\n const v = this.getAttribute(n);\r\n return v == null ? undefined : Number(v);\r\n }\r\n\r\n /** The underlying Player instance (for programmatic control). */\r\n get api(): Player | undefined { return this.player; }\r\n}\r\n\r\nexport function registerWebComponent(tag = 'three-video') {\r\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\r\n customElements.define(tag, ThreeVideoElement);\r\n }\r\n}\r\n","export { Player } from './Player.js';\r\nexport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\nexport { ThreeVideoElement, registerWebComponent } from './webcomponent.js';\r\nexport * from './core/index.js';\r\n\r\nimport { registerWebComponent } from './webcomponent.js';\r\n// Auto-register <three-video> when the main entry is imported.\r\nregisterWebComponent();\r\n"],"names":["h","tag","props","children","el","cls","rest","c","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrBtn","projBtn","projMenu","p","PROJECTIONS","b","offBtn","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","useProxyCb","proxyUrlIn","proxyPwIn","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","closeMenus","toastTimer","showToast","msg","onTap","downOn","firedAt","run","e","formatTime","open","updateProjection","mode","label","on2","d","x","applyProxy","next","_b","_a","path","inside","hideTimer","popupOpen","hideBar","showBar","ok","err","presenting","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","cb","src","o","proj","detectProjection","url","format","buildProxyUrl","primaryCO","err2","deg","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}
1
+ {"version":3,"file":"three-vr-player.js","sources":["../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["import type { Projection } from '../types.js';\r\nimport { PROJECTIONS } from '../core/projections.js';\r\nimport { formatTime } from './format.js';\r\n\r\n/** What the ControlsUI needs from the Player to drive playback + view. */\r\nexport interface PlayerBridge {\r\n video: HTMLVideoElement;\r\n surface: HTMLElement; // canvas — for wheel-zoom\r\n fullscreenTarget: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n enterVR(): Promise<void>;\r\n exitVR(): void;\r\n isPresenting(): boolean;\r\n onVrChange(cb: (presenting: boolean) => void): void;\r\n getProjection(): Projection | 'off';\r\n setProjection(p: Projection | 'off'): void;\r\n setSwapEyes(v: boolean): void;\r\n setFov(deg: number): void;\r\n setSupersampling(x: number): void;\r\n initial: { swapEyes: boolean; fov: number; supersampling: number };\r\n proxy: { url: string; apiPassword: string; enabled: boolean };\r\n setProxy(p: { url: string; apiPassword: string; enabled: boolean }): void;\r\n}\r\n\r\ntype Props = Record<string, unknown> & { class?: string };\r\nfunction h<K extends keyof HTMLElementTagNameMap>(tag: K, props: Props = {}, children: (Node | string)[] = []): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tag);\r\n const { class: cls, ...rest } = props;\r\n if (cls) el.className = cls;\r\n Object.assign(el, rest);\r\n for (const c of children) el.append(c);\r\n return el;\r\n}\r\n\r\n/** Builds the controls bar + settings/menus inside `root` and wires them to `bridge`. */\r\nexport class ControlsUI {\r\n private readonly nodes: HTMLElement[] = [];\r\n private readonly disposers: (() => void)[] = [];\r\n\r\n constructor(root: ParentNode, bridge: PlayerBridge) {\r\n const v = bridge.video;\r\n\r\n // --- playback ---\r\n const play = h('button', { class: 'tvp-btn tvp-play', title: 'Play/Pause', textContent: '▶' });\r\n const seek = h('input', { class: 'tvp-seek', type: 'range', min: '0', max: '1000', value: '0' });\r\n const time = h('span', { class: 'tvp-time', textContent: '0:00 / 0:00' });\r\n\r\n // --- volume ---\r\n const mute = h('button', { class: 'tvp-btn tvp-mute', title: 'Volume', textContent: '🔊' });\r\n const volume = h('input', { class: 'tvp-volume', type: 'range', min: '0', max: '1', step: '0.01', value: '1' });\r\n const volPopup = h('div', { class: 'tvp-volpopup', hidden: true }, [volume]);\r\n const volWrap = h('span', { class: 'tvp-volwrap' }, [mute, volPopup]);\r\n\r\n // --- VR ---\r\n const vrBtn = h('button', { class: 'tvp-btn tvp-vrbtn', title: 'Enter VR', textContent: '🥽', hidden: true });\r\n\r\n // --- projection menu ---\r\n const projBtn = h('button', { class: 'tvp-btn tvp-projbtn', title: 'Projection', textContent: '🌐' });\r\n const projMenu = h('div', { class: 'tvp-projmenu', hidden: true });\r\n for (const p of PROJECTIONS) {\r\n const b = h('button', { textContent: p.label });\r\n b.dataset.mode = p.value;\r\n projMenu.append(b);\r\n }\r\n const offBtn = h('button', { textContent: 'Off (native player)' });\r\n offBtn.dataset.mode = 'off';\r\n projMenu.append(h('hr', { class: 'tvp-sep' }), offBtn);\r\n const projWrap = h('span', { class: 'tvp-projwrap' }, [projBtn, projMenu]);\r\n\r\n // --- settings ---\r\n const settingsBtn = h('button', { class: 'tvp-btn tvp-settingsbtn', title: 'Settings', textContent: '⚙' });\r\n const fullscreen = h('button', { class: 'tvp-btn tvp-fullscreen', title: 'Fullscreen', textContent: '⛶' });\r\n\r\n const controls = h('footer', { class: 'tvp-controls' }, [play, seek, time, volWrap, vrBtn, projWrap, settingsBtn, fullscreen]);\r\n\r\n const swapCb = h('input', { type: 'checkbox', checked: bridge.initial.swapEyes });\r\n const fovRange = h('input', { type: 'range', min: '30', max: '100', step: '1', value: String(bridge.initial.fov) });\r\n const fovVal = h('span', { textContent: String(bridge.initial.fov) });\r\n const ssRange = h('input', { type: 'range', min: '1', max: '2', step: '0.25', value: String(bridge.initial.supersampling) });\r\n const ssVal = h('span', { textContent: String(bridge.initial.supersampling) });\r\n const useProxyCb = h('input', { type: 'checkbox', checked: bridge.proxy.enabled });\r\n const proxyUrlIn = h('input', { type: 'text', value: bridge.proxy.url, placeholder: 'http://localhost:8888', spellcheck: false });\r\n const proxyPwIn = h('input', { type: 'password', value: bridge.proxy.apiPassword, placeholder: 'API password' });\r\n const settings = h('section', { class: 'tvp-settings' }, [\r\n h('label', { class: 'row' }, [swapCb, 'Swap eyes (if depth looks wrong)']),\r\n h('label', {}, [makeText('Field of view (zoom): ', fovVal, '°'), fovRange]),\r\n h('label', {}, [makeText('Supersampling: ', ssVal, '× (sharpness)'), ssRange]),\r\n h('hr', { class: 'tvp-sep' }),\r\n h('label', { class: 'row' }, [useProxyCb, 'Use CORS proxy']),\r\n h('label', {}, ['Proxy URL', proxyUrlIn]),\r\n h('label', {}, ['API password', proxyPwIn]),\r\n ]);\r\n\r\n const toast = h('div', { class: 'tvp-toast' });\r\n\r\n this.nodes.push(controls, settings, toast);\r\n for (const n of this.nodes) root.append(n);\r\n\r\n // ---- wiring ----\r\n const on = <T extends EventTarget>(t: T, ev: string, fn: (e: any) => void, opts?: AddEventListenerOptions) => {\r\n t.addEventListener(ev, fn as EventListener, opts);\r\n this.disposers.push(() => t.removeEventListener(ev, fn as EventListener, opts));\r\n };\r\n const setSeekFill = (pct: number) => seek.style.setProperty('--seek', `${pct}%`);\r\n // Only one popup (volume / projection / settings) open at a time.\r\n const closeMenus = () => { volPopup.hidden = true; projMenu.hidden = true; settings.classList.remove('open'); };\r\n let toastTimer = 0;\r\n const showToast = (msg: string) => {\r\n toast.textContent = msg;\r\n toast.classList.add('show');\r\n clearTimeout(toastTimer);\r\n toastTimer = window.setTimeout(() => toast.classList.remove('show'), 4500);\r\n };\r\n\r\n // Fire on a real pointer tap. The Meta Quest controller ray often fails to\r\n // synthesize `click` on <button> (tiny ray movement between press/release exceeds\r\n // the click threshold), while pointerup always fires. We trigger on pointerup and\r\n // keep `click` for keyboard/assistive tech, deduped so each activation fires once.\r\n // (pointerup carries transient activation too, so WebXR requestSession still works.)\r\n const onTap = (el: HTMLElement, fn: (e: Event) => void) => {\r\n let downOn = false;\r\n let firedAt = -1e9;\r\n const run = (e: Event) => { firedAt = e.timeStamp; fn(e); };\r\n on(el, 'pointerdown', () => { downOn = true; });\r\n on(el, 'pointercancel', () => { downOn = false; });\r\n on(el, 'pointerup', (e: Event) => { if (downOn) { downOn = false; run(e); } });\r\n on(el, 'click', (e: Event) => { if (e.timeStamp - firedAt > 700) run(e); });\r\n };\r\n\r\n onTap(play, () => { v.paused ? void v.play() : v.pause(); });\r\n on(v, 'play', () => { play.textContent = '⏸'; });\r\n on(v, 'pause', () => { play.textContent = '▶'; });\r\n\r\n on(v, 'timeupdate', () => {\r\n if (v.duration) {\r\n const pct = (v.currentTime / v.duration) * 100;\r\n seek.value = String(pct * 10);\r\n setSeekFill(pct);\r\n time.textContent = `${formatTime(v.currentTime)} / ${formatTime(v.duration)}`;\r\n }\r\n });\r\n on(seek, 'input', () => {\r\n setSeekFill(Number(seek.value) / 10);\r\n if (v.duration) v.currentTime = (Number(seek.value) / 1000) * v.duration;\r\n });\r\n\r\n onTap(mute, () => { const open = volPopup.hidden; closeMenus(); volPopup.hidden = !open; });\r\n on(volume, 'input', () => { v.volume = Number(volume.value); v.muted = v.volume === 0; mute.textContent = v.muted ? '🔇' : '🔊'; });\r\n\r\n const updateProjection = () => {\r\n const mode = bridge.getProjection();\r\n let label = 'Projection';\r\n projMenu.querySelectorAll('button').forEach((b) => {\r\n const on2 = b.dataset.mode === mode;\r\n b.classList.toggle('active', on2);\r\n if (on2) label = `Projection: ${b.textContent}`;\r\n });\r\n projBtn.title = label;\r\n };\r\n onTap(projBtn, () => { const open = projMenu.hidden; closeMenus(); projMenu.hidden = !open; });\r\n onTap(projMenu, (e: Event) => {\r\n const b = (e.target as HTMLElement).closest('button[data-mode]') as HTMLElement | null;\r\n if (b) { bridge.setProjection(b.dataset.mode as Projection | 'off'); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n onTap(settingsBtn, () => { const open = !settings.classList.contains('open'); closeMenus(); settings.classList.toggle('open', open); });\r\n on(swapCb, 'change', () => bridge.setSwapEyes(swapCb.checked));\r\n on(fovRange, 'input', () => { const d = Number(fovRange.value); fovVal.textContent = String(d); bridge.setFov(d); });\r\n on(ssRange, 'input', () => { const x = Number(ssRange.value); ssVal.textContent = String(x); bridge.setSupersampling(x); });\r\n const applyProxy = () => bridge.setProxy({ url: proxyUrlIn.value.trim(), apiPassword: proxyPwIn.value, enabled: useProxyCb.checked });\r\n on(useProxyCb, 'change', applyProxy);\r\n on(proxyUrlIn, 'change', applyProxy);\r\n on(proxyPwIn, 'change', applyProxy);\r\n on(bridge.surface, 'wheel', (e: WheelEvent) => {\r\n e.preventDefault();\r\n const next = Math.max(30, Math.min(100, Number(fovRange.value) + Math.sign(e.deltaY) * 3));\r\n fovRange.value = String(next); fovVal.textContent = String(next); bridge.setFov(next);\r\n }, { passive: false });\r\n\r\n onTap(fullscreen, () => {\r\n const t = bridge.fullscreenTarget;\r\n if (!document.fullscreenElement) void t.requestFullscreen?.();\r\n else void document.exitFullscreen?.();\r\n });\r\n\r\n // outside-tap closes any open popup (pointerdown fires reliably on Quest; click may not)\r\n on(document, 'pointerdown', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n const inside = (el: Node) => path.includes(el);\r\n if (![volPopup, mute, projMenu, projBtn, settings, settingsBtn].some(inside)) closeMenus();\r\n });\r\n\r\n // Auto-hide the control bar after 5s of inactivity; show on movement over the player.\r\n let hideTimer = 0;\r\n const popupOpen = () => !volPopup.hidden || !projMenu.hidden || settings.classList.contains('open');\r\n const hideBar = () => { if (!popupOpen()) controls.classList.add('tvp-hidden'); };\r\n const showBar = () => { controls.classList.remove('tvp-hidden'); clearTimeout(hideTimer); hideTimer = window.setTimeout(hideBar, 5000); };\r\n on(bridge.fullscreenTarget, 'pointermove', showBar);\r\n on(bridge.fullscreenTarget, 'pointerdown', showBar);\r\n on(bridge.fullscreenTarget, 'pointerleave', () => { clearTimeout(hideTimer); hideBar(); });\r\n this.disposers.push(() => clearTimeout(hideTimer));\r\n showBar();\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => { vrBtn.hidden = !ok; });\r\n onTap(vrBtn, () => {\r\n if (bridge.isPresenting()) { bridge.exitVR(); return; }\r\n // requestSession is the first thing off the tap so it keeps the user gesture;\r\n // any rejection is surfaced instead of silently swallowed.\r\n bridge.enterVR().catch((err: unknown) => showToast(err instanceof Error ? err.message : 'Could not enter VR.'));\r\n });\r\n bridge.onVrChange((presenting) => { vrBtn.classList.toggle('active', presenting); vrBtn.title = presenting ? 'Exit VR' : 'Enter VR'; });\r\n }\r\n\r\n dispose() {\r\n for (const d of this.disposers) d();\r\n for (const n of this.nodes) n.remove();\r\n }\r\n}\r\n\r\nfunction makeText(prefix: string, valueSpan: HTMLElement, suffix: string): DocumentFragment {\r\n const frag = document.createDocumentFragment();\r\n frag.append(prefix, valueSpan, suffix);\r\n return frag;\r\n}\r\n","import { StereoScene } from './core/StereoScene.js';\r\nimport { LookControls } from './core/LookControls.js';\r\nimport { VideoSource } from './core/VideoSource.js';\r\nimport { buildProxyUrl, type ProxyConfig } from './core/proxy.js';\r\nimport { detectProjection } from './core/projections.js';\r\nimport { ControlsUI } from './ui/ControlsUI.js';\r\nimport css from './ui/controls.css?inline';\r\nimport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\n\r\ntype ViewSettings = { projection: Projection; swapEyes: boolean; fov: number; supersampling: number };\r\nconst STORE_KEY = 'three-vr-player:settings';\r\n\r\n/**\r\n * A drop-in 3D/VR video player. Mounts a canvas + controls (into a Shadow DOM by\r\n * default) inside `container`, composing the headless core.\r\n */\r\nexport class Player {\r\n readonly video: HTMLVideoElement;\r\n\r\n private readonly opts: PlayerOptions;\r\n private readonly wrap: HTMLElement;\r\n private readonly canvas: HTMLCanvasElement;\r\n private readonly scene: StereoScene;\r\n private readonly look: LookControls;\r\n private readonly source = new VideoSource();\r\n private readonly ui?: ControlsUI;\r\n private readonly listeners = new Map<PlayerEvent, Set<(p?: unknown) => void>>();\r\n private view: ViewSettings;\r\n private readyEmitted = false;\r\n private native = false;\r\n private tainted = false; // current video loaded via CORS-fallback (crossOrigin removed)\r\n private displayMode: Projection | 'off';\r\n private loading = false;\r\n private proxyConfig?: ProxyConfig;\r\n private useProxy = false;\r\n private currentSrc?: string;\r\n\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\r\n this.proxyConfig = options.proxy;\r\n this.useProxy = !!options.proxy;\r\n const stored = options.persistSettings ? this.loadSettings() : null;\r\n this.view = {\r\n projection: options.projection ?? stored?.projection ?? '180-sbs',\r\n swapEyes: options.swapEyes ?? stored?.swapEyes ?? false,\r\n fov: options.fov ?? stored?.fov ?? 70,\r\n supersampling: options.supersampling ?? stored?.supersampling ?? 1.5,\r\n };\r\n this.displayMode = this.view.projection;\r\n\r\n this.wrap = document.createElement('div');\r\n this.wrap.className = 'tvp';\r\n this.wrap.style.cssText = 'position:relative;width:100%;height:100%;overflow:hidden;';\r\n container.appendChild(this.wrap);\r\n\r\n let root: ParentNode;\r\n if (options.shadowDom !== false) {\r\n const shadow = this.wrap.attachShadow({ mode: 'open' });\r\n const style = document.createElement('style'); style.textContent = css; shadow.appendChild(style);\r\n root = shadow;\r\n } else {\r\n if (!document.getElementById('tvp-styles')) {\r\n const style = document.createElement('style'); style.id = 'tvp-styles'; style.textContent = css; document.head.appendChild(style);\r\n }\r\n root = this.wrap;\r\n }\r\n\r\n this.canvas = document.createElement('canvas'); this.canvas.className = 'tvp-canvas';\r\n this.video = document.createElement('video'); this.video.className = 'tvp-video'; this.video.playsInline = true;\r\n if (options.crossOrigin !== null) this.video.crossOrigin = options.crossOrigin ?? 'anonymous';\r\n root.append(this.canvas, this.video);\r\n\r\n this.scene = new StereoScene({\r\n canvas: this.canvas, video: this.video,\r\n projection: this.view.projection, swapEyes: this.view.swapEyes,\r\n fov: this.view.fov, supersampling: this.view.supersampling,\r\n });\r\n if (options.title) this.scene.setVRTitle(options.title);\r\n // In-VR projection stepper routes through Player so its state/persistence/DOM controls stay in sync.\r\n this.scene.setProjectionRequester((p) => this.setProjection(p));\r\n this.look = new LookControls(this.scene.camera, this.canvas, { isPresenting: () => this.scene.renderer.xr.isPresenting });\r\n this.scene.onFrame(() => this.look.update());\r\n this.look.setEnabled(!this.scene.isFlat());\r\n\r\n if (options.controls !== false) {\r\n this.ui = new ControlsUI(root, {\r\n video: this.video,\r\n surface: this.canvas,\r\n fullscreenTarget: this.wrap,\r\n vrSupported: () => this.vrSupported(),\r\n enterVR: () => this.enterVR(),\r\n exitVR: () => this.scene.exitVR(),\r\n isPresenting: () => this.scene.renderer.xr.isPresenting,\r\n onVrChange: (cb) => { this.on('enterxr', () => cb(true)); this.on('exitxr', () => cb(false)); },\r\n getProjection: () => this.displayMode,\r\n setProjection: (p) => this.setProjection(p),\r\n setSwapEyes: (v) => this.setSwapEyes(v),\r\n setFov: (d) => this.setFov(d),\r\n setSupersampling: (x) => this.setSupersampling(x),\r\n initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling },\r\n proxy: { url: this.proxyConfig?.url ?? '', apiPassword: this.proxyConfig?.apiPassword ?? '', enabled: this.useProxy },\r\n setProxy: (p) => this.setProxy(p),\r\n });\r\n }\r\n\r\n this.video.addEventListener('play', () => this.emit('play'));\r\n this.video.addEventListener('pause', () => this.emit('pause'));\r\n this.video.addEventListener('ended', () => this.emit('ended'));\r\n this.video.addEventListener('timeupdate', () => this.emit('timeupdate', this.video.currentTime));\r\n this.video.addEventListener('error', () => { if (!this.loading) this.emit('error', this.video.error); });\r\n this.video.addEventListener('loadedmetadata', () => { if (!this.readyEmitted) { this.readyEmitted = true; this.emit('ready'); } });\r\n this.scene.renderer.xr.addEventListener('sessionstart', () => this.emit('enterxr'));\r\n this.scene.renderer.xr.addEventListener('sessionend', () => this.emit('exitxr'));\r\n // Arm the headset/browser's own \"Enter VR\" affordance when immersive VR is available.\r\n void this.vrSupported().then((ok) => { if (ok) this.scene.offerVR(); });\r\n\r\n if (options.src) void this.load(options.src, { projection: options.projection });\r\n }\r\n\r\n private vrSupported(): Promise<boolean> {\r\n if (this.opts.vrButton === false || !navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-vr').catch(() => false);\r\n }\r\n\r\n /** Whether immersive-ar (passthrough) is available — used for alpha/fisheye content. */\r\n arSupported(): Promise<boolean> {\r\n if (!navigator.xr) return Promise.resolve(false);\r\n return navigator.xr.isSessionSupported('immersive-ar').catch(() => false);\r\n }\r\n\r\n async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n this.currentSrc = src;\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.applyProjectionGeometry(proj);\r\n // Packed alpha matte (fisheye passthrough): explicit option, else DeoVR's `_ALPHA` filename marker.\r\n this.scene.setAlphaMatte(this.opts.alpha ?? /_alpha/i.test(src));\r\n if (this.displayMode !== 'off') this.displayMode = this.view.projection;\r\n const { url, format } = buildProxyUrl(src, this.useProxy ? this.proxyConfig : undefined);\r\n const primaryCO = this.opts.crossOrigin === undefined ? 'anonymous' : this.opts.crossOrigin;\r\n this.loading = true;\r\n try {\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: primaryCO });\r\n this.tainted = false;\r\n this.applyDisplay();\r\n await this.video.play().catch(() => { /* autoplay may be blocked until a user gesture */ });\r\n } catch (err) {\r\n // A cross-origin progressive source that isn't CORS-clean (and isn't proxied) can't be a\r\n // WebGL texture. Retry without crossOrigin and show plain 2D <video> playback if allowed.\r\n if (this.opts.nativeFallback !== false && format === 'progressive' && primaryCO !== null) {\r\n try {\r\n this.tainted = true;\r\n this.setNativeFallback(true); // stop WebGL rendering before the tainted video loads\r\n await this.source.attach(this.video, { url, format }, { crossOrigin: null });\r\n await this.video.play().catch(() => {});\r\n this.emit('fallback');\r\n return;\r\n } catch (err2) {\r\n this.tainted = false;\r\n this.setNativeFallback(false);\r\n this.emit('error', err2);\r\n throw err2;\r\n }\r\n }\r\n this.setNativeFallback(false);\r\n this.emit('error', err);\r\n throw err;\r\n } finally {\r\n this.loading = false;\r\n }\r\n }\r\n\r\n /** Swap between WebGL (3D) rendering and plain 2D `<video>` playback. */\r\n private setNativeFallback(on: boolean) {\r\n if (on === this.native) return;\r\n this.native = on;\r\n if (on) {\r\n this.scene.pauseRendering();\r\n this.canvas.style.display = 'none';\r\n Object.assign(this.video.style, {\r\n display: 'block', position: 'absolute', inset: '0',\r\n width: '100%', height: '100%', objectFit: 'contain', background: '#000', zIndex: '1',\r\n });\r\n } else {\r\n this.canvas.style.display = '';\r\n this.video.style.cssText = '';\r\n this.scene.resumeRendering();\r\n }\r\n }\r\n\r\n async play() { await this.video.play(); }\r\n pause() { this.video.pause(); }\r\n /** Set the display mode: a geometry projection (WebGL 3D) or 'off' for plain 2D `<video>`. */\r\n setProjection(p: Projection | 'off') {\r\n if (p === 'off') {\r\n this.displayMode = 'off';\r\n this.setNativeFallback(true);\r\n this.emit('projectionchange', p);\r\n return;\r\n }\r\n this.displayMode = p;\r\n this.applyProjectionGeometry(p);\r\n // A CORS-tainted (native-fallback) video can't be a WebGL texture — reload to re-attempt 3D.\r\n if (this.tainted && this.currentSrc) void this.load(this.currentSrc);\r\n else this.setNativeFallback(false);\r\n this.emit('projectionchange', p);\r\n }\r\n private applyProjectionGeometry(p: Projection) {\r\n this.scene.setProjection(p);\r\n this.look.setEnabled(!this.scene.isFlat());\r\n this.look.reset();\r\n this.view.projection = p; this.persist();\r\n }\r\n private applyDisplay() { this.setNativeFallback(this.displayMode === 'off'); }\r\n setSwapEyes(v: boolean) { this.scene.setSwapEyes(v); this.view.swapEyes = v; this.persist(); }\r\n setFov(deg: number) { this.scene.setFov(deg); this.view.fov = deg; this.persist(); }\r\n setSupersampling(x: number) { this.scene.setSupersampling(x); this.view.supersampling = x; this.persist(); }\r\n /** Update the CORS proxy config / toggle it, and reload the current source if any. */\r\n setProxy(p: { url: string; apiPassword?: string; enabled: boolean }) {\r\n this.proxyConfig = p.url ? { url: p.url, apiPassword: p.apiPassword || undefined } : undefined;\r\n this.useProxy = p.enabled;\r\n if (this.currentSrc) void this.load(this.currentSrc);\r\n }\r\n\r\n /** Enter immersive VR. Rejects if the WebXR session request fails (e.g. no headset,\r\n * or a non-secure origin) — the built-in controls surface the reason as a toast. */\r\n async enterVR() { await this.scene.enterVR(); }\r\n /** Enter immersive AR (passthrough) — for alpha/fisheye content. */\r\n async enterAR() { await this.scene.enterAR(); }\r\n exitVR() { this.scene.exitVR(); }\r\n\r\n get three() { return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera }; }\r\n\r\n on(e: PlayerEvent, cb: (p?: unknown) => void) {\r\n let s = this.listeners.get(e); if (!s) { s = new Set(); this.listeners.set(e, s); } s.add(cb); return this;\r\n }\r\n off(e: PlayerEvent, cb: (p?: unknown) => void) { this.listeners.get(e)?.delete(cb); return this; }\r\n private emit(e: PlayerEvent, p?: unknown) { this.listeners.get(e)?.forEach((cb) => cb(p)); }\r\n\r\n private persist() {\r\n if (!this.opts.persistSettings) return;\r\n try { localStorage.setItem(STORE_KEY, JSON.stringify(this.view)); } catch { /* private mode */ }\r\n }\r\n private loadSettings(): Partial<ViewSettings> | null {\r\n try { const raw = localStorage.getItem(STORE_KEY); return raw ? JSON.parse(raw) as Partial<ViewSettings> : null; }\r\n catch { return null; }\r\n }\r\n\r\n dispose() {\r\n this.ui?.dispose(); this.look.dispose(); this.source.dispose(); this.scene.dispose();\r\n this.video.remove(); this.canvas.remove(); this.wrap.remove(); this.listeners.clear();\r\n }\r\n}\r\n","import { Player } from './Player.js';\r\nimport type { Projection } from './types.js';\r\n\r\n/**\r\n * `<three-video src=\"…\" projection=\"180-sbs\" controls>` — a framework-agnostic\r\n * custom element wrapping {@link Player}. Give it a size via CSS.\r\n */\r\nexport class ThreeVideoElement extends HTMLElement {\r\n private player?: Player;\r\n\r\n static get observedAttributes() { return ['src', 'projection']; }\r\n\r\n connectedCallback() {\r\n if (this.player) return;\r\n if (!this.style.display) this.style.display = 'block';\r\n this.player = new Player(this, {\r\n src: this.getAttribute('src') ?? undefined,\r\n projection: (this.getAttribute('projection') as Projection) ?? undefined,\r\n controls: this.hasAttribute('controls'),\r\n swapEyes: this.hasAttribute('swap-eyes'),\r\n autoDetect: this.getAttribute('auto-detect') !== 'false',\r\n fov: this.numAttr('fov'),\r\n supersampling: this.numAttr('supersampling'),\r\n proxy: this.getAttribute('proxy-url')\r\n ? { url: this.getAttribute('proxy-url')!, apiPassword: this.getAttribute('proxy-password') ?? undefined }\r\n : undefined,\r\n });\r\n }\r\n\r\n attributeChangedCallback(name: string, _old: string | null, value: string | null) {\r\n if (!this.player || value == null) return;\r\n if (name === 'src') void this.player.load(value);\r\n if (name === 'projection') this.player.setProjection(value as Projection);\r\n }\r\n\r\n disconnectedCallback() { this.player?.dispose(); this.player = undefined; }\r\n\r\n private numAttr(n: string): number | undefined {\r\n const v = this.getAttribute(n);\r\n return v == null ? undefined : Number(v);\r\n }\r\n\r\n /** The underlying Player instance (for programmatic control). */\r\n get api(): Player | undefined { return this.player; }\r\n}\r\n\r\nexport function registerWebComponent(tag = 'three-video') {\r\n if (typeof customElements !== 'undefined' && !customElements.get(tag)) {\r\n customElements.define(tag, ThreeVideoElement);\r\n }\r\n}\r\n","export { Player } from './Player.js';\r\nexport type { PlayerOptions, PlayerEvent, Projection } from './types.js';\r\nexport { ThreeVideoElement, registerWebComponent } from './webcomponent.js';\r\nexport * from './core/index.js';\r\n\r\nimport { registerWebComponent } from './webcomponent.js';\r\n// Auto-register <three-video> when the main entry is imported.\r\nregisterWebComponent();\r\n"],"names":["h","tag","props","children","el","cls","rest","c","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrBtn","projBtn","projMenu","p","PROJECTIONS","b","offBtn","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","useProxyCb","proxyUrlIn","proxyPwIn","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","closeMenus","toastTimer","showToast","msg","onTap","downOn","firedAt","run","e","formatTime","open","updateProjection","mode","label","on2","d","x","applyProxy","next","_b","_a","path","inside","hideTimer","popupOpen","hideBar","showBar","ok","err","presenting","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","cb","src","o","proj","detectProjection","url","format","buildProxyUrl","primaryCO","err2","deg","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}