@banou/media-player 0.10.14 → 0.10.15

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-DdbOiEX3.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-DQ537ehC.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 };
@@ -188,7 +188,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
188
188
  length: s,
189
189
  audioStreamIndex: _,
190
190
  read: a
191
- }), M = [], N = !1, P = () => {
191
+ }), M = [], N = !1, se = () => {
192
192
  if (!N) {
193
193
  N = !0;
194
194
  for (let e of [...M].reverse()) try {
@@ -237,138 +237,138 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
237
237
  i(), n(/* @__PURE__ */ Error("The MediaSource closed before a SourceBuffer could be created"));
238
238
  };
239
239
  m.addEventListener("sourceopen", a, { once: !0 }), m.addEventListener("sourceclose", o, { once: !0 }), m.addEventListener("error", o, { once: !0 });
240
- }), { appendBuffer: F, unbufferRange: se, updateTimestampOffset: ce, endOfStream: le } = o(A, m);
241
- await F(e.data), e.subtitles?.length && s.pushFragments(e.subtitles), te?.();
242
- let I = !1, L = !1, R = 0, z, ue = () => z ?? n.currentTime, B = !1, de = 0, V = 0, H = null, U = 0, W = 0, G = !1, fe = 0, K = !0, q = (e, t, n = !1) => {
240
+ }), { appendBuffer: P, unbufferRange: ce, updateTimestampOffset: le, endOfStream: ue } = o(A, m);
241
+ await P(e.data), e.subtitles?.length && s.pushFragments(e.subtitles), te?.();
242
+ let F = !1, I = !1, L = 0, R, de = () => R ?? n.currentTime, z = !1, fe = !1, pe = 0, B = 0, V = null, H = 0, U = 0, W = !1, G = 0, K = !0, q = (e, t, n = !1) => {
243
243
  let r = e?.message === "Cancelled";
244
- t && r || (console.error(e), (!G || n) && (G = !0, ne?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
245
- }, pe = async (e = !1) => {
246
- if (R > 0 && !e) return;
247
- let t = n.currentTime, r = e ? x : v, a = e ? S : y, o = i(A), s = (e, t) => (K = !1, se(e, t));
244
+ t && r || (console.error(e), (!W || n) && (W = !0, ne?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
245
+ }, me = async (e = !1) => {
246
+ if (L > 0 && !e) return;
247
+ let t = n.currentTime, r = e ? x : v, a = e ? S : y, o = i(A), s = (e, t) => (K = !1, ce(e, t));
248
248
  o.length && o[0].start < t + r && await s(0, t + r);
249
249
  for (let { end: e } of o) e > t + a && await s(t + a, e);
250
- }, me = () => {
250
+ }, he = () => {
251
251
  let e = i(A);
252
252
  if (!e.length) return !0;
253
- let t = ue(), n = e.find((e) => e.start <= t + T && t < e.end);
253
+ let t = de(), n = e.find((e) => e.start <= t + T && t < e.end);
254
254
  return n && n.end >= t + y ? !1 : !n || n.end < t + b;
255
- }, he = (e) => i(A).find((t) => t.start <= e + T && e < t.end), ge = () => he(n.currentTime), _e = (e = n.currentTime) => {
256
- let t = he(e);
257
- return e >= W - .1 || !!t && t.end >= W - .1;
258
- }, ve = () => {
255
+ }, ge = (e) => i(A).find((t) => t.start <= e + T && e < t.end), _e = () => ge(n.currentTime), ve = (e = n.currentTime) => {
256
+ let t = ge(e);
257
+ return e >= U - .1 || !!t && t.end >= U - .1;
258
+ }, ye = () => {
259
259
  let e = i(A).map((e) => e.end);
260
- e.length && (W = Math.max(W, ...e));
261
- }, J = async (e) => {
262
- L = !0, H = null, U = 0, fe = e, K = !0;
263
- let t = ++V;
260
+ e.length && (U = Math.max(U, ...e)), z && (fe = !0);
261
+ }, be = () => z || fe && (i(A).at(-1)?.end ?? 0) >= U - .1, J = async (e) => {
262
+ I = !0, V = null, H = 0, G = e, K = !0;
263
+ let t = ++B;
264
264
  try {
265
265
  let { data: n, pts: r, subtitles: i, finished: a } = await j.seek(e);
266
- if (N || t !== V || (a && (B = !0), i?.length && s.pushFragments(i), await ce(r), N || t !== V)) return;
267
- n.byteLength && (await F(n), ve());
266
+ if (N || t !== B || (a && (z = !0), i?.length && s.pushFragments(i), await le(r), N || t !== B)) return;
267
+ n.byteLength && (await P(n), ye());
268
268
  } catch (e) {
269
- t === V && (K = !1), q(e, N || t !== V);
269
+ t === B && (K = !1), q(e, N || t !== B);
270
270
  } finally {
271
- t === V && (L = !1);
271
+ t === B && (I = !1);
272
272
  }
273
- }, ye = async () => {
274
- let e = H;
273
+ }, xe = async () => {
274
+ let e = V;
275
275
  if (e) try {
276
- await F(e), ve(), H === e && (H = null, U = 0);
276
+ await P(e), ye(), V === e && (V = null, H = 0);
277
277
  } catch (t) {
278
- if (H !== e) return;
278
+ if (V !== e) return;
279
279
  if (t?.name === "QuotaExceededError") {
280
- let t = await pe(!0).then(() => !0, () => !1);
281
- if (H !== e) return;
280
+ let t = await me(!0).then(() => !0, () => !1);
281
+ if (V !== e) return;
282
282
  if (t) {
283
- B = !1, await J(ge()?.end ?? n.currentTime);
283
+ z = !1, await J(_e()?.end ?? n.currentTime);
284
284
  return;
285
285
  }
286
286
  }
287
- if (U += 1, U < C) return;
288
- throw H = null, t;
287
+ if (H += 1, H < C) return;
288
+ throw V = null, t;
289
289
  }
290
- }, be = async () => {
291
- if (I || L || Q !== void 0 || N || !H && (B || !me())) return;
292
- let e = ++de, t = V, n = () => N || e !== de || t !== V;
293
- I = !0;
290
+ }, Se = async () => {
291
+ if (F || I || Q !== void 0 || N || !V && (z || !he())) return;
292
+ let e = ++pe, t = B, n = () => N || e !== pe || t !== B;
293
+ F = !0;
294
294
  try {
295
- if (!H) {
295
+ if (!V) {
296
296
  let { data: e, subtitles: t, finished: r } = await j.read();
297
- if (n() || (r && (B = !0), t?.length && s.pushFragments(t), !e.byteLength)) return;
298
- H = e, U = 0;
297
+ if (n() || (r && (z = !0), t?.length && s.pushFragments(t), !e.byteLength)) return;
298
+ V = e, H = 0;
299
299
  }
300
- await ye(), !n() && G && (G = !1, re?.());
300
+ await xe(), !n() && W && (W = !1, re?.());
301
301
  } catch (e) {
302
302
  q(e, n());
303
303
  } finally {
304
- e === de && (I = !1);
304
+ e === pe && (F = !1);
305
305
  }
306
- }, xe = (e) => e >= fe && Se(e) && Ce(e), Se = (e) => i(A).some((t) => t.start <= e && e < t.end), Y = (e) => {
307
- let t = he(e);
306
+ }, Ce = (e) => e >= G && we(e) && Te(e), we = (e) => i(A).some((t) => t.start <= e && e < t.end), Y = (e) => {
307
+ let t = ge(e);
308
308
  return t ? t.end - e : 0;
309
- }, Ce = (e) => Y(e) >= E || B && _e(e), we = async (e) => {
310
- if (!N && !(Se(e) && Ce(e))) {
311
- B = !1, R++, z = e;
309
+ }, Te = (e) => Y(e) >= E || be() && ve(e), Ee = async (e) => {
310
+ if (!N && !(we(e) && Te(e))) {
311
+ z = !1, L++, R = e;
312
312
  try {
313
313
  await J(e);
314
- let t = V;
315
- for (let n = 0; n < ee && !(N || t !== V || B || Y(e) >= E); n++) {
314
+ let t = B;
315
+ for (let n = 0; n < ee && !(N || t !== B || z || Y(e) >= E); n++) {
316
316
  let t = Y(e);
317
- if (await be(), Y(e) <= t) break;
317
+ if (await Se(), Y(e) <= t) break;
318
318
  }
319
319
  } finally {
320
- R--, z === e && (z = void 0);
320
+ L--, R === e && (R = void 0);
321
321
  }
322
322
  }
323
- }, Te = 0, X = null, Z, Ee = (e) => {
324
- let t = performance.now(), n = t - Te;
323
+ }, De = 0, X = null, Z, Oe = (e) => {
324
+ let t = performance.now(), n = t - De;
325
325
  if (n >= D) {
326
- Te = t, X = null, ie?.(e);
326
+ De = t, X = null, ie?.(e);
327
327
  return;
328
328
  }
329
329
  X = e, !Z && (Z = setTimeout(() => {
330
330
  Z = void 0;
331
331
  let e = X;
332
- X = null, !(e === null || N) && (Te = performance.now(), ie?.(e));
332
+ X = null, !(e === null || N) && (De = performance.now(), ie?.(e));
333
333
  }, D - n));
334
334
  };
335
335
  M.push(() => {
336
336
  Z && clearTimeout(Z);
337
337
  });
338
- let De = 0, Q;
338
+ let ke = 0, Q;
339
339
  M.push(() => {
340
340
  Q && clearTimeout(Q);
341
341
  });
342
- let Oe = () => {
342
+ let Ae = () => {
343
343
  let t = n.currentTime, r = e.info.input.duration || n.duration;
344
- r > 0 && Ee(Math.min(Math.max(t / r, 0), 1));
345
- let i = performance.now(), a = i - De < O;
346
- if (De = i, !xe(t) && !(K && (L || Y(t) > 0) && Math.abs(t - fe) < .001)) {
347
- if (B = !1, Q && clearTimeout(Q), !a) {
344
+ r > 0 && Oe(Math.min(Math.max(t / r, 0), 1));
345
+ let i = performance.now(), a = i - ke < O;
346
+ if (ke = i, !Ce(t) && !(K && (I || Y(t) > 0) && Math.abs(t - G) < .001)) {
347
+ if (z = !1, Q && clearTimeout(Q), !a) {
348
348
  Q = void 0, J(t);
349
349
  return;
350
350
  }
351
351
  Q = setTimeout(() => {
352
352
  if (Q = void 0, N) return;
353
353
  let e = n.currentTime;
354
- xe(e) || (B = !1, J(e));
354
+ Ce(e) || (z = !1, J(e));
355
355
  }, O);
356
356
  }
357
357
  };
358
- n.addEventListener("seeking", Oe), M.push(() => n.removeEventListener("seeking", Oe));
359
- let $ = !1, ke = () => {
358
+ n.addEventListener("seeking", Ae), M.push(() => n.removeEventListener("seeking", Ae));
359
+ let $ = !1, je = () => {
360
360
  if ($) return;
361
361
  $ = !0;
362
362
  let e = n.error;
363
363
  q(new g(`the media element failed: ${e?.message ?? "unknown"}`, { cause: e }), !1, !0);
364
364
  };
365
- n.addEventListener("error", ke), M.push(() => n.removeEventListener("error", ke));
366
- let Ae = setInterval(() => {
367
- $ || n.error || (pe().catch(() => {}), be().catch((e) => q(e, !1)), B && !H && _e() && m.readyState === "open" && le().catch(() => {}));
365
+ n.addEventListener("error", je), M.push(() => n.removeEventListener("error", je));
366
+ let Me = setInterval(() => {
367
+ $ || n.error || (me().catch(() => {}), Se().catch((e) => q(e, !1)), be() && !V && ve() && m.readyState === "open" && ue().catch(() => {}));
368
368
  }, 100);
369
- return M.push(() => clearInterval(Ae)), {
370
- destroy: P,
371
- prepareSeek: we,
369
+ return M.push(() => clearInterval(Me)), {
370
+ destroy: se,
371
+ prepareSeek: Ee,
372
372
  videoElement: n,
373
373
  selectSubtitleStream: (e) => s.selectStream(e),
374
374
  indexes: e.indexes ?? [],
@@ -378,9 +378,9 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
378
378
  audioMimeType: e.info.output.audioMimeType
379
379
  };
380
380
  } catch (e) {
381
- throw P(), e;
381
+ throw se(), e;
382
382
  }
383
- }, ne = 5, re = 500, ie = 320, ae = 1e6, oe = 3, j = 1e4, M = 1.5, N = 4e6, P = 3e4, F = async (e) => {
383
+ }, ne = 5, re = 500, ie = 320, ae = 1e6, oe = 3, j = 1e4, M = 1.5, N = 4e6, se = 3e4, P = async (e) => {
384
384
  let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s = ie } = e, c = await t({
385
385
  publicPath: n,
386
386
  workerUrl: r,
@@ -469,7 +469,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
469
469
  }, w = async (e) => {
470
470
  _ = !0;
471
471
  try {
472
- let t = await Promise.race([c.init(), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")), P))]);
472
+ let t = await Promise.race([c.init(), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")), se))]);
473
473
  if (d) return;
474
474
  h = e;
475
475
  let n = l(t.indexes);
@@ -509,8 +509,8 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
509
509
  } catch (e) {
510
510
  throw A(c), e;
511
511
  }
512
- }, se = 1280, ce = 1920, le = 250, I = 100, L = () => typeof HTMLCanvasElement < "u" && "captureStream" in HTMLCanvasElement.prototype, R = () => typeof document < "u" && typeof HTMLVideoElement < "u" && typeof HTMLVideoElement.prototype.requestPictureInPicture == "function" && !!document.pictureInPictureEnabled, z = () => typeof HTMLVideoElement < "u" && "mozCaptureStream" in HTMLVideoElement.prototype, ue = () => L() ? R() ? "window" : z() ? "burn-in" : null : null, B = (e) => {
513
- let { video: t, subtitles: n } = e, r = e.mode ?? ue() ?? "window", i = e.maxWidth ?? (r === "burn-in" ? ce : se), 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) => {
512
+ }, ce = 1280, le = 1920, ue = 250, F = 100, I = () => 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, de = () => I() ? L() ? "window" : R() ? "burn-in" : null : null, z = (e) => {
513
+ let { video: t, subtitles: n } = e, r = e.mode ?? de() ?? "window", i = e.maxWidth ?? (r === "burn-in" ? le : ce), 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) => {
514
514
  r.drawImage(t, 0, 0, e.width, e.height);
515
515
  let i = n.querySelector("canvas");
516
516
  i && i.width > 0 && i.height > 0 && r.drawImage(i, 0, 0, e.width, e.height);
@@ -539,11 +539,11 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
539
539
  l = !0;
540
540
  let { mirror: e } = a;
541
541
  h(), e.play().then(() => new Promise((e) => {
542
- setTimeout(e, I);
542
+ setTimeout(e, F);
543
543
  })).catch(() => {}).finally(() => {
544
544
  e.pause(), setTimeout(() => {
545
545
  l = !1;
546
- }, I);
546
+ }, F);
547
547
  });
548
548
  }, E = [["play", () => {
549
549
  t.play().catch(() => {});
@@ -580,7 +580,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
580
580
  };
581
581
  if (a = f, u.srcObject = f.stream, (e.container ?? t.parentElement ?? document.body).appendChild(u), v(), s = setInterval(() => {
582
582
  t.paused && h();
583
- }, le), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
583
+ }, ue), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
584
584
  let t = () => {
585
585
  clearTimeout(n), e();
586
586
  }, n = setTimeout(t, 1e3);
@@ -626,4 +626,4 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
626
626
  };
627
627
  };
628
628
  //#endregion
629
- export { g as a, A as c, i as d, o as f, k as i, s as l, ue as n, _ as o, F as r, te as s, B as t, h as u };
629
+ export { g as a, A as c, i as d, o as f, k as i, s as l, de as n, _ as o, P as r, te as s, z as t, h 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-DdbOiEX3.js";
1
+ import { l as e, n as t, o as n, r, s as i, t as a, u as o } from "./engine-DQ537ehC.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, useSyncExternalStore as h } from "react";
3
3
  import { css as g, keyframes as _ } from "@emotion/react";
4
4
  import { createPlayer as v, useContainerAttach as y, useMediaAttach as b } from "@videojs/react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banou/media-player",
3
- "version": "0.10.14",
3
+ "version": "0.10.15",
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",
@@ -44,7 +44,7 @@
44
44
  "@videojs/react": "10.0.0-beta.26",
45
45
  "ass-compiler": "^0.1.16",
46
46
  "jassub": "^2.5.16",
47
- "libav-wasm": "^0.8.7",
47
+ "libav-wasm": "^0.8.9",
48
48
  "lucide-react": "^0.536.0",
49
49
  "osra": "^0.6.12",
50
50
  "react-tooltip": "^5.28.0"
@@ -271,6 +271,8 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
271
271
  let prepareTarget: number | undefined
272
272
  const bufferAnchor = () => prepareTarget ?? videoElement.currentTime
273
273
  let finished = false
274
+ // whether the end of the file was ever appended, which unlike `finished` a seek does not clear
275
+ let endAppended = false
274
276
  // libav aborts the running task when a new one starts, so both flags need a generation token
275
277
  let readGeneration = 0
276
278
  let seekGeneration = 0
@@ -364,7 +366,16 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
364
366
  const trackAppendedEnd = () => {
365
367
  const ends = getTimeRanges(sourceBuffer).map((r) => r.end)
366
368
  if (ends.length) lastAppendedEnd = Math.max(lastAppendedEnd, ...ends)
369
+ if (finished) endAppended = true
367
370
  }
371
+ /*
372
+ * Whether the end of the file is in the buffer. A seek clears `finished`, so past one it is read back
373
+ * from `buffered`, since evict() and the browser both remove data: counted from a flag alone, a move to
374
+ * the end of a file whose end had been removed signalled the end there, endOfStream cut the duration to
375
+ * what was buffered, and the playhead went back to 20 s of 30 in both engines (measured 2026-10-12).
376
+ */
377
+ const endInBuffer = () =>
378
+ finished || (endAppended && (getTimeRanges(sourceBuffer).at(-1)?.end ?? 0) >= lastAppendedEnd - 0.1)
368
379
 
369
380
  const seekTo = async (time: number) => {
370
381
  seeking = true
@@ -489,15 +500,17 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
489
500
  }
490
501
 
491
502
  /**
492
- * Whether a position has its runway, which stops at the end of the file once the remuxer has read to it.
503
+ * Whether a position has its runway, which stops at the end of the file while the end is in the buffer.
493
504
  *
494
505
  * No target in the last 3 s can have the full runway, and re-positioning the remuxer there re-appends
495
- * over the buffered end, after which Firefox 151 stays at the end and never fires `ended`. The end is
496
- * the furthest append, the one `endOfStream` waits on, never the declared duration: that is the longer
497
- * track, while an open MediaSource's `buffered` ends at the shorter one, and evict() reopens it on
498
- * every trim (measured 2026-10-11).
506
+ * over the buffered end, after which Firefox 151 stays at the end and never fires `ended`: its demuxer
507
+ * had read the track's last frame, and once that frame is replaced it waits for one starting at the
508
+ * track's end. So the end counts while it is buffered, even with the reader re-reading toward it after
509
+ * a seek back (measured 2026-10-12). The end is the furthest append, the one `endOfStream` waits on,
510
+ * never the declared duration: that is the longer track, while an open MediaSource's `buffered` ends at
511
+ * the shorter one, and evict() reopens it on every trim (measured 2026-10-11).
499
512
  */
500
- const hasRunway = (time: number) => runwayFrom(time) >= SEEK_RUNWAY || (finished && atEnd(time))
513
+ const hasRunway = (time: number) => runwayFrom(time) >= SEEK_RUNWAY || (endInBuffer() && atEnd(time))
501
514
 
502
515
  /**
503
516
  * Put the data in place for a position the playhead has NOT moved to yet.
@@ -692,7 +705,7 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
692
705
  evict().catch(() => {})
693
706
  pump().catch((error) => reportError(error, false))
694
707
  // every remove() puts the MediaSource back to 'open', so the end has to be re-armed
695
- if (finished && !pending && atEnd() && mediaSource.readyState === 'open') endOfStream().catch(() => {})
708
+ if (endInBuffer() && !pending && atEnd() && mediaSource.readyState === 'open') endOfStream().catch(() => {})
696
709
  }, 100)
697
710
  teardown.push(() => clearInterval(interval))
698
711