@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.
@@ -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-DPnChmYc.js";
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
- /** Fallback face for `liberation sans`. Without it jassub falls back to whatever the wasm build embeds. */
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, stringify as i } from "ass-compiler";
3
+ import { parse as r } from "ass-compiler";
4
4
  //#region src/lib/engine/source-buffer.ts
5
- var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
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
- })), o = (e) => new Promise((t, n) => {
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
- }), s = (e, t) => {
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), o(e))),
25
- unbufferRange: (t, n) => r(() => t >= n ? Promise.resolve() : (e.remove(t, n), o(e))),
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
- }, 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) => {
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
- }, g = (e, t) => {
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
- }, _ = (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(() => {
79
- c?.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
80
- }, 100), b = () => c?.setRate(t.playbackRate), x = (e) => {
81
- c = new n({
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: m(e.content),
74
+ subContent: e.content,
86
75
  workerUrl: i,
87
76
  modernWasmUrl: a,
88
77
  ...o ? { wasmUrl: o } : {},
89
- fonts: u.map(([, e]) => e),
78
+ fonts: d.map(([, e]) => e),
90
79
  availableFonts: {
91
- ...Object.fromEntries(u),
80
+ ...Object.fromEntries(d),
92
81
  ...s ? { "liberation sans": s } : {}
93
82
  }
94
- }), t.removeEventListener("ratechange", c._boundSetRate), t.addEventListener("ratechange", b);
83
+ }), t.removeEventListener("ratechange", u._boundSetRate), t.addEventListener("ratechange", y);
95
84
  };
