rvms-vue 0.1.13 → 0.1.14
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/dist/lib/index.js +180 -177
- package/dist/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/components/RvmsVideo.vue +3 -0
- package/src/components/RvmsVideoPlayer.vue +1 -0
package/dist/lib/index.js
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { defineComponent as J, ref as S, onMounted as
|
|
2
|
-
const
|
|
1
|
+
import { defineComponent as J, ref as S, onMounted as G, onBeforeUnmount as q, watch as K, openBlock as c, createElementBlock as d, normalizeStyle as P, createElementVNode as i, toDisplayString as _, createCommentVNode as B, createTextVNode as X, Fragment as Z, renderList as ee } from "vue";
|
|
2
|
+
const te = {
|
|
3
3
|
width: "64",
|
|
4
4
|
height: "64",
|
|
5
5
|
viewBox: "0 0 64 64",
|
|
6
6
|
fill: "none",
|
|
7
7
|
style: { opacity: "0.8" }
|
|
8
|
-
},
|
|
8
|
+
}, ne = {
|
|
9
9
|
key: 1,
|
|
10
10
|
class: "rvms-overlay"
|
|
11
|
-
},
|
|
11
|
+
}, re = {
|
|
12
12
|
key: 2,
|
|
13
13
|
class: "rvms-overlay"
|
|
14
|
-
},
|
|
14
|
+
}, oe = {
|
|
15
15
|
key: 3,
|
|
16
16
|
class: "rvms-overlay"
|
|
17
|
-
},
|
|
17
|
+
}, ae = {
|
|
18
18
|
key: 0,
|
|
19
19
|
style: { color: "#fca5a5", "font-size": "0.8em", "text-align": "center", padding: "0 1em", "max-width": "80%" }
|
|
20
|
-
},
|
|
20
|
+
}, le = {
|
|
21
21
|
key: 4,
|
|
22
22
|
class: "rvms-overlay"
|
|
23
|
-
},
|
|
23
|
+
}, se = { style: { position: "absolute", top: "8px", left: "8px", display: "flex", gap: "6px", "align-items": "center", "pointer-events": "none" } }, ie = {
|
|
24
24
|
key: 1,
|
|
25
25
|
style: { padding: "2px 6px", "border-radius": "4px", "font-size": "10px", "font-family": "monospace", background: "rgba(51, 65, 85, 0.8)", color: "#cbd5e1" }
|
|
26
|
-
},
|
|
26
|
+
}, ue = {
|
|
27
27
|
key: 2,
|
|
28
28
|
style: { display: "inline-flex", "align-items": "center", gap: "4px", padding: "2px 8px", "border-radius": "4px", "font-size": "10px", background: "#065f46", color: "#6ee7b7" }
|
|
29
|
-
},
|
|
29
|
+
}, ce = {
|
|
30
30
|
key: 5,
|
|
31
31
|
style: { position: "absolute", bottom: "12px", right: "12px", display: "flex", gap: "6px" }
|
|
32
|
-
},
|
|
32
|
+
}, de = ["title"], fe = {
|
|
33
33
|
key: 6,
|
|
34
34
|
style: { position: "absolute", bottom: "48px", left: "8px", right: "8px", "max-height": "140px", "overflow-y": "auto", display: "flex", "flex-direction": "column", gap: "4px" }
|
|
35
|
-
},
|
|
35
|
+
}, pe = ["title", "onClick"], ve = ["src"], me = { style: { flex: "1", "min-width": "0" } }, ge = { style: { display: "flex", gap: "6px", "align-items": "baseline" } }, ye = { style: { color: "#fbbf24", "font-weight": "600", "text-transform": "uppercase", "font-size": "10px" } }, he = {
|
|
36
36
|
key: 0,
|
|
37
37
|
style: { color: "#94a3b8" }
|
|
38
|
-
},
|
|
38
|
+
}, be = { style: { color: "#94a3b8", "font-size": "10px", "white-space": "nowrap", overflow: "hidden", "text-overflow": "ellipsis" } }, He = /* @__PURE__ */ J({
|
|
39
39
|
__name: "RvmsVideoPlayer",
|
|
40
40
|
props: {
|
|
41
41
|
nvrId: {},
|
|
@@ -50,27 +50,27 @@ const ee = {
|
|
|
50
50
|
token: {}
|
|
51
51
|
},
|
|
52
52
|
emits: ["alarm", "stream-status", "alarm-click"],
|
|
53
|
-
setup(C, { expose: U, emit:
|
|
54
|
-
const s = C, f =
|
|
53
|
+
setup(C, { expose: U, emit: k }) {
|
|
54
|
+
const s = C, f = k, l = S(null), a = S("idle"), m = S(null), w = S(null), b = S(!1), I = S(s.initialProfile);
|
|
55
55
|
function E() {
|
|
56
56
|
I.value = I.value === "main" ? "sub" : "main", b.value && u();
|
|
57
57
|
}
|
|
58
|
-
const L = S(null),
|
|
58
|
+
const L = S(null), O = S([]);
|
|
59
59
|
function R(e) {
|
|
60
60
|
return `${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}${e}`;
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
62
|
+
function M() {
|
|
63
|
+
Q();
|
|
64
64
|
const e = new WebSocket(s.token ? `/ws/alarms?token=${encodeURIComponent(s.token)}` : R("/ws/alarms"));
|
|
65
65
|
L.value = e, e.onmessage = (t) => {
|
|
66
66
|
try {
|
|
67
67
|
const r = JSON.parse(t.data);
|
|
68
68
|
switch (r.type) {
|
|
69
69
|
case "snapshot":
|
|
70
|
-
r.events && (
|
|
70
|
+
r.events && (O.value = r.events, r.events.forEach((p) => f("alarm", p)));
|
|
71
71
|
break;
|
|
72
72
|
case "alarm":
|
|
73
|
-
r.event && (
|
|
73
|
+
r.event && (O.value.unshift(r.event), O.value.length > 50 && (O.value.length = 50), f("alarm", r.event));
|
|
74
74
|
break;
|
|
75
75
|
default:
|
|
76
76
|
break;
|
|
@@ -78,11 +78,11 @@ const ee = {
|
|
|
78
78
|
} catch {
|
|
79
79
|
}
|
|
80
80
|
}, e.onclose = () => {
|
|
81
|
-
L.value = null, setTimeout(
|
|
81
|
+
L.value = null, setTimeout(M, 3e3);
|
|
82
82
|
}, e.onerror = () => {
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
-
function
|
|
85
|
+
function Q() {
|
|
86
86
|
if (L.value) {
|
|
87
87
|
try {
|
|
88
88
|
L.value.onclose = null, L.value.close();
|
|
@@ -91,20 +91,20 @@ const ee = {
|
|
|
91
91
|
L.value = null;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
async function
|
|
94
|
+
async function T() {
|
|
95
95
|
try {
|
|
96
96
|
const e = {};
|
|
97
97
|
s.token && (e.Authorization = `Bearer ${s.token}`);
|
|
98
98
|
const t = await fetch("/api/alarms/recent", { headers: e });
|
|
99
99
|
if (t.ok) {
|
|
100
100
|
const r = await t.json();
|
|
101
|
-
|
|
101
|
+
O.value = r;
|
|
102
102
|
}
|
|
103
103
|
} catch {
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
let A = null;
|
|
107
|
-
function
|
|
107
|
+
function j() {
|
|
108
108
|
return {
|
|
109
109
|
cancelled: !1,
|
|
110
110
|
ws: null,
|
|
@@ -147,120 +147,121 @@ const ee = {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
function
|
|
150
|
+
function D(e) {
|
|
151
151
|
if (!l.value) return;
|
|
152
152
|
if (!("MediaSource" in window)) {
|
|
153
153
|
m.value = "MediaSource Extensions not supported", a.value = "error", f("stream-status", "error");
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
156
|
a.value = "connecting", m.value = null, w.value = null, f("stream-status", "connecting");
|
|
157
|
-
const t =
|
|
157
|
+
const t = j();
|
|
158
158
|
A = t;
|
|
159
159
|
const r = new MediaSource();
|
|
160
160
|
t.mediaSource = r, l.value.src = URL.createObjectURL(r);
|
|
161
161
|
const p = () => {
|
|
162
|
-
t.cancelled || t.mediaSource !== r || (a.value = "buffering", f("stream-status", "buffering"),
|
|
162
|
+
t.cancelled || t.mediaSource !== r || (a.value = "buffering", f("stream-status", "buffering"), V(t));
|
|
163
163
|
};
|
|
164
164
|
t.sourceOpenHandler = p, r.addEventListener("sourceopen", p);
|
|
165
|
-
const
|
|
166
|
-
|
|
165
|
+
const g = new WebSocket(e);
|
|
166
|
+
g.binaryType = "arraybuffer", t.ws = g, g.onmessage = (h) => {
|
|
167
167
|
if (t.cancelled) return;
|
|
168
|
-
if (typeof
|
|
168
|
+
if (typeof h.data == "string") {
|
|
169
169
|
try {
|
|
170
|
-
const
|
|
171
|
-
|
|
170
|
+
const z = JSON.parse(h.data);
|
|
171
|
+
z.type === "error" && z.message && (m.value = z.message, a.value = "error", f("stream-status", "error"), $());
|
|
172
172
|
} catch {
|
|
173
173
|
}
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
|
-
const
|
|
176
|
+
const x = new Uint8Array(h.data);
|
|
177
177
|
if (!t.sourceBuffer) {
|
|
178
|
-
t.preBuffer.push(
|
|
178
|
+
t.preBuffer.push(x), V(t);
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
181
|
t.appendQueue.push(
|
|
182
|
-
|
|
182
|
+
x.buffer.slice(x.byteOffset, x.byteOffset + x.byteLength)
|
|
183
183
|
), o(t);
|
|
184
|
-
},
|
|
184
|
+
}, g.onerror = () => {
|
|
185
185
|
t.cancelled || a.value !== "error" && (m.value = "WebSocket error", a.value = "error", f("stream-status", "error"));
|
|
186
|
-
},
|
|
187
|
-
t.cancelled || (a.value === "connecting" || a.value === "buffering" ? (m.value =
|
|
186
|
+
}, g.onclose = (h) => {
|
|
187
|
+
t.cancelled || (a.value === "connecting" || a.value === "buffering" ? (m.value = h.reason || `Stream closed (code ${h.code})`, a.value = "error", f("stream-status", "error")) : a.value === "playing" && (a.value = "ended", f("stream-status", "ended")));
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
|
-
function
|
|
190
|
+
function W(e) {
|
|
191
191
|
const t = (r) => r.toString(16).padStart(2, "0").toUpperCase();
|
|
192
192
|
for (let r = 0; r + 7 < e.length; r++) {
|
|
193
193
|
const p = String.fromCharCode(e[r], e[r + 1], e[r + 2], e[r + 3]);
|
|
194
194
|
if (p === "avcC") {
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
197
|
-
return `avc1.${t(
|
|
195
|
+
const g = e[r + 5], h = e[r + 6], x = e[r + 7];
|
|
196
|
+
if (g !== void 0 && h !== void 0 && x !== void 0)
|
|
197
|
+
return `avc1.${t(g)}${t(h)}${t(x)}`;
|
|
198
198
|
}
|
|
199
199
|
if (p === "hvcC") return "hev1.1.6.L93.B0";
|
|
200
200
|
}
|
|
201
201
|
return null;
|
|
202
202
|
}
|
|
203
|
-
function
|
|
203
|
+
function N(e) {
|
|
204
204
|
for (let t = 0; t + 3 < e.length; t++)
|
|
205
205
|
if (e[t] === 101 && e[t + 1] === 115 && e[t + 2] === 100 && e[t + 3] === 115)
|
|
206
206
|
return !0;
|
|
207
207
|
return !1;
|
|
208
208
|
}
|
|
209
|
-
function
|
|
209
|
+
function V(e) {
|
|
210
210
|
if (e.cancelled || e.sourceBuffer || !e.mediaSource || e.mediaSource.readyState !== "open") return;
|
|
211
|
-
const t = n(e.preBuffer), r =
|
|
211
|
+
const t = n(e.preBuffer), r = W(t);
|
|
212
212
|
if (!r) return;
|
|
213
|
-
const p =
|
|
214
|
-
w.value =
|
|
215
|
-
let
|
|
216
|
-
if (!MediaSource.isTypeSupported(
|
|
217
|
-
m.value = `Codec not supported: ${
|
|
213
|
+
const p = N(t), g = p ? `${r}, mp4a.40.2` : r;
|
|
214
|
+
w.value = g;
|
|
215
|
+
let h = `video/mp4; codecs="${g}"`;
|
|
216
|
+
if (!MediaSource.isTypeSupported(h) && p && (h = `video/mp4; codecs="${r}"`, w.value = r), !MediaSource.isTypeSupported(h)) {
|
|
217
|
+
m.value = `Codec not supported: ${h}`, a.value = "error", $();
|
|
218
218
|
return;
|
|
219
219
|
}
|
|
220
|
-
let
|
|
220
|
+
let x;
|
|
221
221
|
try {
|
|
222
|
-
|
|
223
|
-
} catch (
|
|
224
|
-
m.value = `addSourceBuffer failed: ${
|
|
222
|
+
x = e.mediaSource.addSourceBuffer(h);
|
|
223
|
+
} catch (H) {
|
|
224
|
+
m.value = `addSourceBuffer failed: ${H.message}`, a.value = "error", $();
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
|
-
|
|
228
|
-
const
|
|
227
|
+
x.mode = "segments";
|
|
228
|
+
const z = () => {
|
|
229
229
|
e.cancelled || o(e);
|
|
230
230
|
};
|
|
231
|
-
e.updateEndHandler =
|
|
231
|
+
e.updateEndHandler = z, x.addEventListener("updateend", z), x.addEventListener("error", () => {
|
|
232
232
|
e.cancelled || (m.value = "SourceBuffer error", a.value = "error");
|
|
233
|
-
}), e.sourceBuffer =
|
|
233
|
+
}), e.sourceBuffer = x, e.appendQueue.unshift(t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength)), e.preBuffer.length = 0, o(e);
|
|
234
234
|
}
|
|
235
235
|
function n(e) {
|
|
236
|
-
const t = e.reduce((
|
|
236
|
+
const t = e.reduce((g, h) => g + h.byteLength, 0), r = new Uint8Array(t);
|
|
237
237
|
let p = 0;
|
|
238
|
-
for (const
|
|
239
|
-
r.set(
|
|
238
|
+
for (const g of e)
|
|
239
|
+
r.set(g, p), p += g.byteLength;
|
|
240
240
|
return r;
|
|
241
241
|
}
|
|
242
242
|
function o(e) {
|
|
243
|
-
var h,
|
|
243
|
+
var g, h, x;
|
|
244
244
|
if (e.cancelled) return;
|
|
245
245
|
const t = e.sourceBuffer, r = e.mediaSource;
|
|
246
246
|
if (!t || !r || r.readyState !== "open" || t.updating) return;
|
|
247
247
|
const p = e.appendQueue.shift();
|
|
248
248
|
if (p)
|
|
249
249
|
try {
|
|
250
|
-
t.appendBuffer(p), a.value !== "playing" && (a.value = "playing", f("stream-status", "playing"))
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
t.appendBuffer(p), a.value !== "playing" && (a.value = "playing", f("stream-status", "playing"), (g = l.value) == null || g.play().catch(() => {
|
|
251
|
+
}));
|
|
252
|
+
} catch (z) {
|
|
253
|
+
const H = z;
|
|
254
|
+
if (e.cancelled || (h = H.message) != null && h.includes("removed from the parent"))
|
|
254
255
|
return;
|
|
255
|
-
if (
|
|
256
|
-
const
|
|
256
|
+
if (H.name === "QuotaExceededError") {
|
|
257
|
+
const Y = ((x = l.value) == null ? void 0 : x.currentTime) ?? 0, F = Math.max(0, Y - 5);
|
|
257
258
|
try {
|
|
258
259
|
t.buffered.length > 0 && t.buffered.start(0) < F && t.remove(t.buffered.start(0), F);
|
|
259
260
|
} catch {
|
|
260
261
|
}
|
|
261
262
|
e.appendQueue.unshift(p);
|
|
262
263
|
} else
|
|
263
|
-
m.value = `appendBuffer failed: ${
|
|
264
|
+
m.value = `appendBuffer failed: ${H.message}`, a.value = "error", $();
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
async function u() {
|
|
@@ -280,29 +281,29 @@ const ee = {
|
|
|
280
281
|
const t = location.protocol === "https:" ? "wss:" : "ws:";
|
|
281
282
|
s.token && e.set("token", s.token);
|
|
282
283
|
const r = `${t}//${location.host}/ws/stream?${e.toString()}`;
|
|
283
|
-
|
|
284
|
+
D(r), T();
|
|
284
285
|
} catch (e) {
|
|
285
286
|
m.value = e.message, a.value = "error", b.value = !1, f("stream-status", "error"), console.error("[RvmsVideoPlayer] start failed:", e.message);
|
|
286
287
|
}
|
|
287
288
|
}
|
|
288
|
-
function
|
|
289
|
+
function y() {
|
|
289
290
|
b.value = !1, $(), a.value = "idle", m.value = null, f("stream-status", "idle");
|
|
290
291
|
}
|
|
291
292
|
function v(e) {
|
|
292
293
|
return e || "";
|
|
293
294
|
}
|
|
294
|
-
return
|
|
295
|
-
|
|
295
|
+
return G(() => {
|
|
296
|
+
M(), T();
|
|
296
297
|
}), q(() => {
|
|
297
|
-
|
|
298
|
+
Q(), y();
|
|
298
299
|
}), K(
|
|
299
300
|
() => [s.nvrId, s.deviceId, s.mode].join("|"),
|
|
300
301
|
() => {
|
|
301
302
|
b.value && s.nvrId && s.deviceId && u();
|
|
302
303
|
}
|
|
303
|
-
), U({ start: u, stop:
|
|
304
|
+
), U({ start: u, stop: y }), (e, t) => (c(), d("div", {
|
|
304
305
|
class: "rvms-player-wrapper",
|
|
305
|
-
style:
|
|
306
|
+
style: P({ width: C.width, height: C.height, position: "relative", background: "#000", overflow: "hidden", borderRadius: "8px" })
|
|
306
307
|
}, [
|
|
307
308
|
i("video", {
|
|
308
309
|
ref_key: "videoEl",
|
|
@@ -318,7 +319,7 @@ const ee = {
|
|
|
318
319
|
style: { cursor: "pointer" },
|
|
319
320
|
onClick: u
|
|
320
321
|
}, [
|
|
321
|
-
(c(), d("svg",
|
|
322
|
+
(c(), d("svg", te, [...t[2] || (t[2] = [
|
|
322
323
|
i("circle", {
|
|
323
324
|
cx: "32",
|
|
324
325
|
cy: "32",
|
|
@@ -333,28 +334,28 @@ const ee = {
|
|
|
333
334
|
}, null, -1)
|
|
334
335
|
])])),
|
|
335
336
|
t[3] || (t[3] = i("span", { style: { color: "#94a3b8", "font-size": "0.85rem" } }, "Click to play", -1))
|
|
336
|
-
])) : a.value === "connecting" ? (c(), d("div",
|
|
337
|
+
])) : a.value === "connecting" ? (c(), d("div", ne, [...t[4] || (t[4] = [
|
|
337
338
|
i("span", { style: { color: "#93c5fd", animation: "rvms-pulse 1.5s infinite" } }, "Connecting…", -1)
|
|
338
|
-
])])) : a.value === "buffering" ? (c(), d("div",
|
|
339
|
+
])])) : a.value === "buffering" ? (c(), d("div", re, [...t[5] || (t[5] = [
|
|
339
340
|
i("span", { style: { color: "#93c5fd", animation: "rvms-pulse 1.5s infinite" } }, "Buffering…", -1)
|
|
340
|
-
])])) : a.value === "error" ? (c(), d("div",
|
|
341
|
+
])])) : a.value === "error" ? (c(), d("div", oe, [
|
|
341
342
|
t[6] || (t[6] = i("span", { style: { color: "#f87171", "font-weight": "600" } }, "Stream error", -1)),
|
|
342
|
-
m.value ? (c(), d("span",
|
|
343
|
+
m.value ? (c(), d("span", ae, _(m.value), 1)) : B("", !0),
|
|
343
344
|
i("button", {
|
|
344
345
|
class: "rvms-btn",
|
|
345
346
|
onClick: u
|
|
346
347
|
}, " Retry ")
|
|
347
|
-
])) : a.value === "ended" ? (c(), d("div",
|
|
348
|
+
])) : a.value === "ended" ? (c(), d("div", le, [
|
|
348
349
|
t[7] || (t[7] = i("span", { style: { color: "#94a3b8" } }, "Stream ended", -1)),
|
|
349
350
|
i("button", {
|
|
350
351
|
class: "rvms-btn",
|
|
351
352
|
onClick: u
|
|
352
353
|
}, " Reconnect ")
|
|
353
354
|
])) : B("", !0),
|
|
354
|
-
i("div",
|
|
355
|
+
i("div", se, [
|
|
355
356
|
a.value !== "idle" ? (c(), d("span", {
|
|
356
357
|
key: 0,
|
|
357
|
-
style:
|
|
358
|
+
style: P({
|
|
358
359
|
padding: "2px 8px",
|
|
359
360
|
borderRadius: "4px",
|
|
360
361
|
fontSize: "10px",
|
|
@@ -365,26 +366,26 @@ const ee = {
|
|
|
365
366
|
color: "#fff"
|
|
366
367
|
})
|
|
367
368
|
}, _(C.mode === "live" ? "LIVE" : "PLAYBACK"), 5)) : B("", !0),
|
|
368
|
-
w.value ? (c(), d("span",
|
|
369
|
-
L.value ? (c(), d("span",
|
|
369
|
+
w.value ? (c(), d("span", ie, _(w.value), 1)) : B("", !0),
|
|
370
|
+
L.value ? (c(), d("span", ue, [...t[8] || (t[8] = [
|
|
370
371
|
i("span", { style: { width: "6px", height: "6px", "border-radius": "50%", background: "#22c55e", display: "inline-block" } }, null, -1),
|
|
371
|
-
|
|
372
|
+
X(" Alarms ", -1)
|
|
372
373
|
])])) : B("", !0)
|
|
373
374
|
]),
|
|
374
|
-
a.value !== "idle" ? (c(), d("div",
|
|
375
|
+
a.value !== "idle" ? (c(), d("div", ce, [
|
|
375
376
|
i("button", {
|
|
376
377
|
class: "rvms-btn",
|
|
377
378
|
title: "Stop stream",
|
|
378
|
-
onClick:
|
|
379
|
+
onClick: y
|
|
379
380
|
}, " ⏹ Stop "),
|
|
380
381
|
i("button", {
|
|
381
382
|
class: "rvms-btn",
|
|
382
383
|
title: `Switch to ${I.value === "main" ? "sub" : "main"} stream`,
|
|
383
384
|
onClick: E
|
|
384
|
-
}, _(I.value === "main" ? "HD" : "SD"), 9,
|
|
385
|
+
}, _(I.value === "main" ? "HD" : "SD"), 9, de)
|
|
385
386
|
])) : B("", !0),
|
|
386
|
-
C.showAlarms &&
|
|
387
|
-
(c(!0), d(
|
|
387
|
+
C.showAlarms && O.value.length > 0 ? (c(), d("div", fe, [
|
|
388
|
+
(c(!0), d(Z, null, ee(O.value.slice(0, 5), (r) => (c(), d("div", {
|
|
388
389
|
key: r.id,
|
|
389
390
|
title: (r.description || r.type) + " — click to view playback",
|
|
390
391
|
style: { display: "flex", "align-items": "center", gap: "8px", padding: "4px 8px", "border-radius": "4px", background: "rgba(0, 0, 0, 0.65)", "font-size": "11px", "backdrop-filter": "blur(4px)", cursor: "pointer", transition: "background 0.15s" },
|
|
@@ -398,43 +399,43 @@ const ee = {
|
|
|
398
399
|
alt: "snapshot",
|
|
399
400
|
style: { width: "32px", height: "24px", "border-radius": "2px", "object-fit": "cover", background: "#1e293b" },
|
|
400
401
|
loading: "lazy"
|
|
401
|
-
}, null, 8,
|
|
402
|
-
i("div",
|
|
403
|
-
i("div",
|
|
404
|
-
i("span",
|
|
405
|
-
r.channel ? (c(), d("span",
|
|
402
|
+
}, null, 8, ve)) : B("", !0),
|
|
403
|
+
i("div", me, [
|
|
404
|
+
i("div", ge, [
|
|
405
|
+
i("span", ye, _(r.type), 1),
|
|
406
|
+
r.channel ? (c(), d("span", he, " ch" + _(r.channel), 1)) : B("", !0)
|
|
406
407
|
]),
|
|
407
|
-
i("div",
|
|
408
|
+
i("div", be, _(new Date(r.timestamp).toLocaleString()), 1),
|
|
408
409
|
t[9] || (t[9] = i("div", { style: { color: "#60a5fa", "font-size": "9px", "margin-top": "1px" } }, " Click to view playback ", -1))
|
|
409
410
|
])
|
|
410
|
-
], 40,
|
|
411
|
+
], 40, pe))), 128))
|
|
411
412
|
])) : B("", !0)
|
|
412
413
|
], 4));
|
|
413
414
|
}
|
|
414
|
-
}),
|
|
415
|
+
}), xe = {
|
|
415
416
|
key: 1,
|
|
416
417
|
style: { position: "absolute", inset: "0", display: "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center", gap: "8px", background: "rgba(0, 0, 0, 0.4)", "z-index": "10" }
|
|
417
|
-
},
|
|
418
|
+
}, ke = { style: { color: "#e2e8f0", "font-size": "0.85rem" } }, Se = {
|
|
418
419
|
key: 2,
|
|
419
420
|
style: { position: "absolute", inset: "0", display: "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center", gap: "12px", background: "rgba(0, 0, 0, 0.5)", "z-index": "10" }
|
|
420
|
-
},
|
|
421
|
+
}, we = { style: { color: "#ef4444", "font-size": "0.85rem", "text-align": "center", padding: "0 16px" } }, Be = {
|
|
421
422
|
key: 3,
|
|
422
423
|
style: { position: "absolute", inset: "0", display: "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center", gap: "12px", background: "rgba(0, 0, 0, 0.4)", "z-index": "10" }
|
|
423
|
-
},
|
|
424
|
+
}, $e = { style: { position: "absolute", top: "8px", left: "8px", right: "8px", display: "flex", gap: "6px", "align-items": "center", "flex-wrap": "wrap", "z-index": "11", "pointer-events": "none" } }, _e = ["title"], Ce = ["title"], Ie = {
|
|
424
425
|
key: 0,
|
|
425
426
|
class: "rvms-chip",
|
|
426
427
|
style: { background: "rgba(51, 65, 85, 0.8)", color: "#cbd5e1", "font-family": "monospace" }
|
|
427
|
-
},
|
|
428
|
+
}, Ee = ["value"], Le = {
|
|
428
429
|
key: 2,
|
|
429
430
|
class: "rvms-chip",
|
|
430
431
|
style: { background: "rgba(15, 23, 42, 0.85)", color: "#94a3b8", cursor: "default", "pointer-events": "auto" }
|
|
431
|
-
},
|
|
432
|
+
}, Oe = {
|
|
432
433
|
key: 4,
|
|
433
434
|
style: { position: "absolute", bottom: "44px", left: "8px", display: "flex", gap: "6px", "align-items": "center", "z-index": "11", "pointer-events": "none" }
|
|
434
|
-
},
|
|
435
|
+
}, ze = {
|
|
435
436
|
key: 5,
|
|
436
437
|
style: { position: "absolute", bottom: "8px", right: "8px", display: "flex", gap: "6px", "z-index": "11" }
|
|
437
|
-
},
|
|
438
|
+
}, Re = /* @__PURE__ */ J({
|
|
438
439
|
__name: "RvmsVideo",
|
|
439
440
|
props: {
|
|
440
441
|
nvrId: {},
|
|
@@ -446,9 +447,9 @@ const ee = {
|
|
|
446
447
|
},
|
|
447
448
|
emits: ["status-change"],
|
|
448
449
|
setup(C, { emit: U }) {
|
|
449
|
-
const
|
|
450
|
+
const k = C, s = U, f = S(null), l = S("idle"), a = S(null), m = S(null), w = S(!1), b = S("live"), I = S(k.initialProfile), E = S("");
|
|
450
451
|
let L = null;
|
|
451
|
-
function
|
|
452
|
+
function O() {
|
|
452
453
|
return {
|
|
453
454
|
cancelled: !1,
|
|
454
455
|
ws: null,
|
|
@@ -491,21 +492,21 @@ const ee = {
|
|
|
491
492
|
}
|
|
492
493
|
}
|
|
493
494
|
}
|
|
494
|
-
function
|
|
495
|
+
function M(n) {
|
|
495
496
|
if (!f.value) return;
|
|
496
497
|
if (!("MediaSource" in window)) {
|
|
497
498
|
a.value = "MediaSource Extensions not supported", l.value = "error", s("status-change", "error");
|
|
498
499
|
return;
|
|
499
500
|
}
|
|
500
501
|
l.value = "connecting", a.value = null, m.value = null, s("status-change", "connecting");
|
|
501
|
-
const o =
|
|
502
|
+
const o = O();
|
|
502
503
|
L = o;
|
|
503
504
|
const u = new MediaSource();
|
|
504
505
|
o.mediaSource = u, f.value.src = URL.createObjectURL(u);
|
|
505
|
-
const
|
|
506
|
+
const y = () => {
|
|
506
507
|
o.cancelled || o.mediaSource !== u || (l.value = "buffering", s("status-change", "buffering"), A(o));
|
|
507
508
|
};
|
|
508
|
-
o.sourceOpenHandler =
|
|
509
|
+
o.sourceOpenHandler = y, u.addEventListener("sourceopen", y);
|
|
509
510
|
const v = new WebSocket(n);
|
|
510
511
|
v.binaryType = "arraybuffer", o.ws = v, v.onmessage = (e) => {
|
|
511
512
|
if (o.cancelled) return;
|
|
@@ -524,27 +525,27 @@ const ee = {
|
|
|
524
525
|
}
|
|
525
526
|
o.appendQueue.push(
|
|
526
527
|
t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength)
|
|
527
|
-
),
|
|
528
|
+
), j(o);
|
|
528
529
|
}, v.onerror = () => {
|
|
529
530
|
o.cancelled || l.value !== "error" && (a.value = "WebSocket error", l.value = "error", s("status-change", "error"));
|
|
530
531
|
}, v.onclose = (e) => {
|
|
531
532
|
o.cancelled || (l.value === "connecting" || l.value === "buffering" ? (a.value = e.reason || `Stream closed (code ${e.code})`, l.value = "error", s("status-change", "error")) : l.value === "playing" && (l.value = "ended", s("status-change", "ended")));
|
|
532
533
|
};
|
|
533
534
|
}
|
|
534
|
-
function
|
|
535
|
+
function Q(n) {
|
|
535
536
|
const o = (u) => u.toString(16).padStart(2, "0").toUpperCase();
|
|
536
537
|
for (let u = 0; u + 7 < n.length; u++) {
|
|
537
|
-
const
|
|
538
|
-
if (
|
|
538
|
+
const y = String.fromCharCode(n[u], n[u + 1], n[u + 2], n[u + 3]);
|
|
539
|
+
if (y === "avcC") {
|
|
539
540
|
const v = n[u + 5], e = n[u + 6], t = n[u + 7];
|
|
540
541
|
if (v !== void 0 && e !== void 0 && t !== void 0)
|
|
541
542
|
return `avc1.${o(v)}${o(e)}${o(t)}`;
|
|
542
543
|
}
|
|
543
|
-
if (
|
|
544
|
+
if (y === "hvcC") return "hev1.1.6.L93.B0";
|
|
544
545
|
}
|
|
545
546
|
return null;
|
|
546
547
|
}
|
|
547
|
-
function
|
|
548
|
+
function T(n) {
|
|
548
549
|
for (let o = 0; o + 3 < n.length; o++)
|
|
549
550
|
if (n[o] === 101 && n[o + 1] === 115 && n[o + 2] === 100 && n[o + 3] === 115) return !0;
|
|
550
551
|
return !1;
|
|
@@ -554,9 +555,9 @@ const ee = {
|
|
|
554
555
|
const o = ["avc1.64001F", "avc1.4D0028", "avc1.42001E", "hev1.1.6.L93.B0"];
|
|
555
556
|
let u = 'video/mp4; codecs="avc1.64001F"';
|
|
556
557
|
for (const v of n.preBuffer) {
|
|
557
|
-
const e =
|
|
558
|
+
const e = Q(v);
|
|
558
559
|
if (e) {
|
|
559
|
-
const t =
|
|
560
|
+
const t = T(v), r = t ? `${e}, mp4a.40.2` : e;
|
|
560
561
|
m.value = r, u = `video/mp4; codecs="${r}"`, !MediaSource.isTypeSupported(u) && t && (u = `video/mp4; codecs="${e}"`, m.value = e);
|
|
561
562
|
break;
|
|
562
563
|
}
|
|
@@ -581,83 +582,84 @@ const ee = {
|
|
|
581
582
|
} catch {
|
|
582
583
|
}
|
|
583
584
|
n.preBuffer.length = 0;
|
|
584
|
-
const
|
|
585
|
-
|
|
585
|
+
const y = () => {
|
|
586
|
+
j(n);
|
|
586
587
|
};
|
|
587
|
-
n.updateEndHandler =
|
|
588
|
+
n.updateEndHandler = y, n.sourceBuffer.addEventListener("updateend", y);
|
|
588
589
|
}
|
|
589
|
-
function
|
|
590
|
-
var
|
|
590
|
+
function j(n) {
|
|
591
|
+
var y, v, e;
|
|
591
592
|
const o = n.sourceBuffer;
|
|
592
593
|
if (!o || o.updating || n.appendQueue.length === 0) return;
|
|
593
594
|
const u = n.appendQueue.shift();
|
|
594
595
|
try {
|
|
595
|
-
o.appendBuffer(u), l.value !== "playing" && (l.value = "playing", s("status-change", "playing"))
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
596
|
+
o.appendBuffer(u), l.value !== "playing" && (l.value = "playing", s("status-change", "playing"), (y = f.value) == null || y.play().catch(() => {
|
|
597
|
+
}));
|
|
598
|
+
} catch (t) {
|
|
599
|
+
const r = t;
|
|
600
|
+
if (n.cancelled || (v = r.message) != null && v.includes("removed from the parent"))
|
|
599
601
|
return;
|
|
600
|
-
if (
|
|
601
|
-
const
|
|
602
|
+
if (r.name === "QuotaExceededError") {
|
|
603
|
+
const p = ((e = f.value) == null ? void 0 : e.currentTime) ?? 0, g = Math.max(0, p - 5);
|
|
602
604
|
try {
|
|
603
|
-
o.buffered.length > 0 && o.buffered.start(0) <
|
|
605
|
+
o.buffered.length > 0 && o.buffered.start(0) < g && o.remove(o.buffered.start(0), g);
|
|
604
606
|
} catch {
|
|
605
607
|
}
|
|
606
608
|
n.appendQueue.unshift(u);
|
|
607
609
|
} else
|
|
608
|
-
a.value = `appendBuffer failed: ${
|
|
610
|
+
a.value = `appendBuffer failed: ${r.message}`, l.value = "error", R();
|
|
609
611
|
}
|
|
610
612
|
}
|
|
611
613
|
async function $() {
|
|
612
|
-
if (b.value === "playback" && !E.value && (E.value = (/* @__PURE__ */ new Date()).toISOString()), R(), w.value = !0, !
|
|
614
|
+
if (b.value === "playback" && !E.value && (E.value = (/* @__PURE__ */ new Date()).toISOString()), R(), w.value = !0, !k.nvrId || !k.channelId) {
|
|
613
615
|
a.value = "nvrId and channelId are required", l.value = "error", w.value = !1, s("status-change", "error");
|
|
614
616
|
return;
|
|
615
617
|
}
|
|
616
618
|
l.value = "connecting", a.value = null, m.value = null, s("status-change", "connecting");
|
|
617
619
|
try {
|
|
618
620
|
const n = new URLSearchParams({
|
|
619
|
-
nvrId:
|
|
620
|
-
deviceId:
|
|
621
|
+
nvrId: k.nvrId,
|
|
622
|
+
deviceId: k.channelId,
|
|
621
623
|
mode: b.value,
|
|
622
624
|
profile: I.value
|
|
623
625
|
});
|
|
624
626
|
if (b.value === "playback") {
|
|
625
|
-
let
|
|
626
|
-
|
|
627
|
-
const v = new Date(
|
|
627
|
+
let y = E.value;
|
|
628
|
+
y || (y = (/* @__PURE__ */ new Date()).toISOString(), E.value = y);
|
|
629
|
+
const v = new Date(y).getTime();
|
|
628
630
|
n.set("from", new Date(v - 3e4).toISOString()), n.set("to", new Date(v + 3e4).toISOString());
|
|
629
631
|
}
|
|
630
632
|
const o = location.protocol === "https:" ? "wss:" : "ws:";
|
|
631
|
-
|
|
633
|
+
k.token && n.set("token", k.token);
|
|
632
634
|
const u = `${o}//${location.host}/ws/stream?${n.toString()}`;
|
|
633
|
-
|
|
635
|
+
M(u);
|
|
634
636
|
} catch (n) {
|
|
635
637
|
a.value = n.message, l.value = "error", w.value = !1, s("status-change", "error");
|
|
636
638
|
}
|
|
637
639
|
}
|
|
638
|
-
function
|
|
640
|
+
function D() {
|
|
639
641
|
w.value = !1, R(), l.value = "idle", a.value = null, s("status-change", "idle");
|
|
640
642
|
}
|
|
641
|
-
function
|
|
643
|
+
function W() {
|
|
642
644
|
b.value = b.value === "live" ? "playback" : "live", w.value && $();
|
|
643
645
|
}
|
|
644
|
-
function
|
|
646
|
+
function N() {
|
|
645
647
|
I.value = I.value === "main" ? "sub" : "main", w.value && $();
|
|
646
648
|
}
|
|
647
|
-
function
|
|
649
|
+
function V(n) {
|
|
648
650
|
const o = n.target;
|
|
649
651
|
o.value && (E.value = new Date(o.value).toISOString());
|
|
650
652
|
}
|
|
651
653
|
return q(() => {
|
|
652
|
-
|
|
654
|
+
D();
|
|
653
655
|
}), K(
|
|
654
|
-
() => [
|
|
656
|
+
() => [k.nvrId, k.channelId].join("|"),
|
|
655
657
|
() => {
|
|
656
|
-
w.value &&
|
|
658
|
+
w.value && k.nvrId && k.channelId && $();
|
|
657
659
|
}
|
|
658
660
|
), (n, o) => (c(), d("div", {
|
|
659
661
|
class: "rvms-video",
|
|
660
|
-
style:
|
|
662
|
+
style: P({
|
|
661
663
|
width: C.width,
|
|
662
664
|
height: C.height,
|
|
663
665
|
position: "relative",
|
|
@@ -669,6 +671,7 @@ const ee = {
|
|
|
669
671
|
i("video", {
|
|
670
672
|
ref_key: "videoEl",
|
|
671
673
|
ref: f,
|
|
674
|
+
autoplay: "",
|
|
672
675
|
muted: "",
|
|
673
676
|
playsinline: "",
|
|
674
677
|
style: { width: "100%", height: "100%", display: "block", "object-fit": "contain" }
|
|
@@ -698,53 +701,53 @@ const ee = {
|
|
|
698
701
|
})
|
|
699
702
|
], -1),
|
|
700
703
|
i("span", { style: { color: "#e2e8f0", "font-size": "0.9rem", "font-weight": "600" } }, "Click to play", -1)
|
|
701
|
-
])])) : l.value === "connecting" || l.value === "buffering" ? (c(), d("div",
|
|
704
|
+
])])) : l.value === "connecting" || l.value === "buffering" ? (c(), d("div", xe, [
|
|
702
705
|
o[1] || (o[1] = i("div", { class: "rvms-spinner" }, null, -1)),
|
|
703
|
-
i("span",
|
|
704
|
-
])) : l.value === "error" ? (c(), d("div",
|
|
705
|
-
i("span",
|
|
706
|
+
i("span", ke, _(l.value === "connecting" ? "Connecting…" : "Buffering…"), 1)
|
|
707
|
+
])) : l.value === "error" ? (c(), d("div", Se, [
|
|
708
|
+
i("span", we, _(a.value || "Stream error"), 1),
|
|
706
709
|
i("button", {
|
|
707
710
|
class: "rvms-btn",
|
|
708
711
|
onClick: $
|
|
709
712
|
}, " Retry ")
|
|
710
|
-
])) : l.value === "ended" ? (c(), d("div",
|
|
713
|
+
])) : l.value === "ended" ? (c(), d("div", Be, [
|
|
711
714
|
o[2] || (o[2] = i("span", { style: { color: "#94a3b8" } }, "Stream ended", -1)),
|
|
712
715
|
i("button", {
|
|
713
716
|
class: "rvms-btn",
|
|
714
717
|
onClick: $
|
|
715
718
|
}, "Reconnect")
|
|
716
719
|
])) : B("", !0),
|
|
717
|
-
i("div",
|
|
720
|
+
i("div", $e, [
|
|
718
721
|
i("button", {
|
|
719
722
|
class: "rvms-chip",
|
|
720
723
|
title: `Switch to ${b.value === "live" ? "playback" : "live"} mode`,
|
|
721
|
-
onClick:
|
|
722
|
-
style:
|
|
724
|
+
onClick: W,
|
|
725
|
+
style: P({
|
|
723
726
|
background: b.value === "live" ? "#dc2626" : "#2563eb",
|
|
724
727
|
pointerEvents: "auto"
|
|
725
728
|
})
|
|
726
|
-
}, _(b.value === "live" ? "🔴 LIVE" : "🔵 PLAYBACK"), 13,
|
|
729
|
+
}, _(b.value === "live" ? "🔴 LIVE" : "🔵 PLAYBACK"), 13, _e),
|
|
727
730
|
i("button", {
|
|
728
731
|
class: "rvms-chip",
|
|
729
732
|
title: `Switch to ${I.value === "main" ? "sub" : "main"} stream`,
|
|
730
|
-
onClick:
|
|
733
|
+
onClick: N,
|
|
731
734
|
style: { pointerEvents: "auto" }
|
|
732
|
-
}, _(I.value === "main" ? "HD" : "SD"), 9,
|
|
733
|
-
m.value ? (c(), d("span",
|
|
735
|
+
}, _(I.value === "main" ? "HD" : "SD"), 9, Ce),
|
|
736
|
+
m.value ? (c(), d("span", Ie, _(m.value), 1)) : B("", !0),
|
|
734
737
|
b.value === "playback" ? (c(), d("input", {
|
|
735
738
|
key: 1,
|
|
736
739
|
type: "datetime-local",
|
|
737
740
|
value: E.value ? E.value.slice(0, 16) : "",
|
|
738
|
-
onInput:
|
|
741
|
+
onInput: V,
|
|
739
742
|
class: "rvms-datetime",
|
|
740
743
|
title: "Playback timestamp",
|
|
741
744
|
style: { "pointer-events": "auto" }
|
|
742
|
-
}, null, 40,
|
|
743
|
-
b.value === "playback" && E.value ? (c(), d("span",
|
|
745
|
+
}, null, 40, Ee)) : B("", !0),
|
|
746
|
+
b.value === "playback" && E.value ? (c(), d("span", Le, _(new Date(E.value).toLocaleString()), 1)) : B("", !0)
|
|
744
747
|
]),
|
|
745
|
-
l.value !== "idle" ? (c(), d("div",
|
|
748
|
+
l.value !== "idle" ? (c(), d("div", Oe, [
|
|
746
749
|
i("span", {
|
|
747
|
-
style:
|
|
750
|
+
style: P({
|
|
748
751
|
padding: "2px 8px",
|
|
749
752
|
borderRadius: "4px",
|
|
750
753
|
fontSize: "10px",
|
|
@@ -755,12 +758,12 @@ const ee = {
|
|
|
755
758
|
})
|
|
756
759
|
}, _(l.value), 5)
|
|
757
760
|
])) : B("", !0),
|
|
758
|
-
l.value !== "idle" ? (c(), d("div",
|
|
761
|
+
l.value !== "idle" ? (c(), d("div", ze, [
|
|
759
762
|
w.value ? (c(), d("button", {
|
|
760
763
|
key: 0,
|
|
761
764
|
class: "rvms-btn",
|
|
762
765
|
title: "Stop stream",
|
|
763
|
-
onClick:
|
|
766
|
+
onClick: D
|
|
764
767
|
}, " ⏹ Stop ")) : (c(), d("button", {
|
|
765
768
|
key: 1,
|
|
766
769
|
class: "rvms-btn",
|
|
@@ -770,13 +773,13 @@ const ee = {
|
|
|
770
773
|
])) : B("", !0)
|
|
771
774
|
], 4));
|
|
772
775
|
}
|
|
773
|
-
}),
|
|
774
|
-
const
|
|
776
|
+
}), Ae = (C, U) => {
|
|
777
|
+
const k = C.__vccOpts || C;
|
|
775
778
|
for (const [s, f] of U)
|
|
776
|
-
|
|
777
|
-
return
|
|
778
|
-
},
|
|
779
|
+
k[s] = f;
|
|
780
|
+
return k;
|
|
781
|
+
}, Pe = /* @__PURE__ */ Ae(Re, [["__scopeId", "data-v-7528fc18"]]);
|
|
779
782
|
export {
|
|
780
|
-
|
|
781
|
-
|
|
783
|
+
Pe as RvmsVideo,
|
|
784
|
+
He as RvmsVideoPlayer
|
|
782
785
|
};
|
package/dist/lib/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@keyframes rvms-pulse{0%,to{opacity:1}50%{opacity:.4}}.rvms-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:#00000080;font-size:14px}.rvms-btn{padding:4px 12px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#1e293bcc;color:#e2e8f0;font-size:11px;font-weight:600;cursor:pointer;pointer-events:auto;transition:background .15s}.rvms-btn:hover{background:#334155e6}@keyframes rvms-spin-
|
|
1
|
+
@keyframes rvms-pulse{0%,to{opacity:1}50%{opacity:.4}}.rvms-overlay{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:#00000080;font-size:14px}.rvms-btn{padding:4px 12px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#1e293bcc;color:#e2e8f0;font-size:11px;font-weight:600;cursor:pointer;pointer-events:auto;transition:background .15s}.rvms-btn:hover{background:#334155e6}@keyframes rvms-spin-7528fc18{to{transform:rotate(360deg)}}.rvms-spinner[data-v-7528fc18]{width:32px;height:32px;border:3px solid rgba(255,255,255,.2);border-top-color:#60a5fa;border-radius:50%;animation:rvms-spin-7528fc18 .8s linear infinite}.rvms-btn[data-v-7528fc18]{padding:6px 14px;border:none;border-radius:6px;background:#0f172ad9;color:#e2e8f0;font-size:.8rem;font-weight:700;cursor:pointer;transition:background .15s;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.rvms-btn[data-v-7528fc18]:hover{background:#1e293bf2}.rvms-chip[data-v-7528fc18]{padding:2px 8px;border:none;border-radius:4px;font-size:10px;font-weight:700;color:#fff;cursor:pointer;transition:opacity .15s;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);text-transform:uppercase;letter-spacing:.05em}.rvms-chip[data-v-7528fc18]:hover{opacity:.85}.rvms-datetime[data-v-7528fc18]{padding:2px 6px;border:1px solid rgba(255,255,255,.2);border-radius:4px;background:#0f172ad9;color:#e2e8f0;font-size:11px;cursor:pointer;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.rvms-datetime[data-v-7528fc18]:focus{outline:none;border-color:#60a5fa}
|
package/package.json
CHANGED
|
@@ -324,6 +324,8 @@ function flushQueue(s: Session): void {
|
|
|
324
324
|
if (status.value !== 'playing') {
|
|
325
325
|
status.value = 'playing';
|
|
326
326
|
emit('status-change', 'playing');
|
|
327
|
+
// Ensure video starts playing once data is available (muted autoplay policy)
|
|
328
|
+
videoEl.value?.play().catch(() => { /* user interaction may be needed */ });
|
|
327
329
|
}
|
|
328
330
|
} catch (err) {
|
|
329
331
|
const e = err as DOMException;
|
|
@@ -480,6 +482,7 @@ watch(
|
|
|
480
482
|
<!-- ── Video element ────────────────────────────────────────────────── -->
|
|
481
483
|
<video
|
|
482
484
|
ref="videoEl"
|
|
485
|
+
autoplay
|
|
483
486
|
muted
|
|
484
487
|
playsinline
|
|
485
488
|
style="width: 100%; height: 100%; display: block; object-fit: contain;"
|
|
@@ -468,6 +468,7 @@ function flushQueue(session: Session): void {
|
|
|
468
468
|
if (streamStatus.value !== 'playing') {
|
|
469
469
|
streamStatus.value = 'playing';
|
|
470
470
|
emit('stream-status', 'playing');
|
|
471
|
+
videoEl.value?.play().catch(() => { /* user interaction may be needed */ });
|
|
471
472
|
}
|
|
472
473
|
} catch (err) {
|
|
473
474
|
const e = err as DOMException;
|