@banou/media-player 0.8.15 → 0.8.16
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 +3 -0
- package/dist/engine/index.js +1 -1
- package/dist/{engine-CDdjRm_U.js → engine-DPnChmYc.js} +144 -169
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/lib/engine/subtitles.ts +46 -38
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
1
3
|
# @banou/media-player
|
|
2
4
|
|
|
3
5
|
A React video player for files the browser cannot open on its own. It takes a `read(offset, size)` and
|
|
@@ -21,6 +23,7 @@ import MediaPlayer from '@banou/media-player'
|
|
|
21
23
|
libavWorkerUrl="/libav-worker.js"
|
|
22
24
|
jassubWorkerUrl={jassubWorkerUrl}
|
|
23
25
|
jassubWasmUrl="/jassub-worker-modern.wasm"
|
|
26
|
+
jassubLegacyWasmUrl="/jassub-worker.wasm"
|
|
24
27
|
defaultFontUrl="/default.woff2"
|
|
25
28
|
title="episode.mkv"
|
|
26
29
|
autoplay
|
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-DPnChmYc.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 };
|
|
@@ -32,34 +32,10 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
32
32
|
} catch {}
|
|
33
33
|
})
|
|
34
34
|
};
|
|
35
|
-
}, c = -1, l = (e) => new Date(e).toISOString().slice(11, 22),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Justify: 0,
|
|
40
|
-
FontName: t.Fontname,
|
|
41
|
-
FontSize: Number(t.Fontsize),
|
|
42
|
-
PrimaryColour: Number(t.PrimaryColour),
|
|
43
|
-
SecondaryColour: Number(t.SecondaryColour),
|
|
44
|
-
OutlineColour: Number(t.OutlineColour),
|
|
45
|
-
BackColour: Number(t.BackColour),
|
|
46
|
-
Bold: Number(t.Bold),
|
|
47
|
-
Italic: Number(t.Italic),
|
|
48
|
-
Underline: Number(t.Underline),
|
|
49
|
-
StrikeOut: Number(t.StrikeOut),
|
|
50
|
-
ScaleX: Number(t.ScaleX),
|
|
51
|
-
ScaleY: Number(t.ScaleY),
|
|
52
|
-
Spacing: Number(t.Spacing),
|
|
53
|
-
Angle: Number(t.Angle),
|
|
54
|
-
BorderStyle: Number(t.BorderStyle),
|
|
55
|
-
Outline: Number(t.Outline),
|
|
56
|
-
Shadow: Number(t.Shadow),
|
|
57
|
-
Alignment: Number(t.Alignment),
|
|
58
|
-
MarginL: Number(t.MarginL),
|
|
59
|
-
MarginR: Number(t.MarginR),
|
|
60
|
-
MarginV: Number(t.MarginV),
|
|
61
|
-
Encoding: Number(t.Encoding)
|
|
62
|
-
}), d = (e) => {
|
|
35
|
+
}, c = -1, l = (e, t) => e.createEvent(t), 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
|
+
let n = t.replace(/^\*+/, ""), r = n.toLowerCase() === "default" ? "Default" : n;
|
|
37
|
+
return e.styles.get(r) ?? e.styles.get("Default") ?? 0;
|
|
38
|
+
}, m = (e) => {
|
|
63
39
|
let t = r(e);
|
|
64
40
|
return i({
|
|
65
41
|
...t,
|
|
@@ -70,17 +46,17 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
70
46
|
LayoutResY: ""
|
|
71
47
|
}
|
|
72
48
|
});
|
|
73
|
-
},
|
|
49
|
+
}, h = (e) => {
|
|
74
50
|
let t = e.content.match(/\r?\n\[Events\]\r?\nFormat: (.*)/)?.[0];
|
|
75
51
|
return t ? {
|
|
76
52
|
type: "header",
|
|
77
53
|
streamIndex: e.streamIndex,
|
|
78
54
|
content: e.content,
|
|
79
55
|
eventsContent: t,
|
|
80
|
-
|
|
56
|
+
styles: f(e.content)
|
|
81
57
|
} : (console.warn(`subtitle stream ${e.streamIndex} has no Events format, ignoring the track`), null);
|
|
82
|
-
},
|
|
83
|
-
let [n, i] = t.content.split(","), a = Number(n), o = `Dialogue: ${i},${
|
|
58
|
+
}, g = (e, t) => {
|
|
59
|
+
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];
|
|
84
60
|
if (!s) throw Error("dialogue event is undefined");
|
|
85
61
|
return {
|
|
86
62
|
type: "dialogue",
|
|
@@ -88,6 +64,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
88
64
|
index: a,
|
|
89
65
|
assEvent: {
|
|
90
66
|
...s,
|
|
67
|
+
Style: p(e, s.Style),
|
|
91
68
|
Effect: s.Effect ?? "",
|
|
92
69
|
Text: s.Text.raw,
|
|
93
70
|
Duration: (s.End - s.Start) * 1e3,
|
|
@@ -97,63 +74,61 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
97
74
|
_index: a
|
|
98
75
|
}
|
|
99
76
|
};
|
|
100
|
-
},
|
|
101
|
-
let { video: t, canvas: r, workerUrl: i, wasmUrl: a, legacyWasmUrl: o, defaultFontUrl: s } = e, c,
|
|
77
|
+
}, _ = (e) => {
|
|
78
|
+
let { video: t, canvas: r, workerUrl: i, wasmUrl: a, legacyWasmUrl: o, defaultFontUrl: s } = e, c, u = [], d = /* @__PURE__ */ new Map(), f = [], p = /* @__PURE__ */ new Map(), _, v = e.onStreams, y = setInterval(() => {
|
|
102
79
|
c?.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
|
|
103
80
|
}, 100), b = () => c?.setRate(t.playbackRate), x = (e) => {
|
|
104
81
|
c = new n({
|
|
105
82
|
onDemandRender: !1,
|
|
106
83
|
video: t,
|
|
107
84
|
canvas: r,
|
|
108
|
-
subContent:
|
|
85
|
+
subContent: m(e.content),
|
|
109
86
|
workerUrl: i,
|
|
110
87
|
modernWasmUrl: a,
|
|
111
88
|
...o ? { wasmUrl: o } : {},
|
|
112
|
-
fonts:
|
|
89
|
+
fonts: u.map(([, e]) => e),
|
|
113
90
|
availableFonts: {
|
|
114
|
-
...Object.fromEntries(
|
|
91
|
+
...Object.fromEntries(u),
|
|
115
92
|
...s ? { "liberation sans": s } : {}
|
|
116
93
|
}
|
|
117
94
|
}), t.removeEventListener("ratechange", c._boundSetRate), t.addEventListener("ratechange", b);
|
|
118
|
-
for (let t of e.parsed.styles.style) u(c, t);
|
|
119
95
|
};
|
|
120
96
|
return {
|
|
121
97
|
pushAttachments: (e) => {
|
|
122
|
-
|
|
98
|
+
u = [...u, ...e.map((e) => [e.filename, new Uint8Array(e.data)])];
|
|
123
99
|
},
|
|
124
100
|
pushFragments: (e) => {
|
|
125
101
|
for (let t of e) if (t.type === "header") {
|
|
126
|
-
if (
|
|
127
|
-
let e =
|
|
102
|
+
if (d.has(t.streamIndex)) continue;
|
|
103
|
+
let e = h(t);
|
|
128
104
|
if (!e) continue;
|
|
129
|
-
|
|
105
|
+
d.set(t.streamIndex, e), f.push({
|
|
130
106
|
streamIndex: t.streamIndex,
|
|
131
107
|
title: t.title,
|
|
132
108
|
language: t.language
|
|
133
|
-
}), v?.([...
|
|
109
|
+
}), v?.([...f]), _ === void 0 && (_ = t.streamIndex), c || x(e);
|
|
134
110
|
} else {
|
|
135
|
-
let e =
|
|
111
|
+
let e = d.get(t.streamIndex);
|
|
136
112
|
if (!e) continue;
|
|
137
|
-
let n =
|
|
138
|
-
n || (n = /* @__PURE__ */ new Map(),
|
|
139
|
-
let r =
|
|
113
|
+
let n = p.get(t.streamIndex);
|
|
114
|
+
n || (n = /* @__PURE__ */ new Map(), p.set(t.streamIndex, n));
|
|
115
|
+
let r = g(e, t);
|
|
140
116
|
if (n.has(r.index)) continue;
|
|
141
|
-
n.set(r.index, r), _ === t.streamIndex && c
|
|
117
|
+
n.set(r.index, r), _ === t.streamIndex && c && l(c, r.assEvent);
|
|
142
118
|
}
|
|
143
119
|
},
|
|
144
120
|
selectStream: (e) => {
|
|
145
121
|
let n = e ?? -1;
|
|
146
122
|
if (n === _ || !c) return;
|
|
147
123
|
_ = n, c.freeTrack();
|
|
148
|
-
let r =
|
|
124
|
+
let r = d.get(n);
|
|
149
125
|
if (r) {
|
|
150
|
-
c.setTrack(
|
|
151
|
-
for (let e of
|
|
152
|
-
for (let e of g.get(n)?.values() ?? []) c.createEvent(e.assEvent);
|
|
126
|
+
c.setTrack(m(r.content));
|
|
127
|
+
for (let e of p.get(n)?.values() ?? []) l(c, e.assEvent);
|
|
153
128
|
c.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
|
|
154
129
|
}
|
|
155
130
|
},
|
|
156
|
-
getStreams: () => [...
|
|
131
|
+
getStreams: () => [...f],
|
|
157
132
|
getSelectedStream: () => _,
|
|
158
133
|
setOnStreams: (e) => {
|
|
159
134
|
v = e;
|
|
@@ -162,24 +137,24 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
162
137
|
clearInterval(y), t.removeEventListener("ratechange", b), c?.destroy(), c = void 0;
|
|
163
138
|
}
|
|
164
139
|
};
|
|
165
|
-
},
|
|
140
|
+
}, v = class extends Error {
|
|
166
141
|
mediaElement = !0;
|
|
167
142
|
constructor(e, t) {
|
|
168
143
|
super(e, t), this.name = "MediaElementError";
|
|
169
144
|
}
|
|
170
|
-
},
|
|
145
|
+
}, y = (e) => !!e && typeof e == "object" && e.mediaElement === !0, b = -20, x = 60, S = 30, C = -5, w = 20, T = 5, ee = 15e3, E = 1, D = 3, te = 12, ne = 200, re = 250, O = 25e5, k = (e) => {
|
|
171
146
|
let { worker: t } = e, n = setTimeout(() => t.terminate(), 2e3);
|
|
172
147
|
e.destroy().catch(() => {}).finally(() => {
|
|
173
148
|
clearTimeout(n), t.terminate();
|
|
174
149
|
});
|
|
175
|
-
},
|
|
176
|
-
let { videoElement: n, canvasElement: r, read: i, length: o, publicPath: c, libavWorkerUrl: l, jassubWorkerUrl: u, jassubWasmUrl: d, jassubLegacyWasmUrl: f, defaultFontUrl: p, bufferSize:
|
|
150
|
+
}, ie = async (t) => {
|
|
151
|
+
let { videoElement: n, canvasElement: r, read: i, length: o, publicPath: c, libavWorkerUrl: l, jassubWorkerUrl: u, jassubWasmUrl: d, jassubLegacyWasmUrl: f, defaultFontUrl: p, bufferSize: m = O, audioStreamIndex: h, onReady: g, onError: y, onRecovered: ie, onSeek: ae, onSubtitleStreams: oe, onAudioStreams: se } = t, A = await e({
|
|
177
152
|
publicPath: c,
|
|
178
153
|
workerUrl: l,
|
|
179
154
|
workerOptions: { type: "module" },
|
|
180
|
-
bufferSize:
|
|
155
|
+
bufferSize: m,
|
|
181
156
|
length: o,
|
|
182
|
-
audioStreamIndex:
|
|
157
|
+
audioStreamIndex: h,
|
|
183
158
|
read: i
|
|
184
159
|
}), j = [], M = !1, N = () => {
|
|
185
160
|
if (!M) {
|
|
@@ -189,11 +164,11 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
189
164
|
} catch {}
|
|
190
165
|
}
|
|
191
166
|
};
|
|
192
|
-
j.push(() =>
|
|
167
|
+
j.push(() => k(A));
|
|
193
168
|
try {
|
|
194
|
-
let e = await A.init(), t = e.audioStreams ?? [], i = t.some((e) => e.streamIndex ===
|
|
169
|
+
let e = await A.init(), t = e.audioStreams ?? [], i = t.some((e) => e.streamIndex === h) ? h : t[0]?.streamIndex ?? -1;
|
|
195
170
|
se?.(t, i);
|
|
196
|
-
let o =
|
|
171
|
+
let o = _({
|
|
197
172
|
video: n,
|
|
198
173
|
canvas: r,
|
|
199
174
|
workerUrl: u,
|
|
@@ -205,85 +180,85 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
205
180
|
if (j.push(() => o.destroy()), e.attachments?.length && o.pushAttachments(e.attachments), !e.info.output.videoMimeType) throw Error("The video codec in this file could not be identified");
|
|
206
181
|
let c = [e.info.output.videoMimeType, e.info.output.audioMimeType].filter(Boolean).join(","), l = `video/mp4; codecs="${c}"`;
|
|
207
182
|
if (!MediaSource.isTypeSupported(l)) throw Error(`This browser cannot play the codecs in this file: ${c}`);
|
|
208
|
-
let
|
|
209
|
-
j.push(() => URL.revokeObjectURL(
|
|
183
|
+
let m = new MediaSource(), O = URL.createObjectURL(m);
|
|
184
|
+
j.push(() => URL.revokeObjectURL(O)), n.src = O, j.push(() => {
|
|
210
185
|
try {
|
|
211
186
|
n.pause();
|
|
212
187
|
} catch {}
|
|
213
188
|
n.removeAttribute("src"), n.load();
|
|
214
189
|
});
|
|
215
|
-
let
|
|
190
|
+
let k = await new Promise((t, n) => {
|
|
216
191
|
let r = setTimeout(() => {
|
|
217
192
|
i(), n(/* @__PURE__ */ Error("The MediaSource never opened"));
|
|
218
|
-
},
|
|
219
|
-
clearTimeout(r),
|
|
193
|
+
}, ee), i = () => {
|
|
194
|
+
clearTimeout(r), m.removeEventListener("sourceopen", a), m.removeEventListener("sourceclose", o), m.removeEventListener("error", o);
|
|
220
195
|
}, a = () => {
|
|
221
196
|
try {
|
|
222
|
-
let n =
|
|
197
|
+
let n = m.addSourceBuffer(l);
|
|
223
198
|
n.mode = "segments";
|
|
224
199
|
let r = e.info.input.duration;
|
|
225
|
-
Number.isFinite(r) && r > 0 && (
|
|
200
|
+
Number.isFinite(r) && r > 0 && (m.duration = r), i(), t(n);
|
|
226
201
|
} catch (e) {
|
|
227
202
|
i(), n(e);
|
|
228
203
|
}
|
|
229
204
|
}, o = () => {
|
|
230
205
|
i(), n(/* @__PURE__ */ Error("The MediaSource closed before a SourceBuffer could be created"));
|
|
231
206
|
};
|
|
232
|
-
|
|
233
|
-
}), { appendBuffer: P, unbufferRange: ce, updateTimestampOffset: le, endOfStream: ue } = s(
|
|
234
|
-
await P(e.data), e.subtitles?.length && o.pushFragments(e.subtitles),
|
|
235
|
-
let F = !1, I = !1, L = 0, R, de = () => R ?? n.currentTime, z = !1, B = 0, V = 0, H = null, U = 0, W = 0, G = !1,
|
|
207
|
+
m.addEventListener("sourceopen", a, { once: !0 }), m.addEventListener("sourceclose", o, { once: !0 }), m.addEventListener("error", o, { once: !0 });
|
|
208
|
+
}), { appendBuffer: P, unbufferRange: ce, updateTimestampOffset: le, endOfStream: ue } = s(k, m);
|
|
209
|
+
await P(e.data), e.subtitles?.length && o.pushFragments(e.subtitles), g?.();
|
|
210
|
+
let F = !1, I = !1, L = 0, R, de = () => R ?? n.currentTime, z = !1, B = 0, V = 0, H = null, U = 0, W = 0, G = !1, fe = 0, K = (e, t, n = !1) => {
|
|
236
211
|
let r = e?.message === "Cancelled";
|
|
237
|
-
t && r || (console.error(e), !(G && !n) && (G = !0,
|
|
238
|
-
},
|
|
212
|
+
t && r || (console.error(e), !(G && !n) && (G = !0, y?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
|
|
213
|
+
}, pe = async (e = !1) => {
|
|
239
214
|
if (L > 0 && !e) return;
|
|
240
|
-
let t = n.currentTime, r = e ?
|
|
241
|
-
for (let { start: e, end: n } of a(
|
|
242
|
-
}, pe = () => {
|
|
243
|
-
let e = a(O);
|
|
244
|
-
if (!e.length) return !0;
|
|
245
|
-
let t = de(), n = e.find((e) => e.start <= t + C && t < e.end);
|
|
246
|
-
return n && n.end >= t + v ? !1 : !n || n.end < t + y;
|
|
215
|
+
let t = n.currentTime, r = e ? C : b, i = e ? w : x;
|
|
216
|
+
for (let { start: e, end: n } of a(k)) e < t + r && await ce(e, t + r), n > t + i && await ce(t + i, n);
|
|
247
217
|
}, me = () => {
|
|
248
|
-
let e =
|
|
249
|
-
|
|
218
|
+
let e = a(k);
|
|
219
|
+
if (!e.length) return !0;
|
|
220
|
+
let t = de(), n = e.find((e) => e.start <= t + E && t < e.end);
|
|
221
|
+
return n && n.end >= t + x ? !1 : !n || n.end < t + S;
|
|
250
222
|
}, he = () => {
|
|
251
|
-
let e =
|
|
252
|
-
return
|
|
223
|
+
let e = n.currentTime;
|
|
224
|
+
return a(k).find((t) => t.start <= e + E && e < t.end);
|
|
253
225
|
}, ge = () => {
|
|
254
|
-
let e =
|
|
226
|
+
let e = he();
|
|
227
|
+
return !!e && e.end >= W - .1;
|
|
228
|
+
}, _e = () => {
|
|
229
|
+
let e = a(k).map((e) => e.end);
|
|
255
230
|
e.length && (W = Math.max(W, ...e));
|
|
256
|
-
},
|
|
257
|
-
I = !0, H = null, U = 0,
|
|
231
|
+
}, q = async (e) => {
|
|
232
|
+
I = !0, H = null, U = 0, fe = e;
|
|
258
233
|
let t = ++V;
|
|
259
234
|
try {
|
|
260
235
|
let { data: n, pts: r, subtitles: i, finished: a } = await A.seek(e);
|
|
261
236
|
if (M || t !== V || (a && (z = !0), i?.length && o.pushFragments(i), await le(r), M || t !== V)) return;
|
|
262
|
-
n.byteLength && (await P(n),
|
|
237
|
+
n.byteLength && (await P(n), _e());
|
|
263
238
|
} catch (e) {
|
|
264
|
-
|
|
239
|
+
K(e, M || t !== V);
|
|
265
240
|
} finally {
|
|
266
241
|
t === V && (I = !1);
|
|
267
242
|
}
|
|
268
|
-
},
|
|
243
|
+
}, ve = async () => {
|
|
269
244
|
let e = H;
|
|
270
245
|
if (e) try {
|
|
271
|
-
await P(e),
|
|
246
|
+
await P(e), _e(), H === e && (H = null, U = 0);
|
|
272
247
|
} catch (t) {
|
|
273
248
|
if (H !== e) return;
|
|
274
249
|
if (t?.name === "QuotaExceededError") {
|
|
275
|
-
let t = await
|
|
250
|
+
let t = await pe(!0).then(() => !0, () => !1);
|
|
276
251
|
if (H !== e) return;
|
|
277
252
|
if (t) {
|
|
278
|
-
z = !1, await
|
|
253
|
+
z = !1, await q(he()?.end ?? n.currentTime);
|
|
279
254
|
return;
|
|
280
255
|
}
|
|
281
256
|
}
|
|
282
|
-
if (U += 1, U <
|
|
257
|
+
if (U += 1, U < T) return;
|
|
283
258
|
throw H = null, t;
|
|
284
259
|
}
|
|
285
|
-
},
|
|
286
|
-
if (F || I ||
|
|
260
|
+
}, ye = async () => {
|
|
261
|
+
if (F || I || Q !== void 0 || M || !H && (z || !me())) return;
|
|
287
262
|
let e = ++B, t = V, n = () => M || e !== B || t !== V;
|
|
288
263
|
F = !0;
|
|
289
264
|
try {
|
|
@@ -292,78 +267,78 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
292
267
|
if (n() || (r && (z = !0), t?.length && o.pushFragments(t), !e.byteLength)) return;
|
|
293
268
|
H = e, U = 0;
|
|
294
269
|
}
|
|
295
|
-
await
|
|
270
|
+
await ve(), !n() && G && (G = !1, ie?.());
|
|
296
271
|
} catch (e) {
|
|
297
|
-
|
|
272
|
+
K(e, n());
|
|
298
273
|
} finally {
|
|
299
274
|
e === B && (F = !1);
|
|
300
275
|
}
|
|
301
|
-
},
|
|
302
|
-
let t = a(
|
|
276
|
+
}, be = (e) => e >= fe && !!he(), xe = (e) => a(k).some((t) => t.start <= e + E && e < t.end), J = (e) => {
|
|
277
|
+
let t = a(k).find((t) => t.start <= e + E && e < t.end);
|
|
303
278
|
return t ? t.end - e : 0;
|
|
304
|
-
},
|
|
305
|
-
if (!M && !(
|
|
279
|
+
}, Se = async (e) => {
|
|
280
|
+
if (!M && !(J(e) >= D)) {
|
|
306
281
|
z = !1, L++, R = e;
|
|
307
282
|
try {
|
|
308
|
-
|
|
283
|
+
xe(e) || await q(e);
|
|
309
284
|
let t = V;
|
|
310
|
-
for (let n = 0; n < te && !(M || t !== V || z ||
|
|
311
|
-
let t =
|
|
312
|
-
if (await
|
|
285
|
+
for (let n = 0; n < te && !(M || t !== V || z || J(e) >= D); n++) {
|
|
286
|
+
let t = J(e);
|
|
287
|
+
if (await ye(), J(e) <= t) break;
|
|
313
288
|
}
|
|
314
289
|
} finally {
|
|
315
290
|
L--, R === e && (R = void 0);
|
|
316
291
|
}
|
|
317
292
|
}
|
|
318
|
-
},
|
|
319
|
-
let t = performance.now(), n = t -
|
|
320
|
-
if (n >=
|
|
321
|
-
|
|
293
|
+
}, Y = 0, X = null, Z, Ce = (e) => {
|
|
294
|
+
let t = performance.now(), n = t - Y;
|
|
295
|
+
if (n >= ne) {
|
|
296
|
+
Y = t, X = null, ae?.(e);
|
|
322
297
|
return;
|
|
323
298
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
let e =
|
|
327
|
-
|
|
328
|
-
},
|
|
299
|
+
X = e, !Z && (Z = setTimeout(() => {
|
|
300
|
+
Z = void 0;
|
|
301
|
+
let e = X;
|
|
302
|
+
X = null, !(e === null || M) && (Y = performance.now(), ae?.(e));
|
|
303
|
+
}, ne - n));
|
|
329
304
|
};
|
|
330
305
|
j.push(() => {
|
|
331
|
-
|
|
306
|
+
Z && clearTimeout(Z);
|
|
332
307
|
});
|
|
333
|
-
let
|
|
308
|
+
let we = 0, Q;
|
|
334
309
|
j.push(() => {
|
|
335
|
-
|
|
310
|
+
Q && clearTimeout(Q);
|
|
336
311
|
});
|
|
337
|
-
let
|
|
312
|
+
let Te = () => {
|
|
338
313
|
let t = n.currentTime, r = e.info.input.duration || n.duration;
|
|
339
|
-
r > 0 &&
|
|
340
|
-
let i = performance.now(), a = i -
|
|
341
|
-
if (
|
|
342
|
-
if (z = !1,
|
|
343
|
-
|
|
314
|
+
r > 0 && Ce(Math.min(Math.max(t / r, 0), 1));
|
|
315
|
+
let i = performance.now(), a = i - we < re;
|
|
316
|
+
if (we = i, !be(t) && !(I && Math.abs(t - fe) < .001)) {
|
|
317
|
+
if (z = !1, Q && clearTimeout(Q), !a) {
|
|
318
|
+
Q = void 0, q(t);
|
|
344
319
|
return;
|
|
345
320
|
}
|
|
346
|
-
|
|
347
|
-
if (
|
|
321
|
+
Q = setTimeout(() => {
|
|
322
|
+
if (Q = void 0, M) return;
|
|
348
323
|
let e = n.currentTime;
|
|
349
|
-
|
|
350
|
-
},
|
|
324
|
+
be(e) || (z = !1, q(e));
|
|
325
|
+
}, re);
|
|
351
326
|
}
|
|
352
327
|
};
|
|
353
|
-
n.addEventListener("seeking",
|
|
354
|
-
let
|
|
355
|
-
if (
|
|
356
|
-
|
|
328
|
+
n.addEventListener("seeking", Te), j.push(() => n.removeEventListener("seeking", Te));
|
|
329
|
+
let $ = !1, Ee = () => {
|
|
330
|
+
if ($) return;
|
|
331
|
+
$ = !0;
|
|
357
332
|
let e = n.error;
|
|
358
|
-
|
|
333
|
+
K(new v(`the media element failed: ${e?.message ?? "unknown"}`, { cause: e }), !1, !0);
|
|
359
334
|
};
|
|
360
335
|
n.addEventListener("error", Ee), j.push(() => n.removeEventListener("error", Ee));
|
|
361
336
|
let De = setInterval(() => {
|
|
362
|
-
|
|
337
|
+
$ || n.error || (pe().catch(() => {}), ye().catch((e) => K(e, !1)), z && !H && ge() && m.readyState === "open" && ue().catch(() => {}));
|
|
363
338
|
}, 100);
|
|
364
339
|
return j.push(() => clearInterval(De)), {
|
|
365
340
|
destroy: N,
|
|
366
|
-
prepareSeek:
|
|
341
|
+
prepareSeek: Se,
|
|
367
342
|
videoElement: n,
|
|
368
343
|
selectSubtitleStream: (e) => o.selectStream(e),
|
|
369
344
|
indexes: e.indexes ?? [],
|
|
@@ -374,8 +349,8 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
374
349
|
} catch (e) {
|
|
375
350
|
throw N(), e;
|
|
376
351
|
}
|
|
377
|
-
},
|
|
378
|
-
let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s =
|
|
352
|
+
}, ae = 5, oe = 500, se = 320, A = 1e6, j = 3, M = 1e4, N = async (e) => {
|
|
353
|
+
let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s = se } = e, c = await t({
|
|
379
354
|
publicPath: n,
|
|
380
355
|
workerUrl: r,
|
|
381
356
|
workerOptions: { type: "module" },
|
|
@@ -383,14 +358,14 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
383
358
|
read: a
|
|
384
359
|
});
|
|
385
360
|
try {
|
|
386
|
-
let t = await c.init(), n = t.duration, r = Math.max(e.interval ??
|
|
361
|
+
let t = await c.init(), n = t.duration, r = Math.max(e.interval ?? ae, n / oe), a = [];
|
|
387
362
|
for (let [e, o] of t.indexes.entries()) {
|
|
388
363
|
let s = a.at(-1);
|
|
389
364
|
s && o.timestamp - s.timestamp < r || a.push({
|
|
390
365
|
timestamp: o.timestamp,
|
|
391
366
|
endTime: n,
|
|
392
367
|
startByte: o.pos,
|
|
393
|
-
endByte: Math.min((t.indexes[e + 1]?.pos ?? i) +
|
|
368
|
+
endByte: Math.min((t.indexes[e + 1]?.pos ?? i) + A, i),
|
|
394
369
|
done: !1,
|
|
395
370
|
attempts: 0
|
|
396
371
|
});
|
|
@@ -421,7 +396,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
421
396
|
}, p = (e) => {
|
|
422
397
|
e.done = !0, d = d.then(async () => {
|
|
423
398
|
if (u) return;
|
|
424
|
-
let t = await Promise.race([c.readKeyframe(e.timestamp), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")),
|
|
399
|
+
let t = await Promise.race([c.readKeyframe(e.timestamp), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")), M))]), n = await createImageBitmap(new Blob([t], { type: "image/png" })), r = new OffscreenCanvas(s, Math.max(1, Math.round(n.height * (s / n.width))));
|
|
425
400
|
r.getContext("2d").drawImage(n, 0, 0, r.width, r.height), n.close();
|
|
426
401
|
let i = await r.convertToBlob({
|
|
427
402
|
type: "image/webp",
|
|
@@ -433,7 +408,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
433
408
|
endTime: e.endTime
|
|
434
409
|
}].sort((e, t) => e.startTime - t.startTime), f());
|
|
435
410
|
}).catch(() => {
|
|
436
|
-
e.attempts += 1, e.done = e.attempts >=
|
|
411
|
+
e.attempts += 1, e.done = e.attempts >= j;
|
|
437
412
|
});
|
|
438
413
|
};
|
|
439
414
|
return f(), {
|
|
@@ -443,14 +418,14 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
443
418
|
destroy: () => {
|
|
444
419
|
u = !0;
|
|
445
420
|
for (let e of l) URL.revokeObjectURL(e.url);
|
|
446
|
-
l = [],
|
|
421
|
+
l = [], k(c);
|
|
447
422
|
}
|
|
448
423
|
};
|
|
449
424
|
} catch (e) {
|
|
450
|
-
throw
|
|
425
|
+
throw k(c), e;
|
|
451
426
|
}
|
|
452
|
-
},
|
|
453
|
-
let { video: t, canvas: n } = e, r = e.mode ??
|
|
427
|
+
}, P = 1280, ce = 1920, le = 250, ue = 100, F = () => typeof HTMLCanvasElement < "u" && "captureStream" in HTMLCanvasElement.prototype, I = () => typeof document < "u" && typeof HTMLVideoElement < "u" && typeof HTMLVideoElement.prototype.requestPictureInPicture == "function" && !!document.pictureInPictureEnabled, L = () => typeof HTMLVideoElement < "u" && "mozCaptureStream" in HTMLVideoElement.prototype, R = () => F() ? I() ? "window" : L() ? "burn-in" : null : null, de = (e) => {
|
|
428
|
+
let { video: t, canvas: n } = e, r = e.mode ?? R() ?? "window", i = e.maxWidth ?? (r === "burn-in" ? ce : P), 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) => {
|
|
454
429
|
r.drawImage(t, 0, 0, e.width, e.height), n.width > 0 && n.height > 0 && r.drawImage(n, 0, 0, e.width, e.height);
|
|
455
430
|
}, h = () => {
|
|
456
431
|
a && m(a.composite, a.context);
|
|
@@ -464,52 +439,52 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
464
439
|
"mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
|
|
465
440
|
}, x = () => {
|
|
466
441
|
c || l || !t.paused || t.play().catch(() => {});
|
|
467
|
-
},
|
|
442
|
+
}, S = () => {
|
|
468
443
|
c || l || (t.paused || t.pause(), r !== "burn-in" && (c = !0, a?.mirror.play().catch(() => {}).finally(() => {
|
|
469
444
|
c = !1;
|
|
470
445
|
})));
|
|
471
|
-
}, S = () => {
|
|
472
|
-
b(), a?.mirror.play().catch(() => {});
|
|
473
446
|
}, C = () => {
|
|
474
|
-
b(),
|
|
447
|
+
b(), a?.mirror.play().catch(() => {});
|
|
475
448
|
}, w = () => {
|
|
449
|
+
b(), r === "burn-in" && a?.mirror.pause();
|
|
450
|
+
}, T = () => {
|
|
476
451
|
if (r !== "burn-in" || !a || !t.paused || l) return;
|
|
477
452
|
l = !0;
|
|
478
453
|
let { mirror: e } = a;
|
|
479
454
|
h(), e.play().then(() => new Promise((e) => {
|
|
480
|
-
setTimeout(e,
|
|
455
|
+
setTimeout(e, ue);
|
|
481
456
|
})).catch(() => {}).finally(() => {
|
|
482
457
|
e.pause(), setTimeout(() => {
|
|
483
458
|
l = !1;
|
|
484
|
-
},
|
|
459
|
+
}, ue);
|
|
485
460
|
});
|
|
486
|
-
},
|
|
461
|
+
}, ee = [["play", () => {
|
|
487
462
|
t.play().catch(() => {});
|
|
488
|
-
}], ["pause", () => t.pause()]],
|
|
489
|
-
if ("mediaSession" in navigator) for (let [t, n] of
|
|
463
|
+
}], ["pause", () => t.pause()]], E = (e) => {
|
|
464
|
+
if ("mediaSession" in navigator) for (let [t, n] of ee) try {
|
|
490
465
|
navigator.mediaSession.setActionHandler(t, e ? n : null);
|
|
491
466
|
} catch {}
|
|
492
|
-
},
|
|
493
|
-
if (y(),
|
|
467
|
+
}, D = () => {
|
|
468
|
+
if (y(), E(!1), t.removeEventListener("play", C), t.removeEventListener("pause", w), t.removeEventListener("seeked", T), !a) {
|
|
494
469
|
f?.();
|
|
495
470
|
return;
|
|
496
471
|
}
|
|
497
472
|
let { stream: e, mirror: n } = a;
|
|
498
|
-
n.removeEventListener("play", x), n.removeEventListener("pause",
|
|
473
|
+
n.removeEventListener("play", x), n.removeEventListener("pause", S), n.removeEventListener("leavepictureinpicture", te);
|
|
499
474
|
for (let t of e.getTracks()) t.stop();
|
|
500
475
|
n.remove(), a = void 0, f?.();
|
|
501
|
-
},
|
|
476
|
+
}, te = () => D(), ne = (e) => {
|
|
502
477
|
let r = t.style.opacity, i = n.style.display;
|
|
503
478
|
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 = () => {
|
|
504
479
|
f = void 0, t.style.opacity = r, n.style.display = i;
|
|
505
480
|
};
|
|
506
|
-
},
|
|
481
|
+
}, re = async () => {
|
|
507
482
|
let n = Math.min(t.videoWidth || i, i), o = Math.round(n * ((t.videoHeight || 9) / (t.videoWidth || 16))), c = document.createElement("canvas");
|
|
508
483
|
c.width = n, c.height = o;
|
|
509
484
|
let l = c.getContext("2d");
|
|
510
485
|
if (!l) throw Error("the picture in picture composite has no 2d context");
|
|
511
486
|
let u = document.createElement("video");
|
|
512
|
-
u.muted = !0, u.playsInline = !0, r === "burn-in" ?
|
|
487
|
+
u.muted = !0, u.playsInline = !0, r === "burn-in" ? ne(u) : u.style.cssText = "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;left:-1px;top:-1px", m(c, l);
|
|
513
488
|
let f = {
|
|
514
489
|
composite: c,
|
|
515
490
|
context: l,
|
|
@@ -518,7 +493,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
518
493
|
};
|
|
519
494
|
if (a = f, u.srcObject = f.stream, (e.container ?? t.parentElement ?? document.body).appendChild(u), v(), s = setInterval(() => {
|
|
520
495
|
t.paused && h();
|
|
521
|
-
},
|
|
496
|
+
}, le), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
|
|
522
497
|
let t = () => {
|
|
523
498
|
clearTimeout(n), e();
|
|
524
499
|
}, n = setTimeout(t, 1e3);
|
|
@@ -527,7 +502,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
527
502
|
u.remove();
|
|
528
503
|
return;
|
|
529
504
|
}
|
|
530
|
-
if (u.addEventListener("play", x), u.addEventListener("pause",
|
|
505
|
+
if (u.addEventListener("play", x), u.addEventListener("pause", S), u.addEventListener("leavepictureinpicture", te), t.addEventListener("play", C), t.addEventListener("pause", w), t.addEventListener("seeked", T), E(!0), b(), r === "burn-in") {
|
|
531
506
|
t.paused && u.pause(), e.onBurnedInChange?.(!0);
|
|
532
507
|
return;
|
|
533
508
|
}
|
|
@@ -540,7 +515,7 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
540
515
|
try {
|
|
541
516
|
if (p()) {
|
|
542
517
|
if (r === "burn-in") {
|
|
543
|
-
|
|
518
|
+
D(), e.onBurnedInChange?.(!1);
|
|
544
519
|
return;
|
|
545
520
|
}
|
|
546
521
|
await document.exitPictureInPicture().catch(() => {});
|
|
@@ -548,9 +523,9 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
548
523
|
}
|
|
549
524
|
r !== "burn-in" && document.pictureInPictureElement && await document.exitPictureInPicture().catch(() => {});
|
|
550
525
|
try {
|
|
551
|
-
await
|
|
526
|
+
await re();
|
|
552
527
|
} catch (t) {
|
|
553
|
-
console.warn("subtitle compositing for picture in picture failed", t),
|
|
528
|
+
console.warn("subtitle compositing for picture in picture failed", t), D(), r === "burn-in" && e.onBurnedInChange?.(!1);
|
|
554
529
|
}
|
|
555
530
|
} finally {
|
|
556
531
|
u = !1;
|
|
@@ -559,9 +534,9 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
559
534
|
},
|
|
560
535
|
mode: r,
|
|
561
536
|
destroy: () => {
|
|
562
|
-
d = !0, r !== "burn-in" && p() && document.exitPictureInPicture().catch(() => {}),
|
|
537
|
+
d = !0, r !== "burn-in" && p() && document.exitPictureInPicture().catch(() => {}), D(), r === "burn-in" && e.onBurnedInChange?.(!1);
|
|
563
538
|
}
|
|
564
539
|
};
|
|
565
540
|
};
|
|
566
541
|
//#endregion
|
|
567
|
-
export {
|
|
542
|
+
export { v as a, k as c, a as d, s as f, O as i, c as l, R as n, y as o, N as r, ie as s, de as t, _ 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-DPnChmYc.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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ParsedASS, ParsedASSStyles } from 'ass-compiler'
|
|
2
1
|
import type { ASS_Event } from 'jassub'
|
|
3
2
|
import type { Attachment, SubtitleFragment } from 'libav-wasm/build/worker'
|
|
4
3
|
|
|
@@ -10,8 +9,17 @@ export type SubtitleStream = { streamIndex: number, title: string, language: str
|
|
|
10
9
|
/** -1 turns subtitles off. It frees the track and matches no header, so nothing is set. */
|
|
11
10
|
export const SUBTITLES_OFF = -1
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* jassub's `ASS_Event` types `Style` as a string, which the library it wraps does not agree with:
|
|
14
|
+
* the field is written straight through to libass's `int Style`, an index into the track's style
|
|
15
|
+
* list. Corrected once, here, so a name cannot end up in it again.
|
|
16
|
+
*/
|
|
17
|
+
type StyledEvent = Omit<ASS_Event, 'Style'> & { Style: number }
|
|
18
|
+
|
|
19
|
+
const createEvent = (jassub: JASSUB, event: StyledEvent) => jassub.createEvent(event as unknown as ASS_Event)
|
|
20
|
+
|
|
21
|
+
type SubtitleHeaderPart = { type: 'header', streamIndex: number, content: string, eventsContent: string, styles: Map<string, number> }
|
|
22
|
+
type SubtitleDialoguePart = { type: 'dialogue', streamIndex: number, index: number, assEvent: StyledEvent }
|
|
15
23
|
|
|
16
24
|
export type SubtitleRendererOptions = {
|
|
17
25
|
video: HTMLVideoElement
|
|
@@ -34,35 +42,36 @@ export type SubtitleRendererOptions = {
|
|
|
34
42
|
|
|
35
43
|
const convertTimestamp = (ms: number) => new Date(ms).toISOString().slice(11, 22)
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
45
|
+
/**
|
|
46
|
+
* How many styles libass keeps in front of the ones the file declares.
|
|
47
|
+
*
|
|
48
|
+
* `ass_new_track` always allocates its own "Default" at index 0 before parsing a line of the header,
|
|
49
|
+
* so the file's first style lands at 1. That number is what an event's `Style` field holds: libass
|
|
50
|
+
* reads it as an index into the track's style list and nothing else. `subtitle-scale.browser.test.tsx`
|
|
51
|
+
* is what pins this, by measuring the rendered text against what libass itself draws.
|
|
52
|
+
*/
|
|
53
|
+
const LIBASS_OWN_STYLES = 1
|
|
54
|
+
|
|
55
|
+
const headerStyles = (content: string) =>
|
|
56
|
+
// a duplicated name resolves to the LAST one, the way libass's own lookup scans the list backwards
|
|
57
|
+
new Map(parse(content).styles.style.map((style, index) => [style.Name, index + LIBASS_OWN_STYLES]))
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The index libass will resolve this event's style to.
|
|
61
|
+
*
|
|
62
|
+
* jassub types `ASS_Event.Style` as a string and writes it straight through to an `int`, so handing
|
|
63
|
+
* it a style NAME stores 0, which is libass's own default: Arial at size 18, with margins and a drop
|
|
64
|
+
* shadow of its own. Subtitles still appear, in a face and a size the file never asked for.
|
|
65
|
+
*
|
|
66
|
+
* Falling back to the header's own "Default", then to 0, is what libass does for a name it cannot
|
|
67
|
+
* find. The leading-`*` strip and the case fold on "Default" are its normalisation, kept so a
|
|
68
|
+
* `*Default` written by an older tool resolves here too.
|
|
69
|
+
*/
|
|
70
|
+
const styleIndex = (header: SubtitleHeaderPart, name: string) => {
|
|
71
|
+
const stripped = name.replace(/^\*+/, '')
|
|
72
|
+
const key = stripped.toLowerCase() === 'default' ? 'Default' : stripped
|
|
73
|
+
return header.styles.get(key) ?? header.styles.get('Default') ?? 0
|
|
74
|
+
}
|
|
66
75
|
|
|
67
76
|
// cleared so jassub scales the script to the canvas, not to the authored resolution
|
|
68
77
|
const renderable = (content: string) => {
|
|
@@ -84,7 +93,7 @@ const toHeaderPart = (fragment: SubtitleFragment & { type: 'header' }): Subtitle
|
|
|
84
93
|
console.warn(`subtitle stream ${fragment.streamIndex} has no Events format, ignoring the track`)
|
|
85
94
|
return null
|
|
86
95
|
}
|
|
87
|
-
return { type: 'header', streamIndex: fragment.streamIndex, content: fragment.content, eventsContent,
|
|
96
|
+
return { type: 'header', streamIndex: fragment.streamIndex, content: fragment.content, eventsContent, styles: headerStyles(fragment.content) }
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
const toDialoguePart = (header: SubtitleHeaderPart, fragment: SubtitleFragment & { type: 'dialogue' }): SubtitleDialoguePart => {
|
|
@@ -102,6 +111,7 @@ const toDialoguePart = (header: SubtitleHeaderPart, fragment: SubtitleFragment &
|
|
|
102
111
|
index: dialogueIndex,
|
|
103
112
|
assEvent: {
|
|
104
113
|
...event,
|
|
114
|
+
Style: styleIndex(header, event.Style),
|
|
105
115
|
Effect: event.Effect ?? '',
|
|
106
116
|
Text: event.Text.raw,
|
|
107
117
|
Duration: (event.End - event.Start) * 1000,
|
|
@@ -109,7 +119,7 @@ const toDialoguePart = (header: SubtitleHeaderPart, fragment: SubtitleFragment &
|
|
|
109
119
|
End: event.End * 1000,
|
|
110
120
|
ReadOrder: dialogueIndex,
|
|
111
121
|
_index: dialogueIndex,
|
|
112
|
-
} as
|
|
122
|
+
} as StyledEvent,
|
|
113
123
|
}
|
|
114
124
|
}
|
|
115
125
|
|
|
@@ -149,7 +159,6 @@ export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
|
149
159
|
// jassub 1.8.x binds setRate as the ratechange listener, so the Event becomes the rate
|
|
150
160
|
video.removeEventListener('ratechange', (jassub as unknown as { _boundSetRate: EventListener })._boundSetRate)
|
|
151
161
|
video.addEventListener('ratechange', onRateChange)
|
|
152
|
-
for (const style of header.parsed.styles.style) appendParsedStyle(jassub, style)
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
const pushAttachments = (incoming: Attachment[]) => {
|
|
@@ -175,7 +184,7 @@ export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
|
175
184
|
const part = toDialoguePart(header, fragment)
|
|
176
185
|
if (byIndex.has(part.index)) continue
|
|
177
186
|
byIndex.set(part.index, part)
|
|
178
|
-
if (selected === fragment.streamIndex)
|
|
187
|
+
if (selected === fragment.streamIndex && jassub) createEvent(jassub, part.assEvent)
|
|
179
188
|
}
|
|
180
189
|
}
|
|
181
190
|
}
|
|
@@ -188,8 +197,7 @@ export const createSubtitleRenderer = (options: SubtitleRendererOptions) => {
|
|
|
188
197
|
const header = headers.get(next)
|
|
189
198
|
if (!header) return
|
|
190
199
|
jassub.setTrack(renderable(header.content))
|
|
191
|
-
for (const
|
|
192
|
-
for (const part of dialogues.get(next)?.values() ?? []) jassub.createEvent(part.assEvent)
|
|
200
|
+
for (const part of dialogues.get(next)?.values() ?? []) createEvent(jassub, part.assEvent)
|
|
193
201
|
jassub.setCurrentTime(video.paused, video.currentTime, video.playbackRate)
|
|
194
202
|
}
|
|
195
203
|
|