three-vr-player 0.1.0 → 0.2.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.
- package/README.md +6 -1
- package/dist/Player.d.ts +4 -0
- package/dist/{VideoSource-pc2RM7bl.js → VideoSource-CJGIX8gL.js} +72 -64
- package/dist/VideoSource-CJGIX8gL.js.map +1 -0
- package/dist/core/StereoScene.d.ts +3 -0
- package/dist/core/VideoSource.d.ts +2 -0
- package/dist/core.js +1 -1
- package/dist/three-vr-player.js +135 -109
- package/dist/three-vr-player.js.map +1 -1
- package/dist/three-vr-player.standalone.js +1 -1
- package/dist/three-vr-player.standalone.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/package.json +39 -9
- package/dist/VideoSource-pc2RM7bl.js.map +0 -1
package/dist/three-vr-player.js
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
import { P as
|
|
2
|
-
import { M as G, c as Q, d as X, i as Z } from "./VideoSource-
|
|
3
|
-
function
|
|
4
|
-
if (!Number.isFinite(
|
|
5
|
-
const
|
|
6
|
-
return
|
|
1
|
+
import { P as I, V as R, S as $, L as V, a as D, b as U } from "./VideoSource-CJGIX8gL.js";
|
|
2
|
+
import { M as G, c as Q, d as X, i as Z } from "./VideoSource-CJGIX8gL.js";
|
|
3
|
+
function z(l) {
|
|
4
|
+
if (!Number.isFinite(l) || l < 0) return "0:00";
|
|
5
|
+
const t = Math.floor(l % 60), s = Math.floor(l / 60 % 60), e = Math.floor(l / 3600), a = String(t).padStart(2, "0");
|
|
6
|
+
return e > 0 ? `${e}:${String(s).padStart(2, "0")}:${a}` : `${s}:${a}`;
|
|
7
7
|
}
|
|
8
|
-
function n(
|
|
9
|
-
const
|
|
10
|
-
a && (
|
|
11
|
-
for (const
|
|
12
|
-
return
|
|
8
|
+
function n(l, t = {}, s = []) {
|
|
9
|
+
const e = document.createElement(l), { class: a, ...o } = t;
|
|
10
|
+
a && (e.className = a), Object.assign(e, o);
|
|
11
|
+
for (const c of s) e.append(c);
|
|
12
|
+
return e;
|
|
13
13
|
}
|
|
14
14
|
class J {
|
|
15
|
-
constructor(
|
|
15
|
+
constructor(t, s) {
|
|
16
16
|
this.nodes = [], this.disposers = [];
|
|
17
|
-
const
|
|
18
|
-
for (const i of
|
|
17
|
+
const e = s.video, a = n("button", { class: "tvp-btn tvp-play", title: "Play/Pause", textContent: "▶" }), o = n("input", { class: "tvp-seek", type: "range", min: "0", max: "1000", value: "0" }), c = n("span", { class: "tvp-time", textContent: "0:00 / 0:00" }), h = n("button", { class: "tvp-btn tvp-mute", title: "Volume", textContent: "🔊" }), v = n("input", { class: "tvp-volume", type: "range", min: "0", max: "1", step: "0.01", value: "1" }), m = n("div", { class: "tvp-volpopup", hidden: !0 }, [v]), L = n("span", { class: "tvp-volwrap" }, [h, m]), k = n("span", { class: "tvp-vrslot" }), b = n("button", { class: "tvp-btn tvp-projbtn", title: "Projection", textContent: "🌐" }), d = n("div", { class: "tvp-projmenu", hidden: !0 });
|
|
18
|
+
for (const i of I) {
|
|
19
19
|
const r = n("button", { textContent: i.label });
|
|
20
20
|
r.dataset.mode = i.value, d.append(r);
|
|
21
21
|
}
|
|
22
|
-
const
|
|
22
|
+
const T = n("span", { class: "tvp-projwrap" }, [b, d]), S = n("button", { class: "tvp-btn tvp-settingsbtn", title: "Settings", textContent: "⚙" }), E = n("button", { class: "tvp-btn tvp-fullscreen", title: "Fullscreen", textContent: "⛶" }), M = n("footer", { class: "tvp-controls" }, [a, o, c, L, k, T, S, E]), x = n("input", { type: "checkbox", checked: s.initial.swapEyes }), f = n("input", { type: "range", min: "30", max: "100", step: "1", value: String(s.initial.fov) }), y = n("span", { textContent: String(s.initial.fov) }), w = n("input", { type: "range", min: "1", max: "2", step: "0.25", value: String(s.initial.supersampling) }), j = n("span", { textContent: String(s.initial.supersampling) }), C = n("section", { class: "tvp-settings" }, [
|
|
23
23
|
n("label", { class: "row" }, [x, "Swap eyes (if depth looks wrong)"]),
|
|
24
|
-
n("label", {}, [
|
|
25
|
-
n("label", {}, [
|
|
24
|
+
n("label", {}, [N("Field of view (zoom): ", y, "°"), f]),
|
|
25
|
+
n("label", {}, [N("Supersampling: ", j, "× (sharpness)"), w])
|
|
26
26
|
]), B = n("div", { class: "tvp-toast" });
|
|
27
|
-
this.nodes.push(
|
|
28
|
-
for (const i of this.nodes)
|
|
29
|
-
const p = (i, r, u,
|
|
30
|
-
i.addEventListener(r, u,
|
|
27
|
+
this.nodes.push(M, C, B);
|
|
28
|
+
for (const i of this.nodes) t.append(i);
|
|
29
|
+
const p = (i, r, u, g) => {
|
|
30
|
+
i.addEventListener(r, u, g), this.disposers.push(() => i.removeEventListener(r, u, g));
|
|
31
31
|
}, P = (i) => o.style.setProperty("--seek", `${i}%`);
|
|
32
32
|
p(a, "click", () => {
|
|
33
|
-
|
|
34
|
-
}), p(
|
|
33
|
+
e.paused ? e.play() : e.pause();
|
|
34
|
+
}), p(e, "play", () => {
|
|
35
35
|
a.textContent = "⏸";
|
|
36
|
-
}), p(
|
|
36
|
+
}), p(e, "pause", () => {
|
|
37
37
|
a.textContent = "▶";
|
|
38
|
-
}), p(
|
|
39
|
-
if (
|
|
40
|
-
const i =
|
|
41
|
-
o.value = String(i * 10), P(i),
|
|
38
|
+
}), p(e, "timeupdate", () => {
|
|
39
|
+
if (e.duration) {
|
|
40
|
+
const i = e.currentTime / e.duration * 100;
|
|
41
|
+
o.value = String(i * 10), P(i), c.textContent = `${z(e.currentTime)} / ${z(e.duration)}`;
|
|
42
42
|
}
|
|
43
43
|
}), p(o, "input", () => {
|
|
44
|
-
P(Number(o.value) / 10),
|
|
45
|
-
}), p(
|
|
46
|
-
i.stopPropagation(),
|
|
47
|
-
}), p(
|
|
48
|
-
|
|
44
|
+
P(Number(o.value) / 10), e.duration && (e.currentTime = Number(o.value) / 1e3 * e.duration);
|
|
45
|
+
}), p(h, "click", (i) => {
|
|
46
|
+
i.stopPropagation(), m.hidden = !m.hidden;
|
|
47
|
+
}), p(v, "input", () => {
|
|
48
|
+
e.volume = Number(v.value), e.muted = e.volume === 0, h.textContent = e.muted ? "🔇" : "🔊";
|
|
49
49
|
});
|
|
50
|
-
const
|
|
50
|
+
const F = () => {
|
|
51
51
|
const i = s.getProjection();
|
|
52
52
|
let r = "Projection";
|
|
53
53
|
d.querySelectorAll("button").forEach((u) => {
|
|
54
|
-
const
|
|
55
|
-
u.classList.toggle("active",
|
|
56
|
-
}),
|
|
54
|
+
const g = u.dataset.mode === i;
|
|
55
|
+
u.classList.toggle("active", g), g && (r = `Projection: ${u.textContent}`);
|
|
56
|
+
}), b.title = r;
|
|
57
57
|
};
|
|
58
|
-
p(
|
|
58
|
+
p(b, "click", (i) => {
|
|
59
59
|
i.stopPropagation(), d.hidden = !d.hidden;
|
|
60
60
|
}), p(d, "click", (i) => {
|
|
61
61
|
const r = i.target.closest("button[data-mode]");
|
|
62
|
-
r && (s.setProjection(r.dataset.mode),
|
|
63
|
-
}),
|
|
62
|
+
r && (s.setProjection(r.dataset.mode), F(), d.hidden = !0);
|
|
63
|
+
}), F(), p(S, "click", (i) => {
|
|
64
64
|
i.stopPropagation(), C.classList.toggle("open");
|
|
65
|
-
}), p(x, "change", () => s.setSwapEyes(x.checked)), p(
|
|
66
|
-
const i = Number(
|
|
67
|
-
|
|
68
|
-
}), p(
|
|
69
|
-
const i = Number(
|
|
65
|
+
}), p(x, "change", () => s.setSwapEyes(x.checked)), p(f, "input", () => {
|
|
66
|
+
const i = Number(f.value);
|
|
67
|
+
y.textContent = String(i), s.setFov(i);
|
|
68
|
+
}), p(w, "input", () => {
|
|
69
|
+
const i = Number(w.value);
|
|
70
70
|
j.textContent = String(i), s.setSupersampling(i);
|
|
71
71
|
}), p(s.surface, "wheel", (i) => {
|
|
72
72
|
i.preventDefault();
|
|
73
|
-
const r = Math.max(30, Math.min(100, Number(
|
|
74
|
-
|
|
73
|
+
const r = Math.max(30, Math.min(100, Number(f.value) + Math.sign(i.deltaY) * 3));
|
|
74
|
+
f.value = String(r), y.textContent = String(r), s.setFov(r);
|
|
75
75
|
}, { passive: !1 }), p(E, "click", () => {
|
|
76
76
|
var r, u;
|
|
77
77
|
const i = s.fullscreenTarget;
|
|
78
78
|
document.fullscreenElement ? (u = document.exitFullscreen) == null || u.call(document) : (r = i.requestFullscreen) == null || r.call(i);
|
|
79
79
|
}), p(document, "click", (i) => {
|
|
80
80
|
const r = i.composedPath();
|
|
81
|
-
!
|
|
81
|
+
!m.hidden && !r.includes(m) && !r.includes(h) && (m.hidden = !0), !d.hidden && !r.includes(d) && !r.includes(b) && (d.hidden = !0);
|
|
82
82
|
}), s.vrSupported().then((i) => {
|
|
83
83
|
i && (Object.assign(s.vrButton.style, { position: "static", left: "auto", bottom: "auto", transform: "none", margin: "0" }), k.append(s.vrButton));
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
dispose() {
|
|
87
|
-
for (const
|
|
88
|
-
for (const
|
|
87
|
+
for (const t of this.disposers) t();
|
|
88
|
+
for (const t of this.nodes) t.remove();
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
function
|
|
92
|
-
const
|
|
93
|
-
return
|
|
91
|
+
function N(l, t, s) {
|
|
92
|
+
const e = document.createDocumentFragment();
|
|
93
|
+
return e.append(l, t, s), e;
|
|
94
94
|
}
|
|
95
|
-
const
|
|
95
|
+
const O = ":host,:host *,.tvp,.tvp *{box-sizing:border-box}.tvp-canvas{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;display:block;touch-action:none;cursor:grab}.tvp-canvas:active{cursor:grabbing}.tvp-video{display:none}.tvp-controls{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:12px;background:linear-gradient(transparent,#000c);z-index:5;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed}.tvp-btn{padding:4px;border:0;background:none;color:#e8eaed;cursor:pointer;font-size:22px;line-height:1}.tvp-btn:hover{opacity:.7}.tvp-seek{flex:1;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:14px;background:transparent;cursor:pointer;margin:0}.tvp-seek::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(to right,#4f8cff var(--seek,0%),#3a4150 var(--seek,0%))}.tvp-seek::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-seek::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-seek::-moz-range-progress{height:4px;border-radius:2px;background:#4f8cff}.tvp-seek::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-time{font-variant-numeric:tabular-nums;min-width:96px;text-align:center;color:#cfd6e2}.tvp-volwrap,.tvp-projwrap{position:relative;display:inline-flex}.tvp-volpopup{position:absolute;bottom:calc(100% + 10px);left:50%;transform:translate(-50%);width:40px;height:132px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-volpopup[hidden]{display:none}.tvp-volume{position:absolute;top:50%;left:50%;width:104px;height:14px;margin:0;transform:translate(-50%,-50%) rotate(-90deg);-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;cursor:pointer}.tvp-volume::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-top:-4px;width:12px;height:12px;border-radius:50%;background:#fff;cursor:pointer}.tvp-volume::-moz-range-track{height:4px;border-radius:2px;background:#3a4150}.tvp-volume::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:#fff;cursor:pointer}.tvp-projmenu{position:absolute;bottom:calc(100% + 8px);right:0;min-width:210px;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:10px;padding:6px;display:flex;flex-direction:column;gap:2px;z-index:8;box-shadow:0 8px 24px #0009}.tvp-projmenu[hidden]{display:none}.tvp-projmenu button{text-align:left;background:transparent;border:0;color:#e8eaed;padding:8px 10px;border-radius:7px;cursor:pointer;font:inherit;white-space:nowrap}.tvp-projmenu button:hover{background:#252b39}.tvp-projmenu button.active{background:#4f8cff;color:#fff}.tvp-vrslot button{position:static!important}.tvp-settings{position:absolute;bottom:62px;right:10px;width:300px;max-width:calc(100% - 20px);max-height:70%;overflow-y:auto;background:#121418eb;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid #2a2f3a;border-radius:12px;padding:14px;display:none;flex-direction:column;gap:12px;z-index:6;font:14px system-ui,Segoe UI,Roboto,sans-serif;color:#e8eaed}.tvp-settings.open{display:flex}.tvp-settings label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:#aab2c0}.tvp-settings label.row{flex-direction:row;align-items:center;gap:8px;color:#e8eaed;font-size:13px}.tvp-settings input[type=range]{width:100%;accent-color:#4f8cff}.tvp-toast{position:absolute;left:50%;bottom:74px;transform:translate(-50%);max-width:80%;padding:10px 16px;border-radius:10px;background:#1b1f2aed;border:1px solid #2a2f3a;color:#e8eaed;opacity:0;pointer-events:none;transition:opacity .2s;z-index:7;font:13px system-ui}.tvp-toast.show{opacity:1}", A = "three-vr-player:settings";
|
|
96
96
|
class W {
|
|
97
|
-
constructor(
|
|
98
|
-
this.source = new
|
|
99
|
-
const
|
|
97
|
+
constructor(t, s = {}) {
|
|
98
|
+
this.source = new R(), this.listeners = /* @__PURE__ */ new Map(), this.readyEmitted = !1, this.native = !1, this.loading = !1, this.opts = s;
|
|
99
|
+
const e = s.persistSettings ? this.loadSettings() : null;
|
|
100
100
|
this.view = {
|
|
101
|
-
projection: s.projection ?? (
|
|
102
|
-
swapEyes: s.swapEyes ?? (
|
|
103
|
-
fov: s.fov ?? (
|
|
104
|
-
supersampling: s.supersampling ?? (
|
|
105
|
-
}, this.wrap = document.createElement("div"), this.wrap.className = "tvp", this.wrap.style.cssText = "position:relative;width:100%;height:100%;overflow:hidden;",
|
|
101
|
+
projection: s.projection ?? (e == null ? void 0 : e.projection) ?? "180-sbs",
|
|
102
|
+
swapEyes: s.swapEyes ?? (e == null ? void 0 : e.swapEyes) ?? !1,
|
|
103
|
+
fov: s.fov ?? (e == null ? void 0 : e.fov) ?? 70,
|
|
104
|
+
supersampling: s.supersampling ?? (e == null ? void 0 : e.supersampling) ?? 1.5
|
|
105
|
+
}, this.wrap = document.createElement("div"), this.wrap.className = "tvp", this.wrap.style.cssText = "position:relative;width:100%;height:100%;overflow:hidden;", t.appendChild(this.wrap);
|
|
106
106
|
let a;
|
|
107
107
|
if (s.shadowDom !== !1) {
|
|
108
|
-
const o = this.wrap.attachShadow({ mode: "open" }),
|
|
109
|
-
|
|
108
|
+
const o = this.wrap.attachShadow({ mode: "open" }), c = document.createElement("style");
|
|
109
|
+
c.textContent = O, o.appendChild(c), a = o;
|
|
110
110
|
} else {
|
|
111
111
|
if (!document.getElementById("tvp-styles")) {
|
|
112
112
|
const o = document.createElement("style");
|
|
113
|
-
o.id = "tvp-styles", o.textContent =
|
|
113
|
+
o.id = "tvp-styles", o.textContent = O, document.head.appendChild(o);
|
|
114
114
|
}
|
|
115
115
|
a = this.wrap;
|
|
116
116
|
}
|
|
117
|
-
this.canvas = document.createElement("canvas"), this.canvas.className = "tvp-canvas", this.video = document.createElement("video"), this.video.className = "tvp-video", this.video.playsInline = !0, s.crossOrigin !== null && (this.video.crossOrigin = s.crossOrigin ?? "anonymous"), a.append(this.canvas, this.video), this.scene = new
|
|
117
|
+
this.canvas = document.createElement("canvas"), this.canvas.className = "tvp-canvas", this.video = document.createElement("video"), this.video.className = "tvp-video", this.video.playsInline = !0, s.crossOrigin !== null && (this.video.crossOrigin = s.crossOrigin ?? "anonymous"), a.append(this.canvas, this.video), this.scene = new $({
|
|
118
118
|
canvas: this.canvas,
|
|
119
119
|
video: this.video,
|
|
120
120
|
projection: this.view.projection,
|
|
@@ -133,80 +133,106 @@ class W {
|
|
|
133
133
|
setFov: (o) => this.setFov(o),
|
|
134
134
|
setSupersampling: (o) => this.setSupersampling(o),
|
|
135
135
|
initial: { swapEyes: this.view.swapEyes, fov: this.view.fov, supersampling: this.view.supersampling }
|
|
136
|
-
})), this.video.addEventListener("play", () => this.emit("play")), this.video.addEventListener("pause", () => this.emit("pause")), this.video.addEventListener("ended", () => this.emit("ended")), this.video.addEventListener("timeupdate", () => this.emit("timeupdate", this.video.currentTime)), this.video.addEventListener("error", () =>
|
|
136
|
+
})), this.video.addEventListener("play", () => this.emit("play")), this.video.addEventListener("pause", () => this.emit("pause")), this.video.addEventListener("ended", () => this.emit("ended")), this.video.addEventListener("timeupdate", () => this.emit("timeupdate", this.video.currentTime)), this.video.addEventListener("error", () => {
|
|
137
|
+
this.loading || this.emit("error", this.video.error);
|
|
138
|
+
}), this.video.addEventListener("loadedmetadata", () => {
|
|
137
139
|
this.readyEmitted || (this.readyEmitted = !0, this.emit("ready"));
|
|
138
140
|
}), this.scene.renderer.xr.addEventListener("sessionstart", () => this.emit("enterxr")), this.scene.renderer.xr.addEventListener("sessionend", () => this.emit("exitxr")), s.src && this.load(s.src, { projection: s.projection });
|
|
139
141
|
}
|
|
140
142
|
vrSupported() {
|
|
141
143
|
return this.opts.vrButton === !1 || !navigator.xr ? Promise.resolve(!1) : navigator.xr.isSessionSupported("immersive-vr").catch(() => !1);
|
|
142
144
|
}
|
|
143
|
-
async load(
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
const { url: a, format: o } = U(
|
|
145
|
+
async load(t, s = {}) {
|
|
146
|
+
const e = s.projection ?? (this.opts.autoDetect !== !1 ? D(t) : null);
|
|
147
|
+
e && this.setProjection(e);
|
|
148
|
+
const { url: a, format: o } = U(t, this.opts.proxy), c = this.opts.crossOrigin === void 0 ? "anonymous" : this.opts.crossOrigin;
|
|
149
|
+
this.loading = !0;
|
|
147
150
|
try {
|
|
148
|
-
await this.source.attach(this.video, { url: a, format: o }), await this.video.play().catch(() => {
|
|
151
|
+
await this.source.attach(this.video, { url: a, format: o }, { crossOrigin: c }), this.setNativeFallback(!1), await this.video.play().catch(() => {
|
|
149
152
|
});
|
|
150
|
-
} catch (
|
|
151
|
-
|
|
153
|
+
} catch (h) {
|
|
154
|
+
if (this.opts.nativeFallback !== !1 && o === "progressive" && c !== null)
|
|
155
|
+
try {
|
|
156
|
+
this.setNativeFallback(!0), await this.source.attach(this.video, { url: a, format: o }, { crossOrigin: null }), await this.video.play().catch(() => {
|
|
157
|
+
}), this.emit("fallback");
|
|
158
|
+
return;
|
|
159
|
+
} catch (v) {
|
|
160
|
+
throw this.setNativeFallback(!1), this.emit("error", v), v;
|
|
161
|
+
}
|
|
162
|
+
throw this.setNativeFallback(!1), this.emit("error", h), h;
|
|
163
|
+
} finally {
|
|
164
|
+
this.loading = !1;
|
|
152
165
|
}
|
|
153
166
|
}
|
|
167
|
+
/** Swap between WebGL (3D) rendering and plain 2D `<video>` playback. */
|
|
168
|
+
setNativeFallback(t) {
|
|
169
|
+
t !== this.native && (this.native = t, t ? (this.scene.pauseRendering(), this.canvas.style.display = "none", Object.assign(this.video.style, {
|
|
170
|
+
display: "block",
|
|
171
|
+
position: "absolute",
|
|
172
|
+
inset: "0",
|
|
173
|
+
width: "100%",
|
|
174
|
+
height: "100%",
|
|
175
|
+
objectFit: "contain",
|
|
176
|
+
background: "#000",
|
|
177
|
+
zIndex: "1"
|
|
178
|
+
})) : (this.canvas.style.display = "", this.video.style.cssText = "", this.scene.resumeRendering()));
|
|
179
|
+
}
|
|
154
180
|
async play() {
|
|
155
181
|
await this.video.play();
|
|
156
182
|
}
|
|
157
183
|
pause() {
|
|
158
184
|
this.video.pause();
|
|
159
185
|
}
|
|
160
|
-
setProjection(
|
|
161
|
-
this.scene.setProjection(
|
|
186
|
+
setProjection(t) {
|
|
187
|
+
this.scene.setProjection(t), this.look.setEnabled(!this.scene.isFlat()), this.look.reset(), this.view.projection = t, this.persist(), this.emit("projectionchange", t);
|
|
162
188
|
}
|
|
163
|
-
setSwapEyes(
|
|
164
|
-
this.scene.setSwapEyes(
|
|
189
|
+
setSwapEyes(t) {
|
|
190
|
+
this.scene.setSwapEyes(t), this.view.swapEyes = t, this.persist();
|
|
165
191
|
}
|
|
166
|
-
setFov(
|
|
167
|
-
this.scene.setFov(
|
|
192
|
+
setFov(t) {
|
|
193
|
+
this.scene.setFov(t), this.view.fov = t, this.persist();
|
|
168
194
|
}
|
|
169
|
-
setSupersampling(
|
|
170
|
-
this.scene.setSupersampling(
|
|
195
|
+
setSupersampling(t) {
|
|
196
|
+
this.scene.setSupersampling(t), this.view.supersampling = t, this.persist();
|
|
171
197
|
}
|
|
172
198
|
async enterVR() {
|
|
173
199
|
var s;
|
|
174
|
-
const
|
|
175
|
-
(s =
|
|
200
|
+
const t = this.scene.vrButton;
|
|
201
|
+
(s = t == null ? void 0 : t.click) == null || s.call(t);
|
|
176
202
|
}
|
|
177
203
|
get three() {
|
|
178
204
|
return { renderer: this.scene.renderer, scene: this.scene.scene, camera: this.scene.camera };
|
|
179
205
|
}
|
|
180
|
-
on(
|
|
181
|
-
let
|
|
182
|
-
return
|
|
206
|
+
on(t, s) {
|
|
207
|
+
let e = this.listeners.get(t);
|
|
208
|
+
return e || (e = /* @__PURE__ */ new Set(), this.listeners.set(t, e)), e.add(s), this;
|
|
183
209
|
}
|
|
184
|
-
off(
|
|
185
|
-
var
|
|
186
|
-
return (
|
|
210
|
+
off(t, s) {
|
|
211
|
+
var e;
|
|
212
|
+
return (e = this.listeners.get(t)) == null || e.delete(s), this;
|
|
187
213
|
}
|
|
188
|
-
emit(
|
|
189
|
-
var
|
|
190
|
-
(
|
|
214
|
+
emit(t, s) {
|
|
215
|
+
var e;
|
|
216
|
+
(e = this.listeners.get(t)) == null || e.forEach((a) => a(s));
|
|
191
217
|
}
|
|
192
218
|
persist() {
|
|
193
219
|
if (this.opts.persistSettings)
|
|
194
220
|
try {
|
|
195
|
-
localStorage.setItem(
|
|
221
|
+
localStorage.setItem(A, JSON.stringify(this.view));
|
|
196
222
|
} catch {
|
|
197
223
|
}
|
|
198
224
|
}
|
|
199
225
|
loadSettings() {
|
|
200
226
|
try {
|
|
201
|
-
const
|
|
202
|
-
return
|
|
227
|
+
const t = localStorage.getItem(A);
|
|
228
|
+
return t ? JSON.parse(t) : null;
|
|
203
229
|
} catch {
|
|
204
230
|
return null;
|
|
205
231
|
}
|
|
206
232
|
}
|
|
207
233
|
dispose() {
|
|
208
|
-
var
|
|
209
|
-
(
|
|
234
|
+
var t;
|
|
235
|
+
(t = this.ui) == null || t.dispose(), this.look.dispose(), this.source.dispose(), this.scene.dispose(), this.video.remove(), this.canvas.remove(), this.wrap.remove(), this.listeners.clear();
|
|
210
236
|
}
|
|
211
237
|
}
|
|
212
238
|
class q extends HTMLElement {
|
|
@@ -225,15 +251,15 @@ class q extends HTMLElement {
|
|
|
225
251
|
proxy: this.getAttribute("proxy-url") ? { url: this.getAttribute("proxy-url"), apiPassword: this.getAttribute("proxy-password") ?? void 0 } : void 0
|
|
226
252
|
}));
|
|
227
253
|
}
|
|
228
|
-
attributeChangedCallback(
|
|
229
|
-
!this.player ||
|
|
254
|
+
attributeChangedCallback(t, s, e) {
|
|
255
|
+
!this.player || e == null || (t === "src" && this.player.load(e), t === "projection" && this.player.setProjection(e));
|
|
230
256
|
}
|
|
231
257
|
disconnectedCallback() {
|
|
232
|
-
var
|
|
233
|
-
(
|
|
258
|
+
var t;
|
|
259
|
+
(t = this.player) == null || t.dispose(), this.player = void 0;
|
|
234
260
|
}
|
|
235
|
-
numAttr(
|
|
236
|
-
const s = this.getAttribute(
|
|
261
|
+
numAttr(t) {
|
|
262
|
+
const s = this.getAttribute(t);
|
|
237
263
|
return s == null ? void 0 : Number(s);
|
|
238
264
|
}
|
|
239
265
|
/** The underlying Player instance (for programmatic control). */
|
|
@@ -241,18 +267,18 @@ class q extends HTMLElement {
|
|
|
241
267
|
return this.player;
|
|
242
268
|
}
|
|
243
269
|
}
|
|
244
|
-
function Y(
|
|
245
|
-
typeof customElements < "u" && !customElements.get(
|
|
270
|
+
function Y(l = "three-video") {
|
|
271
|
+
typeof customElements < "u" && !customElements.get(l) && customElements.define(l, q);
|
|
246
272
|
}
|
|
247
273
|
Y();
|
|
248
274
|
export {
|
|
249
275
|
V as LookControls,
|
|
250
276
|
G as MODES,
|
|
251
|
-
|
|
277
|
+
I as PROJECTIONS,
|
|
252
278
|
W as Player,
|
|
253
|
-
|
|
279
|
+
$ as StereoScene,
|
|
254
280
|
q as ThreeVideoElement,
|
|
255
|
-
|
|
281
|
+
R as VideoSource,
|
|
256
282
|
U as buildProxyUrl,
|
|
257
283
|
Q as clampAngles,
|
|
258
284
|
X as detectFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"three-vr-player.js","sources":["../src/ui/format.ts","../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["/** Format seconds as m:ss or h:mm:ss. */\r\nexport function formatTime(seconds: number): string {\r\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\r\n const s = Math.floor(seconds % 60);\r\n const m = Math.floor((seconds / 60) % 60);\r\n const h = Math.floor(seconds / 3600);\r\n const ss = String(s).padStart(2, '0');\r\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;\r\n}\r\n","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 vrButton: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n getProjection(): Projection;\r\n setProjection(p: Projection): 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}\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 slot ---\r\n const vrSlot = h('span', { class: 'tvp-vrslot' });\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 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, vrSlot, 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 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 ]);\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\r\n on(play, 'click', () => { 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 on(mute, 'click', (e: Event) => { e.stopPropagation(); volPopup.hidden = !volPopup.hidden; });\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 on(projBtn, 'click', (e: Event) => { e.stopPropagation(); projMenu.hidden = !projMenu.hidden; });\r\n on(projMenu, 'click', (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); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n on(settingsBtn, 'click', (e: Event) => { e.stopPropagation(); settings.classList.toggle('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 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 on(fullscreen, 'click', () => {\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-click closes the popups (composedPath crosses the shadow boundary)\r\n on(document, 'click', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n if (!volPopup.hidden && !path.includes(volPopup) && !path.includes(mute)) volPopup.hidden = true;\r\n if (!projMenu.hidden && !path.includes(projMenu) && !path.includes(projBtn)) projMenu.hidden = true;\r\n });\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => {\r\n if (ok) {\r\n Object.assign(bridge.vrButton.style, { position: 'static', left: 'auto', bottom: 'auto', transform: 'none', margin: '0' });\r\n vrSlot.append(bridge.vrButton);\r\n }\r\n });\r\n\r\n // silence unused-var lint for the toast helper (available for future use)\r\n void toast;\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 } 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\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\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\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 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 vrButton: this.scene.vrButton,\r\n vrSupported: () => this.vrSupported(),\r\n getProjection: () => this.scene.getProjection(),\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 });\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', () => 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\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 async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.setProjection(proj);\r\n const { url, format } = buildProxyUrl(src, this.opts.proxy);\r\n try {\r\n await this.source.attach(this.video, { url, format });\r\n await this.video.play().catch(() => { /* autoplay may be blocked until a user gesture */ });\r\n } catch (err) { this.emit('error', err); throw err; }\r\n }\r\n\r\n async play() { await this.video.play(); }\r\n pause() { this.video.pause(); }\r\n setProjection(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 this.emit('projectionchange', p);\r\n }\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\r\n async enterVR() {\r\n const btn = this.scene.vrButton as HTMLButtonElement;\r\n btn?.click?.();\r\n }\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":["formatTime","seconds","s","m","h","ss","tag","props","children","el","cls","rest","c","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrSlot","projBtn","projMenu","p","PROJECTIONS","b","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","e","updateProjection","mode","label","on2","d","x","next","_b","_a","path","ok","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","src","o","proj","detectProjection","url","format","buildProxyUrl","err","deg","btn","cb","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"three-vr-player.js","sources":["../src/ui/format.ts","../src/ui/ControlsUI.ts","../src/Player.ts","../src/webcomponent.ts","../src/index.ts"],"sourcesContent":["/** Format seconds as m:ss or h:mm:ss. */\r\nexport function formatTime(seconds: number): string {\r\n if (!Number.isFinite(seconds) || seconds < 0) return '0:00';\r\n const s = Math.floor(seconds % 60);\r\n const m = Math.floor((seconds / 60) % 60);\r\n const h = Math.floor(seconds / 3600);\r\n const ss = String(s).padStart(2, '0');\r\n return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;\r\n}\r\n","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 vrButton: HTMLElement;\r\n vrSupported(): Promise<boolean>;\r\n getProjection(): Projection;\r\n setProjection(p: Projection): 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}\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 slot ---\r\n const vrSlot = h('span', { class: 'tvp-vrslot' });\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 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, vrSlot, 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 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 ]);\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\r\n on(play, 'click', () => { 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 on(mute, 'click', (e: Event) => { e.stopPropagation(); volPopup.hidden = !volPopup.hidden; });\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 on(projBtn, 'click', (e: Event) => { e.stopPropagation(); projMenu.hidden = !projMenu.hidden; });\r\n on(projMenu, 'click', (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); updateProjection(); projMenu.hidden = true; }\r\n });\r\n updateProjection();\r\n\r\n on(settingsBtn, 'click', (e: Event) => { e.stopPropagation(); settings.classList.toggle('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 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 on(fullscreen, 'click', () => {\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-click closes the popups (composedPath crosses the shadow boundary)\r\n on(document, 'click', (e: Event) => {\r\n const path = (e as Event & { composedPath(): EventTarget[] }).composedPath();\r\n if (!volPopup.hidden && !path.includes(volPopup) && !path.includes(mute)) volPopup.hidden = true;\r\n if (!projMenu.hidden && !path.includes(projMenu) && !path.includes(projBtn)) projMenu.hidden = true;\r\n });\r\n\r\n // VR button only when an immersive-VR device is available\r\n void bridge.vrSupported().then((ok) => {\r\n if (ok) {\r\n Object.assign(bridge.vrButton.style, { position: 'static', left: 'auto', bottom: 'auto', transform: 'none', margin: '0' });\r\n vrSlot.append(bridge.vrButton);\r\n }\r\n });\r\n\r\n // silence unused-var lint for the toast helper (available for future use)\r\n void toast;\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 } 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 loading = false;\r\n\r\n constructor(container: HTMLElement, options: PlayerOptions = {}) {\r\n this.opts = options;\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\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 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 vrButton: this.scene.vrButton,\r\n vrSupported: () => this.vrSupported(),\r\n getProjection: () => this.scene.getProjection(),\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 });\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\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 async load(src: string, o: { projection?: Projection } = {}): Promise<void> {\r\n const proj = o.projection ?? (this.opts.autoDetect !== false ? detectProjection(src) : null);\r\n if (proj) this.setProjection(proj);\r\n const { url, format } = buildProxyUrl(src, this.opts.proxy);\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.setNativeFallback(false);\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.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.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 setProjection(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 this.emit('projectionchange', p);\r\n }\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\r\n async enterVR() {\r\n const btn = this.scene.vrButton as HTMLButtonElement;\r\n btn?.click?.();\r\n }\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":["formatTime","seconds","s","m","h","ss","tag","props","children","el","cls","rest","ControlsUI","root","bridge","v","play","seek","time","mute","volume","volPopup","volWrap","vrSlot","projBtn","projMenu","p","PROJECTIONS","b","projWrap","settingsBtn","fullscreen","controls","swapCb","fovRange","fovVal","ssRange","ssVal","settings","makeText","toast","n","on","t","ev","fn","opts","setSeekFill","pct","e","updateProjection","mode","label","on2","d","x","next","_b","_a","path","ok","prefix","valueSpan","suffix","frag","STORE_KEY","Player","container","options","VideoSource","stored","shadow","style","css","StereoScene","LookControls","src","o","proj","detectProjection","url","format","buildProxyUrl","primaryCO","err","err2","deg","btn","cb","raw","ThreeVideoElement","name","_old","value","registerWebComponent"],"mappings":"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"}
|