96
85
  return {
97
86
  pushAttachments: (e) => {
98
- u = [...u, ...e.map((e) => [e.filename, new Uint8Array(e.data)])];
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 (d.has(t.streamIndex)) continue;
103
- let e = h(t);
91
+ if (f.has(t.streamIndex)) continue;
92
+ let e = m(t);
104
93
  if (!e) continue;
105
- d.set(t.streamIndex, e), f.push({
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?.([...f]), _ === void 0 && (_ = t.streamIndex), c || x(e);
98
+ }), v?.([...p]), _ === void 0 && (_ = t.streamIndex), u || b(e);
110
99
  } else {
111
- let e = d.get(t.streamIndex);
100
+ let e = f.get(t.streamIndex);
112
101
  if (!e) continue;
113
- let n = p.get(t.streamIndex);
114
- n || (n = /* @__PURE__ */ new Map(), p.set(t.streamIndex, n));
115
- let r = g(e, t);
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 && c && l(c, r.assEvent);
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 === _ || !c) return;
123
- _ = n, c.freeTrack();
124
- let r = d.get(n);
111
+ if (n === _ || !u) return;
112
+ _ = n, u.freeTrack();
113
+ let r = f.get(n);
125
114
  if (r) {
126
- c.setTrack(m(r.content));
127
- for (let e of p.get(n)?.values() ?? []) l(c, e.assEvent);
128
- c.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
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: () => [...f],
120
+ getStreams: () => [...p],
132
121
  getSelectedStream: () => _,
133
122
  setOnStreams: (e) => {
134
123
  v = e;
135
124
  },
136
125
  destroy: () => {
137
- clearInterval(y), t.removeEventListener("ratechange", b), c?.destroy(), c = void 0;
126
+ clearInterval(ee), t.removeEventListener("ratechange", y), u?.destroy(), u = void 0;
138
127
  }
139
128
  };
140
- }, v = class extends Error {
129
+ }, _ = class extends Error {
141
130
  mediaElement = !0;
142
131
  constructor(e, t) {
143
132
  super(e, t), this.name = "MediaElementError";
144
133
  }
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) => {
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
- }, 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({
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: o,
145
+ length: s,
157
146
  audioStreamIndex: h,
158
- read: i
159
- }), j = [], M = !1, N = () => {
160
- if (!M) {
161
- M = !0;
162
- for (let e of [...j].reverse()) try {
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
- j.push(() => k(A));
156
+ P.push(() => j(N));
168
157
  try {
169
- let e = await A.init(), t = e.audioStreams ?? [], i = t.some((e) => e.streamIndex === h) ? h : t[0]?.streamIndex ?? -1;
170
- se?.(t, i);
171
- let o = _({
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: oe
167
+ onStreams: re
179
168
  });
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");
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(), O = URL.createObjectURL(m);
184
- j.push(() => URL.revokeObjectURL(O)), n.src = O, j.push(() => {
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 k = await new Promise((t, n) => {
179
+ let j = await new Promise((t, n) => {
191
180
  let r = setTimeout(() => {
192
181
  i(), n(/* @__PURE__ */ Error("The MediaSource never opened"));
193
- }, ee), i = () => {
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: 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) => {
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), !(G && !n) && (G = !0, y?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
213
- }, pe = async (e = !1) => {
214
- if (L > 0 && !e) return;
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);
217
- }, me = () => {
218
- let e = a(k);
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 = 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;
222
- }, he = () => {
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 a(k).find((t) => t.start <= e + E && e < t.end);
225
- }, ge = () => {
226
- let e = he();
227
- return !!e && e.end >= W - .1;
228
- }, _e = () => {
229
- let e = a(k).map((e) => e.end);
230
- e.length && (W = Math.max(W, ...e));
231
- }, q = async (e) => {
232
- I = !0, H = null, U = 0, fe = e;
233
- let t = ++V;
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 A.seek(e);
236
- if (M || t !== V || (a && (z = !0), i?.length && o.pushFragments(i), await le(r), M || t !== V)) return;
237
- n.byteLength && (await P(n), _e());
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
- K(e, M || t !== V);
228
+ J(e, F || t !== U);
240
229
  } finally {
241
- t === V && (I = !1);
230
+ t === U && (R = !1);
242
231
  }
243
- }, ve = async () => {
244
- let e = H;
232
+ }, ge = async () => {
233
+ let e = W;
245
234
  if (e) try {
246
- await P(e), _e(), H === e && (H = null, U = 0);
235
+ await I(e), he(), W === e && (W = null, G = 0);
247
236
  } catch (t) {
248
- if (H !== e) return;
237
+ if (W !== e) return;
249
238
  if (t?.name === "QuotaExceededError") {
250
- let t = await pe(!0).then(() => !0, () => !1);
251
- if (H !== e) return;
239
+ let t = await de(!0).then(() => !0, () => !1);
240
+ if (W !== e) return;
252
241
  if (t) {
253
- z = !1, await q(he()?.end ?? n.currentTime);
242
+ V = !1, await Y(pe()?.end ?? n.currentTime);
254
243
  return;
255
244
  }
256
245
  }
257
- if (U += 1, U < T) return;
258
- throw H = null, t;
246
+ if (G += 1, G < C) return;
247
+ throw W = null, t;
259
248
  }
260
- }, ye = async () => {
261
- if (F || I || Q !== void 0 || M || !H && (z || !me())) return;
262
- let e = ++B, t = V, n = () => M || e !== B || t !== V;
263
- F = !0;
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 (!H) {
266
- let { data: e, subtitles: t, finished: r } = await A.read();
267
- if (n() || (r && (z = !0), t?.length && o.pushFragments(t), !e.byteLength)) return;
268
- H = e, U = 0;
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 ve(), !n() && G && (G = !1, ie?.());
259
+ await ge(), !n() && q && (q = !1, ne?.());
271
260
  } catch (e) {
272
- K(e, n());
261
+ J(e, n());
273
262
  } finally {
274
- e === B && (F = !1);
263
+ e === H && (L = !1);
275
264
  }
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);
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
- }, Se = async (e) => {
280
- if (!M && !(J(e) >= D)) {
281
- z = !1, L++, R = e;
268
+ }, be = async (e) => {
269
+ if (!F && !(X(e) >= E)) {
270
+ V = !1, z++, B = e;
282
271
  try {
283
- xe(e) || await q(e);
284
- let t = V;
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;
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
- L--, R === e && (R = void 0);
279
+ z--, B === e && (B = void 0);
291
280
  }
292
281
  }
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);
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
- 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));
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
- j.push(() => {
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 Te = () => {
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 && 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);
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
- Q = setTimeout(() => {
322
- if (Q = void 0, M) return;
310
+ $ = setTimeout(() => {
311
+ if ($ = void 0, F) return;
323
312
  let e = n.currentTime;
324
- be(e) || (z = !1, q(e));
325
- }, re);
313
+ ve(e) || (V = !1, Y(e));
314
+ }, k);
326
315
  }
327
316
  };
328
- n.addEventListener("seeking", Te), j.push(() => n.removeEventListener("seeking", Te));
329
- let $ = !1, Ee = () => {
330
- if ($) return;
331
- $ = !0;
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
- K(new v(`the media element failed: ${e?.message ?? "unknown"}`, { cause: e }), !1, !0);
322
+ J(new _(`the media element failed: ${e?.message ?? "unknown"}`, { cause: e }), !1, !0);
334
323
  };
335
- n.addEventListener("error", Ee), j.push(() => n.removeEventListener("error", Ee));
324
+ n.addEventListener("error", Ee), P.push(() => n.removeEventListener("error", Ee));
336
325
  let De = setInterval(() => {
337
- $ || n.error || (pe().catch(() => {}), ye().catch((e) => K(e, !1)), z && !H && ge() && m.readyState === "open" && ue().catch(() => {}));
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 j.push(() => clearInterval(De)), {
340
- destroy: N,
341
- prepareSeek: Se,
328
+ return P.push(() => clearInterval(De)), {
329
+ destroy: ae,
330
+ prepareSeek: be,
342
331
  videoElement: n,
343
- selectSubtitleStream: (e) => o.selectStream(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 N(), e;
339
+ throw ae(), e;
351
340
  }
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({
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 ?? ae, n / oe), a = [];
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) + A, 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")), M))]), n = await createImageBitmap(new Blob([t], { type: "image/png" })), r = new OffscreenCanvas(s, Math.max(1, Math.round(n.height * (s / n.width))));
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 >= j;
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 = [], k(c);
410
+ l = [], j(c);
422
411
  }
423
412
  };
424
413
  } catch (e) {
425
- throw k(c), e;
414
+ throw j(c), e;
426
415
  }
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) => {
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
- }, y = () => {
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
- }, b = () => {
427
+ }, y = () => {
439
428
  "mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
440
- }, x = () => {
429
+ }, b = () => {
441
430
  c || l || !t.paused || t.play().catch(() => {});
442
- }, S = () => {
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
- b(), a?.mirror.play().catch(() => {});
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, ue);
444
+ setTimeout(e, se);
456
445
  })).catch(() => {}).finally(() => {
457
446
  e.pause(), setTimeout(() => {
458
447
  l = !1;
459
- }, ue);
448
+ }, se);
460
449
  });
461
- }, ee = [["play", () => {
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 ee) try {
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 (y(), E(!1), t.removeEventListener("play", C), t.removeEventListener("pause", w), t.removeEventListener("seeked", T), !a) {
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", x), n.removeEventListener("pause", S), n.removeEventListener("leavepictureinpicture", te);
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
- }, te = () => D(), ne = (e) => {
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
- }, re = async () => {
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" ? ne(u) : u.style.cssText = "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;left:-1px;top:-1px", m(c, l);
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
- }, le), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
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", 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") {
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 re();
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 { 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 };
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-DPnChmYc.js";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banou/media-player",
3
- "version": "0.8.16",
3
+ "version": "0.8.17",
4
4
  "description": "A video player for containers and codecs the browser cannot play natively, remuxed on the fly",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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, stringify } from 'ass-compiler'
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
- /** Fallback face for `liberation sans`. Without it jassub falls back to whatever the wasm build embeds. */
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, wasmUrl, legacyWasmUrl, defaultFontUrl } = options
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: renderable(header.content),
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(renderable(header.content))
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
  }