@banou/media-player 0.8.16 → 0.8.17
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/engine/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-B8CKGIng.js";
|
|
2
2
|
export { i as DEFAULT_BUFFER_SIZE, e as MediaElementError, a as SUBTITLES_OFF, u as createPictureInPicture, d as createSubtitleRenderer, c as createThumbnailGenerator, n as getTimeRanges, s as isMediaElementError, o as pictureInPictureMode, l as startPlayback, t as terminateRemuxer, r as updateSourceBuffer };
|
|
@@ -20,7 +20,12 @@ export type SubtitleRendererOptions = {
|
|
|
20
20
|
* which throws. So leaving this unset does not degrade to the slower build, it fails outright.
|
|
21
21
|
*/
|
|
22
22
|
legacyWasmUrl?: string;
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Fallback face for `liberation sans`. Without it jassub falls back to whatever the wasm build embeds.
|
|
25
|
+
*
|
|
26
|
+
* May be relative: it is resolved against the document before the worker sees it, for the reason
|
|
27
|
+
* given on `workerFetched`.
|
|
28
|
+
*/
|
|
24
29
|
defaultFontUrl?: string;
|
|
25
30
|
onStreams?: (streams: SubtitleStream[]) => void;
|
|
26
31
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { makeRemuxer as e, makeThumbnailer as t } from "libav-wasm";
|
|
2
2
|
import n from "jassub";
|
|
3
|
-
import { parse as r
|
|
3
|
+
import { parse as r } from "ass-compiler";
|
|
4
4
|
//#region src/lib/engine/source-buffer.ts
|
|
5
|
-
var
|
|
5
|
+
var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
6
6
|
index: n,
|
|
7
7
|
start: e.buffered.start(n),
|
|
8
8
|
end: e.buffered.end(n)
|
|
9
|
-
})),
|
|
9
|
+
})), a = (e) => new Promise((t, n) => {
|
|
10
10
|
let r = () => {
|
|
11
11
|
e.removeEventListener("updateend", i), e.removeEventListener("abort", i), e.removeEventListener("error", a);
|
|
12
12
|
}, i = () => {
|
|
@@ -15,14 +15,14 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
15
15
|
r(), n(Error("The browser rejected a media segment", { cause: e }));
|
|
16
16
|
};
|
|
17
17
|
e.addEventListener("updateend", i, { once: !0 }), e.addEventListener("abort", i, { once: !0 }), e.addEventListener("error", a, { once: !0 });
|
|
18
|
-
}),
|
|
18
|
+
}), o = (e, t) => {
|
|
19
19
|
let n = Promise.resolve(), r = (e) => {
|
|
20
20
|
let t = n.then(e, e);
|
|
21
21
|
return n = t.catch(() => {}), t;
|
|
22
22
|
};
|
|
23
23
|
return {
|
|
24
|
-
appendBuffer: (t) => r(() => (e.appendBuffer(t),
|
|
25
|
-
unbufferRange: (t, n) => r(() => t >= n ? Promise.resolve() : (e.remove(t, n),
|
|
24
|
+
appendBuffer: (t) => r(() => (e.appendBuffer(t), a(e))),
|
|
25
|
+
unbufferRange: (t, n) => r(() => t >= n ? Promise.resolve() : (e.remove(t, n), a(e))),
|
|
26
26
|
updateTimestampOffset: (t) => r(async () => {
|
|
27
27
|
e.timestampOffset = t;
|
|
28
28
|
}),
|
|
@@ -32,21 +32,10 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
32
32
|
} catch {}
|
|
33
33
|
})
|
|
34
34
|
};
|
|
35
|
-
},
|
|
35
|
+
}, s = -1, c = (e, t) => e.createEvent(t), l = (e) => e === void 0 ? void 0 : new URL(e, document.baseURI).toString(), u = (e) => new Date(e).toISOString().slice(11, 22), d = 1, f = (e) => new Map(r(e).styles.style.map((e, t) => [e.Name, t + d])), p = (e, t) => {
|
|
36
36
|
let n = t.replace(/^\*+/, ""), r = n.toLowerCase() === "default" ? "Default" : n;
|
|
37
37
|
return e.styles.get(r) ?? e.styles.get("Default") ?? 0;
|
|
38
38
|
}, m = (e) => {
|
|
39
|
-
let t = r(e);
|
|
40
|
-
return i({
|
|
41
|
-
...t,
|
|
42
|
-
info: {
|
|
43
|
-
...t.info,
|
|
44
|
-
ScaledBorderAndShadow: "no",
|
|
45
|
-
LayoutResX: "",
|
|
46
|
-
LayoutResY: ""
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}, h = (e) => {
|
|
50
39
|
let t = e.content.match(/\r?\n\[Events\]\r?\nFormat: (.*)/)?.[0];
|
|
51
40
|
return t ? {
|
|
52
41
|
type: "header",
|
|
@@ -55,7 +44,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
55
44
|
eventsContent: t,
|
|
56
45
|
styles: f(e.content)
|
|
57
46
|
} : (console.warn(`subtitle stream ${e.streamIndex} has no Events format, ignoring the track`), null);
|
|
58
|
-
},
|
|
47
|
+
}, h = (e, t) => {
|
|
59
48
|
let [n, i] = t.content.split(","), a = Number(n), o = `Dialogue: ${i},${u(t.start)},${u(t.end)},${t.content.replace(`${a},${i},`, "")}`, s = r(`${e.eventsContent}\r\n${o}`).events.dialogue[0];
|
|
60
49
|
if (!s) throw Error("dialogue event is undefined");
|
|
61
50
|
return {
|
|
@@ -74,123 +63,123 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
74
63
|
_index: a
|
|
75
64
|
}
|
|
76
65
|
};
|
|
77
|
-
},
|
|
78
|
-
let { video: t, canvas: r, workerUrl: i, wasmUrl
|
|
79
|
-
|
|
80
|
-
}, 100),
|
|
81
|
-
|
|
66
|
+
}, g = (e) => {
|
|
67
|
+
let { video: t, canvas: r, workerUrl: i } = e, a = l(e.wasmUrl), o = l(e.legacyWasmUrl), s = l(e.defaultFontUrl), u, d = [], f = /* @__PURE__ */ new Map(), p = [], g = /* @__PURE__ */ new Map(), _, v = e.onStreams, ee = setInterval(() => {
|
|
68
|
+
u?.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
|
|
69
|
+
}, 100), y = () => u?.setRate(t.playbackRate), b = (e) => {
|
|
70
|
+
u = new n({
|
|
82
71
|
onDemandRender: !1,
|
|
83
72
|
video: t,
|
|
84
73
|
canvas: r,
|
|
85
|
-
subContent:
|
|
74
|
+
subContent: e.content,
|
|
86
75
|
workerUrl: i,
|
|
87
76
|
modernWasmUrl: a,
|
|
88
77
|
...o ? { wasmUrl: o } : {},
|
|
89
|
-
fonts:
|
|
78
|
+
fonts: d.map(([, e]) => e),
|
|
90
79
|
availableFonts: {
|
|
91
|
-
...Object.fromEntries(
|
|
80
|
+
...Object.fromEntries(d),
|
|
92
81
|
...s ? { "liberation sans": s } : {}
|
|
93
82
|
}
|
|
94
|
-
}), t.removeEventListener("ratechange",
|
|
83
|
+
}), t.removeEventListener("ratechange", u._boundSetRate), t.addEventListener("ratechange", y);
|
|
95
84
|
};
|
|
96
85
|
return {
|
|
97
86
|
pushAttachments: (e) => {
|
|
98
|
-
|
|
87
|
+
d = [...d, ...e.map((e) => [e.filename, new Uint8Array(e.data)])];
|
|
99
88
|
},
|
|
100
89
|
pushFragments: (e) => {
|
|
101
90
|
for (let t of e) if (t.type === "header") {
|
|
102
|
-
if (
|
|
103
|
-
let e =
|
|
91
|
+
if (f.has(t.streamIndex)) continue;
|
|
92
|
+
let e = m(t);
|
|
104
93
|
if (!e) continue;
|
|
105
|
-
|
|
94
|
+
f.set(t.streamIndex, e), p.push({
|
|
106
95
|
streamIndex: t.streamIndex,
|
|
107
96
|
title: t.title,
|
|
108
97
|
language: t.language
|
|
109
|
-
}), v?.([...
|
|
98
|
+
}), v?.([...p]), _ === void 0 && (_ = t.streamIndex), u || b(e);
|
|
110
99
|
} else {
|
|
111
|
-
let e =
|
|
100
|
+
let e = f.get(t.streamIndex);
|
|
112
101
|
if (!e) continue;
|
|
113
|
-
let n =
|
|
114
|
-
n || (n = /* @__PURE__ */ new Map(),
|
|
115
|
-
let r =
|
|
102
|
+
let n = g.get(t.streamIndex);
|
|
103
|
+
n || (n = /* @__PURE__ */ new Map(), g.set(t.streamIndex, n));
|
|
104
|
+
let r = h(e, t);
|
|
116
105
|
if (n.has(r.index)) continue;
|
|
117
|
-
n.set(r.index, r), _ === t.streamIndex &&
|
|
106
|
+
n.set(r.index, r), _ === t.streamIndex && u && c(u, r.assEvent);
|
|
118
107
|
}
|
|
119
108
|
},
|
|
120
109
|
selectStream: (e) => {
|
|
121
110
|
let n = e ?? -1;
|
|
122
|
-
if (n === _ || !
|
|
123
|
-
_ = n,
|
|
124
|
-
let r =
|
|
111
|
+
if (n === _ || !u) return;
|
|
112
|
+
_ = n, u.freeTrack();
|
|
113
|
+
let r = f.get(n);
|
|
125
114
|
if (r) {
|
|
126
|
-
|
|
127
|
-
for (let e of
|
|
128
|
-
|
|
115
|
+
u.setTrack(r.content);
|
|
116
|
+
for (let e of g.get(n)?.values() ?? []) c(u, e.assEvent);
|
|
117
|
+
u.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
|
|
129
118
|
}
|
|
130
119
|
},
|
|
131
|
-
getStreams: () => [...
|
|
120
|
+
getStreams: () => [...p],
|
|
132
121
|
getSelectedStream: () => _,
|
|
133
122
|
setOnStreams: (e) => {
|
|
134
123
|
v = e;
|
|
135
124
|
},
|
|
136
125
|
destroy: () => {
|
|
137
|
-
clearInterval(
|
|
126
|
+
clearInterval(ee), t.removeEventListener("ratechange", y), u?.destroy(), u = void 0;
|
|
138
127
|
}
|
|
139
128
|
};
|
|
140
|
-
},
|
|
129
|
+
}, _ = class extends Error {
|
|
141
130
|
mediaElement = !0;
|
|
142
131
|
constructor(e, t) {
|
|
143
132
|
super(e, t), this.name = "MediaElementError";
|
|
144
133
|
}
|
|
145
|
-
},
|
|
134
|
+
}, v = (e) => !!e && typeof e == "object" && e.mediaElement === !0, ee = -20, y = 60, b = 30, x = -5, S = 20, C = 5, w = 15e3, T = 1, E = 3, D = 12, O = 200, k = 250, A = 25e5, j = (e) => {
|
|
146
135
|
let { worker: t } = e, n = setTimeout(() => t.terminate(), 2e3);
|
|
147
136
|
e.destroy().catch(() => {}).finally(() => {
|
|
148
137
|
clearTimeout(n), t.terminate();
|
|
149
138
|
});
|
|
150
|
-
},
|
|
151
|
-
let { videoElement: n, canvasElement: r, read:
|
|
139
|
+
}, te = async (t) => {
|
|
140
|
+
let { videoElement: n, canvasElement: r, read: a, length: s, publicPath: c, libavWorkerUrl: l, jassubWorkerUrl: u, jassubWasmUrl: d, jassubLegacyWasmUrl: f, defaultFontUrl: p, bufferSize: m = A, audioStreamIndex: h, onReady: v, onError: te, onRecovered: ne, onSeek: M, onSubtitleStreams: re, onAudioStreams: ie } = t, N = await e({
|
|
152
141
|
publicPath: c,
|
|
153
142
|
workerUrl: l,
|
|
154
143
|
workerOptions: { type: "module" },
|
|
155
144
|
bufferSize: m,
|
|
156
|
-
length:
|
|
145
|
+
length: s,
|
|
157
146
|
audioStreamIndex: h,
|
|
158
|
-
read:
|
|
159
|
-
}),
|
|
160
|
-
if (!
|
|
161
|
-
|
|
162
|
-
for (let e of [...
|
|
147
|
+
read: a
|
|
148
|
+
}), P = [], F = !1, ae = () => {
|
|
149
|
+
if (!F) {
|
|
150
|
+
F = !0;
|
|
151
|
+
for (let e of [...P].reverse()) try {
|
|
163
152
|
e();
|
|
164
153
|
} catch {}
|
|
165
154
|
}
|
|
166
155
|
};
|
|
167
|
-
|
|
156
|
+
P.push(() => j(N));
|
|
168
157
|
try {
|
|
169
|
-
let e = await
|
|
170
|
-
|
|
171
|
-
let
|
|
158
|
+
let e = await N.init(), t = e.audioStreams ?? [], a = t.some((e) => e.streamIndex === h) ? h : t[0]?.streamIndex ?? -1;
|
|
159
|
+
ie?.(t, a);
|
|
160
|
+
let s = g({
|
|
172
161
|
video: n,
|
|
173
162
|
canvas: r,
|
|
174
163
|
workerUrl: u,
|
|
175
164
|
wasmUrl: d,
|
|
176
165
|
legacyWasmUrl: f,
|
|
177
166
|
defaultFontUrl: p,
|
|
178
|
-
onStreams:
|
|
167
|
+
onStreams: re
|
|
179
168
|
});
|
|
180
|
-
if (
|
|
169
|
+
if (P.push(() => s.destroy()), e.attachments?.length && s.pushAttachments(e.attachments), !e.info.output.videoMimeType) throw Error("The video codec in this file could not be identified");
|
|
181
170
|
let c = [e.info.output.videoMimeType, e.info.output.audioMimeType].filter(Boolean).join(","), l = `video/mp4; codecs="${c}"`;
|
|
182
171
|
if (!MediaSource.isTypeSupported(l)) throw Error(`This browser cannot play the codecs in this file: ${c}`);
|
|
183
|
-
let m = new MediaSource(),
|
|
184
|
-
|
|
172
|
+
let m = new MediaSource(), A = URL.createObjectURL(m);
|
|
173
|
+
P.push(() => URL.revokeObjectURL(A)), n.src = A, P.push(() => {
|
|
185
174
|
try {
|
|
186
175
|
n.pause();
|
|
187
176
|
} catch {}
|
|
188
177
|
n.removeAttribute("src"), n.load();
|
|
189
178
|
});
|
|
190
|
-
let
|
|
179
|
+
let j = await new Promise((t, n) => {
|
|
191
180
|
let r = setTimeout(() => {
|
|
192
181
|
i(), n(/* @__PURE__ */ Error("The MediaSource never opened"));
|
|
193
|
-
},
|
|
182
|
+
}, w), i = () => {
|
|
194
183
|
clearTimeout(r), m.removeEventListener("sourceopen", a), m.removeEventListener("sourceclose", o), m.removeEventListener("error", o);
|
|
195
184
|
}, a = () => {
|
|
196
185
|
try {
|
|
@@ -205,152 +194,152 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
205
194
|
i(), n(/* @__PURE__ */ Error("The MediaSource closed before a SourceBuffer could be created"));
|
|
206
195
|
};
|
|
207
196
|
m.addEventListener("sourceopen", a, { once: !0 }), m.addEventListener("sourceclose", o, { once: !0 }), m.addEventListener("error", o, { once: !0 });
|
|
208
|
-
}), { appendBuffer:
|
|
209
|
-
await
|
|
210
|
-
let
|
|
197
|
+
}), { appendBuffer: I, unbufferRange: oe, updateTimestampOffset: se, endOfStream: ce } = o(j, m);
|
|
198
|
+
await I(e.data), e.subtitles?.length && s.pushFragments(e.subtitles), v?.();
|
|
199
|
+
let L = !1, R = !1, z = 0, B, le = () => B ?? n.currentTime, V = !1, H = 0, U = 0, W = null, G = 0, K = 0, q = !1, ue = 0, J = (e, t, n = !1) => {
|
|
211
200
|
let r = e?.message === "Cancelled";
|
|
212
|
-
t && r || (console.error(e), !(
|
|
213
|
-
},
|
|
214
|
-
if (
|
|
215
|
-
let t = n.currentTime, r = e ?
|
|
216
|
-
for (let { start: e, end: n } of
|
|
217
|
-
},
|
|
218
|
-
let e =
|
|
201
|
+
t && r || (console.error(e), !(q && !n) && (q = !0, te?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
|
|
202
|
+
}, de = async (e = !1) => {
|
|
203
|
+
if (z > 0 && !e) return;
|
|
204
|
+
let t = n.currentTime, r = e ? x : ee, a = e ? S : y;
|
|
205
|
+
for (let { start: e, end: n } of i(j)) e < t + r && await oe(e, t + r), n > t + a && await oe(t + a, n);
|
|
206
|
+
}, fe = () => {
|
|
207
|
+
let e = i(j);
|
|
219
208
|
if (!e.length) return !0;
|
|
220
|
-
let t =
|
|
221
|
-
return n && n.end >= t +
|
|
222
|
-
},
|
|
209
|
+
let t = le(), n = e.find((e) => e.start <= t + T && t < e.end);
|
|
210
|
+
return n && n.end >= t + y ? !1 : !n || n.end < t + b;
|
|
211
|
+
}, pe = () => {
|
|
223
212
|
let e = n.currentTime;
|
|
224
|
-
return
|
|
225
|
-
},
|
|
226
|
-
let e =
|
|
227
|
-
return !!e && e.end >=
|
|
228
|
-
},
|
|
229
|
-
let e =
|
|
230
|
-
e.length && (
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
let t = ++
|
|
213
|
+
return i(j).find((t) => t.start <= e + T && e < t.end);
|
|
214
|
+
}, me = () => {
|
|
215
|
+
let e = pe();
|
|
216
|
+
return !!e && e.end >= K - .1;
|
|
217
|
+
}, he = () => {
|
|
218
|
+
let e = i(j).map((e) => e.end);
|
|
219
|
+
e.length && (K = Math.max(K, ...e));
|
|
220
|
+
}, Y = async (e) => {
|
|
221
|
+
R = !0, W = null, G = 0, ue = e;
|
|
222
|
+
let t = ++U;
|
|
234
223
|
try {
|
|
235
|
-
let { data: n, pts: r, subtitles: i, finished: a } = await
|
|
236
|
-
if (
|
|
237
|
-
n.byteLength && (await
|
|
224
|
+
let { data: n, pts: r, subtitles: i, finished: a } = await N.seek(e);
|
|
225
|
+
if (F || t !== U || (a && (V = !0), i?.length && s.pushFragments(i), await se(r), F || t !== U)) return;
|
|
226
|
+
n.byteLength && (await I(n), he());
|
|
238
227
|
} catch (e) {
|
|
239
|
-
|
|
228
|
+
J(e, F || t !== U);
|
|
240
229
|
} finally {
|
|
241
|
-
t ===
|
|
230
|
+
t === U && (R = !1);
|
|
242
231
|
}
|
|
243
|
-
},
|
|
244
|
-
let e =
|
|
232
|
+
}, ge = async () => {
|
|
233
|
+
let e = W;
|
|
245
234
|
if (e) try {
|
|
246
|
-
await
|
|
235
|
+
await I(e), he(), W === e && (W = null, G = 0);
|
|
247
236
|
} catch (t) {
|
|
248
|
-
if (
|
|
237
|
+
if (W !== e) return;
|
|
249
238
|
if (t?.name === "QuotaExceededError") {
|
|
250
|
-
let t = await
|
|
251
|
-
if (
|
|
239
|
+
let t = await de(!0).then(() => !0, () => !1);
|
|
240
|
+
if (W !== e) return;
|
|
252
241
|
if (t) {
|
|
253
|
-
|
|
242
|
+
V = !1, await Y(pe()?.end ?? n.currentTime);
|
|
254
243
|
return;
|
|
255
244
|
}
|
|
256
245
|
}
|
|
257
|
-
if (
|
|
258
|
-
throw
|
|
246
|
+
if (G += 1, G < C) return;
|
|
247
|
+
throw W = null, t;
|
|
259
248
|
}
|
|
260
|
-
},
|
|
261
|
-
if (
|
|
262
|
-
let e = ++
|
|
263
|
-
|
|
249
|
+
}, _e = async () => {
|
|
250
|
+
if (L || R || $ !== void 0 || F || !W && (V || !fe())) return;
|
|
251
|
+
let e = ++H, t = U, n = () => F || e !== H || t !== U;
|
|
252
|
+
L = !0;
|
|
264
253
|
try {
|
|
265
|
-
if (!
|
|
266
|
-
let { data: e, subtitles: t, finished: r } = await
|
|
267
|
-
if (n() || (r && (
|
|
268
|
-
|
|
254
|
+
if (!W) {
|
|
255
|
+
let { data: e, subtitles: t, finished: r } = await N.read();
|
|
256
|
+
if (n() || (r && (V = !0), t?.length && s.pushFragments(t), !e.byteLength)) return;
|
|
257
|
+
W = e, G = 0;
|
|
269
258
|
}
|
|
270
|
-
await
|
|
259
|
+
await ge(), !n() && q && (q = !1, ne?.());
|
|
271
260
|
} catch (e) {
|
|
272
|
-
|
|
261
|
+
J(e, n());
|
|
273
262
|
} finally {
|
|
274
|
-
e ===
|
|
263
|
+
e === H && (L = !1);
|
|
275
264
|
}
|
|
276
|
-
},
|
|
277
|
-
let t =
|
|
265
|
+
}, ve = (e) => e >= ue && !!pe(), ye = (e) => i(j).some((t) => t.start <= e + T && e < t.end), X = (e) => {
|
|
266
|
+
let t = i(j).find((t) => t.start <= e + T && e < t.end);
|
|
278
267
|
return t ? t.end - e : 0;
|
|
279
|
-
},
|
|
280
|
-
if (!
|
|
281
|
-
|
|
268
|
+
}, be = async (e) => {
|
|
269
|
+
if (!F && !(X(e) >= E)) {
|
|
270
|
+
V = !1, z++, B = e;
|
|
282
271
|
try {
|
|
283
|
-
|
|
284
|
-
let t =
|
|
285
|
-
for (let n = 0; n <
|
|
286
|
-
let t =
|
|
287
|
-
if (await
|
|
272
|
+
ye(e) || await Y(e);
|
|
273
|
+
let t = U;
|
|
274
|
+
for (let n = 0; n < D && !(F || t !== U || V || X(e) >= E); n++) {
|
|
275
|
+
let t = X(e);
|
|
276
|
+
if (await _e(), X(e) <= t) break;
|
|
288
277
|
}
|
|
289
278
|
} finally {
|
|
290
|
-
|
|
279
|
+
z--, B === e && (B = void 0);
|
|
291
280
|
}
|
|
292
281
|
}
|
|
293
|
-
},
|
|
294
|
-
let t = performance.now(), n = t -
|
|
295
|
-
if (n >=
|
|
296
|
-
|
|
282
|
+
}, xe = 0, Z = null, Q, Se = (e) => {
|
|
283
|
+
let t = performance.now(), n = t - xe;
|
|
284
|
+
if (n >= O) {
|
|
285
|
+
xe = t, Z = null, M?.(e);
|
|
297
286
|
return;
|
|
298
287
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
let e =
|
|
302
|
-
|
|
303
|
-
},
|
|
288
|
+
Z = e, !Q && (Q = setTimeout(() => {
|
|
289
|
+
Q = void 0;
|
|
290
|
+
let e = Z;
|
|
291
|
+
Z = null, !(e === null || F) && (xe = performance.now(), M?.(e));
|
|
292
|
+
}, O - n));
|
|
304
293
|
};
|
|
305
|
-
|
|
306
|
-
Z && clearTimeout(Z);
|
|
307
|
-
});
|
|
308
|
-
let we = 0, Q;
|
|
309
|
-
j.push(() => {
|
|
294
|
+
P.push(() => {
|
|
310
295
|
Q && clearTimeout(Q);
|
|
311
296
|
});
|
|
312
|
-
let
|
|
297
|
+
let Ce = 0, $;
|
|
298
|
+
P.push(() => {
|
|
299
|
+
$ && clearTimeout($);
|
|
300
|
+
});
|
|
301
|
+
let we = () => {
|
|
313
302
|
let t = n.currentTime, r = e.info.input.duration || n.duration;
|
|
314
|
-
r > 0 &&
|
|
315
|
-
let i = performance.now(), a = i -
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
318
|
-
|
|
303
|
+
r > 0 && Se(Math.min(Math.max(t / r, 0), 1));
|
|
304
|
+
let i = performance.now(), a = i - Ce < k;
|
|
305
|
+
if (Ce = i, !ve(t) && !(R && Math.abs(t - ue) < .001)) {
|
|
306
|
+
if (V = !1, $ && clearTimeout($), !a) {
|
|
307
|
+
$ = void 0, Y(t);
|
|
319
308
|
return;
|
|
320
309
|
}
|
|
321
|
-
|
|
322
|
-
if (
|
|
310
|
+
$ = setTimeout(() => {
|
|
311
|
+
if ($ = void 0, F) return;
|
|
323
312
|
let e = n.currentTime;
|
|
324
|
-
|
|
325
|
-
},
|
|
313
|
+
ve(e) || (V = !1, Y(e));
|
|
314
|
+
}, k);
|
|
326
315
|
}
|
|
327
316
|
};
|
|
328
|
-
n.addEventListener("seeking",
|
|
329
|
-
let
|
|
330
|
-
if (
|
|
331
|
-
|
|
317
|
+
n.addEventListener("seeking", we), P.push(() => n.removeEventListener("seeking", we));
|
|
318
|
+
let Te = !1, Ee = () => {
|
|
319
|
+
if (Te) return;
|
|
320
|
+
Te = !0;
|
|
332
321
|
let e = n.error;
|
|
333
|
-
|
|
322
|
+
J(new _(`the media element failed: ${e?.message ?? "unknown"}`, { cause: e }), !1, !0);
|
|
334
323
|
};
|
|
335
|
-
n.addEventListener("error", Ee),
|
|
324
|
+
n.addEventListener("error", Ee), P.push(() => n.removeEventListener("error", Ee));
|
|
336
325
|
let De = setInterval(() => {
|
|
337
|
-
|
|
326
|
+
Te || n.error || (de().catch(() => {}), _e().catch((e) => J(e, !1)), V && !W && me() && m.readyState === "open" && ce().catch(() => {}));
|
|
338
327
|
}, 100);
|
|
339
|
-
return
|
|
340
|
-
destroy:
|
|
341
|
-
prepareSeek:
|
|
328
|
+
return P.push(() => clearInterval(De)), {
|
|
329
|
+
destroy: ae,
|
|
330
|
+
prepareSeek: be,
|
|
342
331
|
videoElement: n,
|
|
343
|
-
selectSubtitleStream: (e) =>
|
|
332
|
+
selectSubtitleStream: (e) => s.selectStream(e),
|
|
344
333
|
indexes: e.indexes ?? [],
|
|
345
334
|
duration: e.info.input.duration,
|
|
346
335
|
videoMimeType: e.info.output.videoMimeType,
|
|
347
336
|
audioMimeType: e.info.output.audioMimeType
|
|
348
337
|
};
|
|
349
338
|
} catch (e) {
|
|
350
|
-
throw
|
|
339
|
+
throw ae(), e;
|
|
351
340
|
}
|
|
352
|
-
},
|
|
353
|
-
let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s =
|
|
341
|
+
}, ne = 5, M = 500, re = 320, ie = 1e6, N = 3, P = 1e4, F = async (e) => {
|
|
342
|
+
let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s = re } = e, c = await t({
|
|
354
343
|
publicPath: n,
|
|
355
344
|
workerUrl: r,
|
|
356
345
|
workerOptions: { type: "module" },
|
|
@@ -358,14 +347,14 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
358
347
|
read: a
|
|
359
348
|
});
|
|
360
349
|
try {
|
|
361
|
-
let t = await c.init(), n = t.duration, r = Math.max(e.interval ??
|
|
350
|
+
let t = await c.init(), n = t.duration, r = Math.max(e.interval ?? ne, n / M), a = [];
|
|
362
351
|
for (let [e, o] of t.indexes.entries()) {
|
|
363
352
|
let s = a.at(-1);
|
|
364
353
|
s && o.timestamp - s.timestamp < r || a.push({
|
|
365
354
|
timestamp: o.timestamp,
|
|
366
355
|
endTime: n,
|
|
367
356
|
startByte: o.pos,
|
|
368
|
-
endByte: Math.min((t.indexes[e + 1]?.pos ?? i) +
|
|
357
|
+
endByte: Math.min((t.indexes[e + 1]?.pos ?? i) + ie, i),
|
|
369
358
|
done: !1,
|
|
370
359
|
attempts: 0
|
|
371
360
|
});
|
|
@@ -396,7 +385,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
396
385
|
}, p = (e) => {
|
|
397
386
|
e.done = !0, d = d.then(async () => {
|
|
398
387
|
if (u) return;
|
|
399
|
-
let t = await Promise.race([c.readKeyframe(e.timestamp), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")),
|
|
388
|
+
let t = await Promise.race([c.readKeyframe(e.timestamp), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")), P))]), n = await createImageBitmap(new Blob([t], { type: "image/png" })), r = new OffscreenCanvas(s, Math.max(1, Math.round(n.height * (s / n.width))));
|
|
400
389
|
r.getContext("2d").drawImage(n, 0, 0, r.width, r.height), n.close();
|
|
401
390
|
let i = await r.convertToBlob({
|
|
402
391
|
type: "image/webp",
|
|
@@ -408,7 +397,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
408
397
|
endTime: e.endTime
|
|
409
398
|
}].sort((e, t) => e.startTime - t.startTime), f());
|
|
410
399
|
}).catch(() => {
|
|
411
|
-
e.attempts += 1, e.done = e.attempts >=
|
|
400
|
+
e.attempts += 1, e.done = e.attempts >= N;
|
|
412
401
|
});
|
|
413
402
|
};
|
|
414
403
|
return f(), {
|
|
@@ -418,14 +407,14 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
418
407
|
destroy: () => {
|
|
419
408
|
u = !0;
|
|
420
409
|
for (let e of l) URL.revokeObjectURL(e.url);
|
|
421
|
-
l = [],
|
|
410
|
+
l = [], j(c);
|
|
422
411
|
}
|
|
423
412
|
};
|
|
424
413
|
} catch (e) {
|
|
425
|
-
throw
|
|
414
|
+
throw j(c), e;
|
|
426
415
|
}
|
|
427
|
-
},
|
|
428
|
-
let { video: t, canvas: n } = e, r = e.mode ??
|
|
416
|
+
}, ae = 1280, I = 1920, oe = 250, se = 100, ce = () => typeof HTMLCanvasElement < "u" && "captureStream" in HTMLCanvasElement.prototype, L = () => typeof document < "u" && typeof HTMLVideoElement < "u" && typeof HTMLVideoElement.prototype.requestPictureInPicture == "function" && !!document.pictureInPictureEnabled, R = () => typeof HTMLVideoElement < "u" && "mozCaptureStream" in HTMLVideoElement.prototype, z = () => ce() ? L() ? "window" : R() ? "burn-in" : null : null, B = (e) => {
|
|
417
|
+
let { video: t, canvas: n } = e, r = e.mode ?? z() ?? "window", i = e.maxWidth ?? (r === "burn-in" ? I : ae), a, o, s, c = !1, l = !1, u = !1, d = !1, f, p = () => r === "burn-in" ? !!a : !!a && document.pictureInPictureElement === a.mirror, m = (e, r) => {
|
|
429
418
|
r.drawImage(t, 0, 0, e.width, e.height), n.width > 0 && n.height > 0 && r.drawImage(n, 0, 0, e.width, e.height);
|
|
430
419
|
}, h = () => {
|
|
431
420
|
a && m(a.composite, a.context);
|
|
@@ -433,58 +422,58 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
433
422
|
h(), v();
|
|
434
423
|
}, v = () => {
|
|
435
424
|
d || !a || (o = g ? t.requestVideoFrameCallback(_) : requestAnimationFrame(_));
|
|
436
|
-
},
|
|
425
|
+
}, ee = () => {
|
|
437
426
|
o !== void 0 && (g ? t.cancelVideoFrameCallback(o) : cancelAnimationFrame(o)), s !== void 0 && clearInterval(s), o = void 0, s = void 0;
|
|
438
|
-
},
|
|
427
|
+
}, y = () => {
|
|
439
428
|
"mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
|
|
440
|
-
},
|
|
429
|
+
}, b = () => {
|
|
441
430
|
c || l || !t.paused || t.play().catch(() => {});
|
|
442
|
-
},
|
|
431
|
+
}, x = () => {
|
|
443
432
|
c || l || (t.paused || t.pause(), r !== "burn-in" && (c = !0, a?.mirror.play().catch(() => {}).finally(() => {
|
|
444
433
|
c = !1;
|
|
445
434
|
})));
|
|
435
|
+
}, S = () => {
|
|
436
|
+
y(), a?.mirror.play().catch(() => {});
|
|
446
437
|
}, C = () => {
|
|
447
|
-
|
|
438
|
+
y(), r === "burn-in" && a?.mirror.pause();
|
|
448
439
|
}, w = () => {
|
|
449
|
-
b(), r === "burn-in" && a?.mirror.pause();
|
|
450
|
-
}, T = () => {
|
|
451
440
|
if (r !== "burn-in" || !a || !t.paused || l) return;
|
|
452
441
|
l = !0;
|
|
453
442
|
let { mirror: e } = a;
|
|
454
443
|
h(), e.play().then(() => new Promise((e) => {
|
|
455
|
-
setTimeout(e,
|
|
444
|
+
setTimeout(e, se);
|
|
456
445
|
})).catch(() => {}).finally(() => {
|
|
457
446
|
e.pause(), setTimeout(() => {
|
|
458
447
|
l = !1;
|
|
459
|
-
},
|
|
448
|
+
}, se);
|
|
460
449
|
});
|
|
461
|
-
},
|
|
450
|
+
}, T = [["play", () => {
|
|
462
451
|
t.play().catch(() => {});
|
|
463
452
|
}], ["pause", () => t.pause()]], E = (e) => {
|
|
464
|
-
if ("mediaSession" in navigator) for (let [t, n] of
|
|
453
|
+
if ("mediaSession" in navigator) for (let [t, n] of T) try {
|
|
465
454
|
navigator.mediaSession.setActionHandler(t, e ? n : null);
|
|
466
455
|
} catch {}
|
|
467
456
|
}, D = () => {
|
|
468
|
-
if (
|
|
457
|
+
if (ee(), E(!1), t.removeEventListener("play", S), t.removeEventListener("pause", C), t.removeEventListener("seeked", w), !a) {
|
|
469
458
|
f?.();
|
|
470
459
|
return;
|
|
471
460
|
}
|
|
472
461
|
let { stream: e, mirror: n } = a;
|
|
473
|
-
n.removeEventListener("play",
|
|
462
|
+
n.removeEventListener("play", b), n.removeEventListener("pause", x), n.removeEventListener("leavepictureinpicture", O);
|
|
474
463
|
for (let t of e.getTracks()) t.stop();
|
|
475
464
|
n.remove(), a = void 0, f?.();
|
|
476
|
-
},
|
|
465
|
+
}, O = () => D(), k = (e) => {
|
|
477
466
|
let r = t.style.opacity, i = n.style.display;
|
|
478
467
|
e.style.cssText = "position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000;z-index:1", t.style.opacity = "0", n.style.display = "none", f = () => {
|
|
479
468
|
f = void 0, t.style.opacity = r, n.style.display = i;
|
|
480
469
|
};
|
|
481
|
-
},
|
|
470
|
+
}, A = async () => {
|
|
482
471
|
let n = Math.min(t.videoWidth || i, i), o = Math.round(n * ((t.videoHeight || 9) / (t.videoWidth || 16))), c = document.createElement("canvas");
|
|
483
472
|
c.width = n, c.height = o;
|
|
484
473
|
let l = c.getContext("2d");
|
|
485
474
|
if (!l) throw Error("the picture in picture composite has no 2d context");
|
|
486
475
|
let u = document.createElement("video");
|
|
487
|
-
u.muted = !0, u.playsInline = !0, r === "burn-in" ?
|
|
476
|
+
u.muted = !0, u.playsInline = !0, r === "burn-in" ? k(u) : u.style.cssText = "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;left:-1px;top:-1px", m(c, l);
|
|
488
477
|
let f = {
|
|
489
478
|
composite: c,
|
|
490
479
|
context: l,
|
|
@@ -493,7 +482,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
493
482
|
};
|
|
494
483
|
if (a = f, u.srcObject = f.stream, (e.container ?? t.parentElement ?? document.body).appendChild(u), v(), s = setInterval(() => {
|
|
495
484
|
t.paused && h();
|
|
496
|
-
},
|
|
485
|
+
}, oe), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
|
|
497
486
|
let t = () => {
|
|
498
487
|
clearTimeout(n), e();
|
|
499
488
|
}, n = setTimeout(t, 1e3);
|
|
@@ -502,7 +491,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
502
491
|
u.remove();
|
|
503
492
|
return;
|
|
504
493
|
}
|
|
505
|
-
if (u.addEventListener("play",
|
|
494
|
+
if (u.addEventListener("play", b), u.addEventListener("pause", x), u.addEventListener("leavepictureinpicture", O), t.addEventListener("play", S), t.addEventListener("pause", C), t.addEventListener("seeked", w), E(!0), y(), r === "burn-in") {
|
|
506
495
|
t.paused && u.pause(), e.onBurnedInChange?.(!0);
|
|
507
496
|
return;
|
|
508
497
|
}
|
|
@@ -523,7 +512,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
523
512
|
}
|
|
524
513
|
r !== "burn-in" && document.pictureInPictureElement && await document.exitPictureInPicture().catch(() => {});
|
|
525
514
|
try {
|
|
526
|
-
await
|
|
515
|
+
await A();
|
|
527
516
|
} catch (t) {
|
|
528
517
|
console.warn("subtitle compositing for picture in picture failed", t), D(), r === "burn-in" && e.onBurnedInChange?.(!1);
|
|
529
518
|
}
|
|
@@ -539,4 +528,4 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
539
528
|
};
|
|
540
529
|
};
|
|
541
530
|
//#endregion
|
|
542
|
-
export {
|
|
531
|
+
export { _ as a, j as c, i as d, o as f, A as i, s as l, z as n, v as o, F as r, te as s, B as t, g as u };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as e, n as t, o as n, r, s as i, t as a, u as o } from "./engine-
|
|
1
|
+
import { l as e, n as t, o as n, r, s as i, t as a, u as o } from "./engine-B8CKGIng.js";
|
|
2
2
|
import { Children as s, Fragment as c, isValidElement as l, useCallback as u, useEffect as d, useMemo as f, useRef as p, useState as m } from "react";
|
|
3
3
|
import { css as h, keyframes as g } from "@emotion/react";
|
|
4
4
|
import { createPlayer as _, useContainerAttach as v, useMediaAttach as y } from "@videojs/react";
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import type { ASS_Event } from 'jassub'
|
|
|
2
2
|
import type { Attachment, SubtitleFragment } from 'libav-wasm/build/worker'
|
|
3
3
|
|
|
4
4
|
import JASSUB from 'jassub'
|
|
5
|
-
import { parse
|
|
5
|
+
import { parse } from 'ass-compiler'
|
|
6
6
|
|
|
7
7
|
export type SubtitleStream = { streamIndex: number, title: string, language: string }
|
|
8
8
|
|
|
@@ -35,11 +35,30 @@ export type SubtitleRendererOptions = {
|
|
|
35
35
|
* which throws. So leaving this unset does not degrade to the slower build, it fails outright.
|
|
36
36
|
*/
|
|
37
37
|
legacyWasmUrl?: string
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Fallback face for `liberation sans`. Without it jassub falls back to whatever the wasm build embeds.
|
|
40
|
+
*
|
|
41
|
+
* May be relative: it is resolved against the document before the worker sees it, for the reason
|
|
42
|
+
* given on `workerFetched`.
|
|
43
|
+
*/
|
|
39
44
|
defaultFontUrl?: string
|
|
40
45
|
onStreams?: (streams: SubtitleStream[]) => void
|
|
41
46
|
}
|
|
42
47
|
|
|
48
|
+
/**
|
|
49
|
+
* A url the WORKER will fetch, resolved against the document first.
|
|
50
|
+
*
|
|
51
|
+
* jassub's worker is built from a `blob:` url, so a relative url handed to it resolves against that
|
|
52
|
+
* blob inside the worker and the fetch never lands. For the wasm that fails loudly. For the fallback
|
|
53
|
+
* font it fails in SILENCE: libass is left with no face to draw with, so the track renders nothing at
|
|
54
|
+
* all and the picture simply has no subtitles on it, with no error anywhere to say why.
|
|
55
|
+
*
|
|
56
|
+
* `workerUrl` is deliberately not passed through here. `new Worker()` is called on the main thread,
|
|
57
|
+
* where a relative url already resolves against the document.
|
|
58
|
+
*/
|
|
59
|
+
const workerFetched = (url: string | undefined) =>
|
|
60
|
+
url === undefined ? undefined : new URL(url, document.baseURI).toString()
|
|
61
|
+
|
|
43
62
|
const convertTimestamp = (ms: number) => new Date(ms).toISOString().slice(11, 22)
|
|
44
63
|
|
|
45
64
|
/**
|
|
@@ -73,12 +92,6 @@ const styleIndex = (header: SubtitleHeaderPart, name: string) => {
|
|
|
73
92
|
return header.styles.get(key) ?? header.styles.get('Default') ?? 0
|
|
74
93
|
}
|
|
75
94
|
|
|
76
|
-
// cleared so jassub scales the script to the canvas, not to the authored resolution
|
|
77
|
-
const renderable = (content: string) => {
|
|
78
|
-
const parsed = parse(content)
|
|
79
|
-
return stringify({ ...parsed, info: { ...parsed.info, ScaledBorderAndShadow: 'no', LayoutResX: '', LayoutResY: '' } })
|
|
80
|
-
}
|
|
81
|
-
|
|
82
95
|
/**
|
|
83
96
|
* `\r?\n`, not `\r\n`: an ASS header muxed straight out of a matroska file uses CRLF, but one libav
|
|
84
97
|
* CONVERTED from another format (an srt track, most commonly) is LF only, and requiring CRLF rejected it.
|
|
@@ -126,7 +139,10 @@ const toDialoguePart = (header: SubtitleHeaderPart, fragment: SubtitleFragment &
|
|
|
126
139
|
export type SubtitleRenderer = ReturnType<typeof createSubtitleRenderer>
|
|
127
140
|
|
|
128
141
|
export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
129
|
-
const { video, canvas, workerUrl
|
|
142
|
+
const { video, canvas, workerUrl } = options
|
|
143
|
+
const wasmUrl = workerFetched(options.wasmUrl)!
|
|
144
|
+
const legacyWasmUrl = workerFetched(options.legacyWasmUrl)
|
|
145
|
+
const defaultFontUrl = workerFetched(options.defaultFontUrl)
|
|
130
146
|
let jassub: JASSUB | undefined
|
|
131
147
|
let attachments: [string, Uint8Array][] = []
|
|
132
148
|
const headers = new Map<number, SubtitleHeaderPart>()
|
|
@@ -146,7 +162,7 @@ export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
|
146
162
|
onDemandRender: false,
|
|
147
163
|
video,
|
|
148
164
|
canvas,
|
|
149
|
-
subContent:
|
|
165
|
+
subContent: header.content,
|
|
150
166
|
workerUrl,
|
|
151
167
|
modernWasmUrl: wasmUrl,
|
|
152
168
|
...legacyWasmUrl ? { wasmUrl: legacyWasmUrl } : {},
|
|
@@ -196,7 +212,7 @@ export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
|
196
212
|
jassub.freeTrack()
|
|
197
213
|
const header = headers.get(next)
|
|
198
214
|
if (!header) return
|
|
199
|
-
jassub.setTrack(
|
|
215
|
+
jassub.setTrack(header.content)
|
|
200
216
|
for (const part of dialogues.get(next)?.values() ?? []) createEvent(jassub, part.assEvent)
|
|
201
217
|
jassub.setCurrentTime(video.paused, video.currentTime, video.playbackRate)
|
|
202
218
|
}
|