@banou/media-player 0.10.3 → 0.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var B = {
20
20
  selectAudioTrack: () => {},
21
21
  hideUI: !1,
22
22
  setHideUI: () => {},
23
+ hasMedia: !1,
23
24
  togglePictureInPicture: null,
24
25
  pictureInPictureMode: null,
25
26
  burnedInSubtitles: !1,
@@ -48,7 +49,7 @@ var B = {
48
49
  } catch {
49
50
  return;
50
51
  }
51
- }, G = (e) => e?.replace(/_/g, " ").trim() || void 0, ee = (e) => {
52
+ }, G = (e) => e?.replace(/_/g, " ").trim() || void 0, K = (e) => {
52
53
  let t = e.map((e) => W(e.language) ?? G(e.title) ?? `Track ${e.streamIndex}`), n = t.reduce((e, t) => ({
53
54
  ...e,
54
55
  [t]: (e[t] ?? 0) + 1
@@ -60,27 +61,27 @@ var B = {
60
61
  label: (n[i] ?? 0) > 1 && a && a !== i ? `${i} (${a})` : i
61
62
  };
62
63
  });
63
- }, K = (e) => ee(e).map(({ track: e, label: t }) => ({
64
+ }, q = (e) => K(e).map(({ track: e, label: t }) => ({
64
65
  id: e.streamIndex,
65
66
  label: t
66
- })), te = [
67
+ })), ee = [
67
68
  0,
68
69
  250,
69
70
  1e3,
70
71
  3e3,
71
72
  1e4
72
- ], ne = 6e4, re = 500, ie = 15e3, ae = (e) => e instanceof Error ? e.message : String(e), oe = (e) => {
73
+ ], te = 6e4, ne = 500, re = 15e3, ie = (e) => e instanceof Error ? e.message : String(e), ae = (e) => {
73
74
  let t = [], n = e?.cause;
74
75
  for (let e = 0; n != null && e < 8; e++) {
75
76
  if (typeof MediaError < "u" && n instanceof MediaError) {
76
77
  t.push(`MediaError code ${n.code}${n.message ? `: ${n.message}` : ""}`);
77
78
  break;
78
79
  }
79
- t.push(ae(n)), n = n?.cause;
80
+ t.push(ie(n)), n = n?.cause;
80
81
  }
81
82
  return t.length ? t.join("\n") : void 0;
82
- }, se = (e, t, r) => {
83
- let a = U(), { read: o, size: s, publicPath: c = "", libavWorkerUrl: l = "", jassubWorkerUrl: f = "", jassubWasmUrl: h = "", jassubLegacyWasmUrl: g, defaultFontUrl: _, bufferSize: v, autoplay: y = !1, chapters: b, seekPrepareBudgetMs: x = re } = r ?? {}, S = p(b);
83
+ }, oe = (e, t, r) => {
84
+ let a = U(), { read: o, size: s, publicPath: c = "", libavWorkerUrl: l = "", jassubWorkerUrl: f = "", jassubWasmUrl: h = "", jassubLegacyWasmUrl: g, defaultFontUrl: _, bufferSize: v, autoplay: y = !1, chapters: b, seekPrepareBudgetMs: x = ne } = r ?? {}, S = p(b);
84
85
  S.current = b;
85
86
  let [C, w] = m(void 0), [T, E] = m(0), D = p({
86
87
  count: 0,
@@ -117,7 +118,7 @@ var B = {
117
118
  a.setSourceState({ seekingTo: e });
118
119
  let c = t.videoElement, l = () => {
119
120
  c?.removeEventListener("seeked", l), clearTimeout(u), a.setSourceState({ seekingTo: void 0 });
120
- }, u = setTimeout(l, ie);
121
+ }, u = setTimeout(l, re);
121
122
  c?.addEventListener("seeked", l);
122
123
  let d = setTimeout(() => {
123
124
  o = "deadline", s();
@@ -147,7 +148,7 @@ var B = {
147
148
  ready: !1
148
149
  });
149
150
  let u = (t, n) => {
150
- let r = Date.now(), i = ae(t), o = oe(t), s = a.playbackErrors, c = s[s.length - 1];
151
+ let r = Date.now(), i = ie(t), o = ae(t), s = a.playbackErrors, c = s[s.length - 1];
151
152
  if (c && c.message === i && c.detail === o && c.recovered === n) {
152
153
  let e = {
153
154
  ...c,
@@ -172,11 +173,11 @@ var B = {
172
173
  let t = n(e);
173
174
  if (u(e, t), t) {
174
175
  let t = performance.now();
175
- t - D.current.at > ne && (D.current = {
176
+ t - D.current.at > te && (D.current = {
176
177
  count: 0,
177
178
  at: t
178
179
  });
179
- let n = te[Math.min(D.current.count, te.length - 1)];
180
+ let n = ee[Math.min(D.current.count, ee.length - 1)];
180
181
  D.current = {
181
182
  count: D.current.count + 1,
182
183
  at: t
@@ -213,13 +214,13 @@ var B = {
213
214
  onSeek: (e) => N.current?.(e),
214
215
  onSubtitleStreams: (e) => {
215
216
  r || a.setSourceState({
216
- subtitleTracks: K(e),
217
+ subtitleTracks: q(e),
217
218
  ...j.current ? {} : { selectedSubtitleTrack: e[0]?.streamIndex }
218
219
  });
219
220
  },
220
221
  onAudioStreams: (e, t) => {
221
222
  r || a.setSourceState({
222
- audioTracks: K(e),
223
+ audioTracks: q(e),
223
224
  selectedAudioTrack: t
224
225
  });
225
226
  }
@@ -259,13 +260,13 @@ var B = {
259
260
  y,
260
261
  T
261
262
  ]);
262
- }, ce = 5e3, le = 6e4, ue = ({ publicPath: e, workerUrl: t, length: n, read: i, downloadedRanges: a }) => {
263
+ }, se = 5e3, ce = 6e4, le = ({ publicPath: e, workerUrl: t, length: n, read: i, downloadedRanges: a }) => {
263
264
  let [o, s] = m([]), c = p(null), l = p(i);
264
265
  l.current = i;
265
266
  let h = f(() => a?.map(({ startByteOffset: e, endByteOffset: t }) => [e, t]), [a?.map(({ startByteOffset: e, endByteOffset: t }) => `${e}-${t}`).join(",")]), g = p(h);
266
267
  return g.current = h, d(() => {
267
268
  if (!n || !i || !e || !t) return;
268
- let a = !1, o = null, u, d = ce, f = () => {
269
+ let a = !1, o = null, u, d = se, f = () => {
269
270
  r({
270
271
  publicPath: e,
271
272
  workerUrl: t,
@@ -281,7 +282,7 @@ var B = {
281
282
  }
282
283
  o = e, c.current = e, e.update(g.current);
283
284
  }, () => {
284
- a || (u = setTimeout(f, d), d = Math.min(d * 2, le));
285
+ a || (u = setTimeout(f, d), d = Math.min(d * 2, ce));
285
286
  });
286
287
  };
287
288
  return f(), () => {
@@ -299,7 +300,7 @@ var B = {
299
300
  c.current?.prioritize(e);
300
301
  }, [])
301
302
  };
302
- }, de = (e, n) => {
303
+ }, ue = (e, n) => {
303
304
  let r = p(null), [i, o] = m(!1), [s] = m(() => t());
304
305
  d(() => {
305
306
  if (!e || !n || !s) return;
@@ -327,7 +328,7 @@ var B = {
327
328
  mode: s,
328
329
  burnedIn: i
329
330
  };
330
- }, q = {
331
+ }, J = {
331
332
  headings: {
332
333
  large: "\n font-weight: 600;\n font-size: calc(2.8 * var(--mp-unit));\n line-height: calc(3.4 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(3.4 * var(--mp-unit));\n line-height: calc(4.1 * var(--mp-unit));\n }\n ",
333
334
  medium: "\n font-weight: 600;\n font-size: calc(2.4 * var(--mp-unit));\n line-height: calc(2.9 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(2.8 * var(--mp-unit));\n line-height: calc(3.4 * var(--mp-unit));\n }\n ",
@@ -354,7 +355,7 @@ var B = {
354
355
  medium: "\n font-weight: 500;\n font-size: calc(0.8 * var(--mp-unit));\n line-height: calc(1.1 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(1 * var(--mp-unit));\n line-height: calc(1.4 * var(--mp-unit));\n }\n ",
355
356
  regular: "\n font-weight: 400;\n font-size: calc(0.8 * var(--mp-unit));\n line-height: calc(1.1 * var(--mp-unit));\n @media (min-width: 960px) {\n font-size: calc(1 * var(--mp-unit));\n line-height: calc(1.4 * var(--mp-unit));\n }\n "
356
357
  }
357
- }, fe = h`
358
+ }, de = h`
358
359
  display: flex;
359
360
  justify-content: center;
360
361
  align-items: center;
@@ -367,7 +368,7 @@ var B = {
367
368
  margin: auto;
368
369
  pointer-events: none;
369
370
  }
370
- `, pe = h`
371
+ `, fe = h`
371
372
  position: absolute;
372
373
  top: 0;
373
374
  left: 0;
@@ -375,7 +376,7 @@ var B = {
375
376
  padding: calc(1.2 * var(--mp-unit)) calc(1.6 * var(--mp-unit));
376
377
  /* clears a notch once the player is fullscreen; resolves to zero everywhere else */
377
378
  padding-top: calc(calc(1.2 * var(--mp-unit)) + env(safe-area-inset-top, 0px));
378
- ${q.headings.small}
379
+ ${J.headings.small}
379
380
  color: white;
380
381
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
381
382
  z-index: 2;
@@ -396,7 +397,7 @@ var B = {
396
397
 
397
398
  background: linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.3) 30%, rgba(0,0,0,0.2) 60%, rgba(0,0,0,0.1) 80%, transparent 100%);
398
399
  transition: opacity 0.1s cubic-bezier(.4,0,1,1);
399
- `, me = h`
400
+ `, pe = h`
400
401
  position: absolute;
401
402
  inset: 0;
402
403
  z-index: 2;
@@ -416,40 +417,40 @@ var B = {
416
417
  to { transform: rotate(360deg); }
417
418
  `} 0.8s linear infinite;
418
419
  }
419
- `, he = h`
420
+ `, me = h`
420
421
  position: absolute;
421
422
  inset: auto 0 20%;
422
423
  z-index: 3;
423
424
  padding: 0 calc(2 * var(--mp-unit));
424
425
  text-align: center;
425
426
  color: #fff;
426
- ${q.bLarge.regular}
427
+ ${J.bLarge.regular}
427
428
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
428
429
  pointer-events: none;
429
- `, ge = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", _e = ({ onSubtitleRef: e }) => {
430
+ `, he = (e) => e instanceof Error ? e.message : typeof e == "string" ? e : "Playback failed", ge = ({ onSubtitleRef: e }) => {
430
431
  let t = U((e) => e.title), n = U((e) => e.hideUI), r = U((e) => e.playbackError), i = U((e) => e.ready), a = U((e) => e.size), o = U((e) => e.waiting), s = U((e) => e.seekingTo);
431
432
  return /* @__PURE__ */ w(S, { children: [
432
433
  t ? /* @__PURE__ */ C("div", {
433
434
  className: "title",
434
- css: pe,
435
+ css: fe,
435
436
  style: { ...n ? {
436
437
  opacity: "0",
437
438
  pointerEvents: "none"
438
439
  } : {} },
439
440
  children: t
440
441
  }) : void 0,
441
- ((a ? !i : o) || s !== void 0) && !r ? /* @__PURE__ */ C("div", { css: me }) : void 0,
442
+ ((a ? !i : o) || s !== void 0) && !r ? /* @__PURE__ */ C("div", { css: pe }) : void 0,
442
443
  r ? /* @__PURE__ */ C("div", {
443
- css: he,
444
- children: ge(r)
444
+ css: me,
445
+ children: he(r)
445
446
  }) : void 0,
446
447
  /* @__PURE__ */ C("div", {
447
448
  className: "subtitles",
448
449
  ref: e,
449
- css: fe
450
+ css: de
450
451
  })
451
452
  ] });
452
- }, ve = 1e-4, J = 1, ye = (e) => Math.max(ve, Math.min(J, e)) ** 2, be = (e) => Math.max(ve, Math.min(J, e)) ** (J / 2), Y = (e) => {
453
+ }, _e = 1e-4, ve = 1, ye = (e) => Math.max(_e, Math.min(ve, e)) ** 2, be = (e) => Math.max(_e, Math.min(ve, e)) ** (ve / 2), Y = (e) => {
453
454
  if (e === void 0) return "0:00";
454
455
  let t = Math.floor(e / 3600), n = Math.floor(e % 3600 / 60), r = Math.floor(e % 60);
455
456
  return t > 0 ? `${t}:${n < 10 ? "0" : ""}${n}:${r < 10 ? "0" : ""}${r}` : `${n}:${r < 10 ? "0" : ""}${r}`;
@@ -485,7 +486,7 @@ var B = {
485
486
  z-index: 3;
486
487
 
487
488
  * {
488
- ${q.bMedium.regular}
489
+ ${J.bMedium.regular}
489
490
  }
490
491
 
491
492
  ${e === "sm" && h`
@@ -773,7 +774,7 @@ var B = {
773
774
  box-shadow: 0 0 calc(1 * var(--mp-unit)) rgba(0, 0, 0, .5);
774
775
 
775
776
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
776
- ${q.bMedium.bold}
777
+ ${J.bMedium.bold}
777
778
 
778
779
  gap: calc(.6 * var(--mp-unit));
779
780
 
@@ -989,7 +990,7 @@ var B = {
989
990
  V
990
991
  ]), W = f(() => V >= 0 ? je(z, (e) => e === V) : void 0, [z, V]), G = f(() => {
991
992
  if (y !== void 0) return u.find(({ start: e, end: t }) => e <= y && y < t);
992
- }, [u, y]), ee = f(() => {
993
+ }, [u, y]), K = f(() => {
993
994
  if (y) {
994
995
  if (c) return c(y);
995
996
  if (s.length) return s.find(({ startTime: e, endTime: t }) => e <= y && y < t);
@@ -998,7 +999,7 @@ var B = {
998
999
  s,
999
1000
  c,
1000
1001
  y
1001
- ]), K = (e, t) => /* @__PURE__ */ w("div", {
1002
+ ]), q = (e, t) => /* @__PURE__ */ w("div", {
1002
1003
  className: e ? `track ${e}` : "track",
1003
1004
  style: t ? { maskImage: t } : void 0,
1004
1005
  children: [
@@ -1027,7 +1028,7 @@ var B = {
1027
1028
  onMouseMove: P,
1028
1029
  onMouseOut: F,
1029
1030
  children: [
1030
- K(void 0, H),
1031
+ q(void 0, H),
1031
1032
  y ? /* @__PURE__ */ w("div", {
1032
1033
  className: "cursor-time",
1033
1034
  style: {
@@ -1044,7 +1045,7 @@ var B = {
1044
1045
  }) : void 0,
1045
1046
  /* @__PURE__ */ C("div", { className: "progress" }),
1046
1047
  /* @__PURE__ */ C("div", { className: "hover" }),
1047
- W ? K("focus", W) : void 0,
1048
+ W ? q("focus", W) : void 0,
1048
1049
  /* @__PURE__ */ C("div", { className: "scrubber" }),
1049
1050
  /* @__PURE__ */ C("div", {
1050
1051
  className: "padding",
@@ -1056,7 +1057,7 @@ var B = {
1056
1057
  /* @__PURE__ */ C("div", {
1057
1058
  className: "thumbnail",
1058
1059
  style: { left: `clamp(var(--thumbnail-half), ${I(y ?? 1)}%, calc(100% - var(--thumbnail-half)))` },
1059
- children: ee?.url ? /* @__PURE__ */ C("img", { src: ee.url }) : void 0
1060
+ children: K?.url ? /* @__PURE__ */ C("img", { src: K.url }) : void 0
1060
1061
  })
1061
1062
  ]
1062
1063
  });
@@ -1170,7 +1171,7 @@ var B = {
1170
1171
 
1171
1172
  border-radius: 8px;
1172
1173
  background-color: rgba(28,28,28,0.95);
1173
- ${q.bMedium.regular}
1174
+ ${J.bMedium.regular}
1174
1175
 
1175
1176
  z-index: 4;
1176
1177
 
@@ -1213,7 +1214,7 @@ var B = {
1213
1214
 
1214
1215
  .secondary {
1215
1216
  color: #eee;
1216
- ${q.bSmall.regular}
1217
+ ${J.bSmall.regular}
1217
1218
  }
1218
1219
  }
1219
1220
  }
@@ -1255,7 +1256,7 @@ var B = {
1255
1256
  .failed {
1256
1257
  border-bottom: 1px solid #4D4D4E;
1257
1258
  color: #f66;
1258
- ${q.bSmall.regular}
1259
+ ${J.bSmall.regular}
1259
1260
  }
1260
1261
  }
1261
1262
  `, it = ({ title: e, tracks: t, selected: n, onSelect: r, onBack: i, offLabel: a, pending: o, failed: s }) => {
@@ -1370,7 +1371,7 @@ ${rt}
1370
1371
 
1371
1372
  color: #fff;
1372
1373
  cursor: pointer;
1373
- ${q.bSmall.regular}
1374
+ ${J.bSmall.regular}
1374
1375
 
1375
1376
  &:hover {
1376
1377
  background-color: rgba(255,255,255,.1);
@@ -1391,7 +1392,7 @@ ${rt}
1391
1392
 
1392
1393
  .when {
1393
1394
  color: #bbb;
1394
- ${q.bSmall.regular}
1395
+ ${J.bSmall.regular}
1395
1396
 
1396
1397
  /* the one part of the line worth scanning for, so it is the one part at full brightness */
1397
1398
  .count {
@@ -1410,7 +1411,7 @@ ${rt}
1410
1411
  color: #ddd;
1411
1412
  overflow-wrap: anywhere;
1412
1413
  white-space: pre-wrap;
1413
- ${q.bSmall.regular}
1414
+ ${J.bSmall.regular}
1414
1415
  }
1415
1416
  }
1416
1417
  }
@@ -1874,7 +1875,7 @@ ${rt}
1874
1875
  border-radius: 4px;
1875
1876
 
1876
1877
  .volume-value {
1877
- ${q.bMedium.regular};
1878
+ ${J.bMedium.regular};
1878
1879
  color: #ffffff;
1879
1880
  }
1880
1881
  }
@@ -2060,7 +2061,7 @@ ${rt}
2060
2061
 
2061
2062
  .left {
2062
2063
  .time {
2063
- ${q.bMedium.regular}
2064
+ ${J.bMedium.regular}
2064
2065
  text-shadow: 0 0 4px rgba(0, 0, 0, 1);
2065
2066
  }
2066
2067
  }
@@ -2242,12 +2243,12 @@ ${rt}
2242
2243
  }
2243
2244
 
2244
2245
  .title {
2245
- ${q.bMedium.bold}
2246
+ ${J.bMedium.bold}
2246
2247
  color: #fff;
2247
2248
  }
2248
2249
 
2249
2250
  .body {
2250
- ${q.bSmall.regular}
2251
+ ${J.bSmall.regular}
2251
2252
  color: rgba(255, 255, 255, 0.75);
2252
2253
  }
2253
2254
 
@@ -2310,7 +2311,7 @@ ${rt}
2310
2311
  pointer-events: auto;
2311
2312
  cursor: pointer;
2312
2313
 
2313
- ${q.bMedium.bold}
2314
+ ${J.bMedium.bold}
2314
2315
  color: #fff;
2315
2316
  padding: calc(1 * var(--mp-unit)) calc(1.8 * var(--mp-unit));
2316
2317
  border: 1px solid rgba(255, 255, 255, .55);
@@ -2447,49 +2448,53 @@ ${rt}
2447
2448
  item: e
2448
2449
  }];
2449
2450
  }), Bt = ({ ref: e, onVideoRef: t, onSubtitleRef: n, overlay: r, controls: i, children: a }) => {
2450
- let o = U(), s = U((e) => e.hideUI), c = U((e) => e.setHideUI), l = p(void 0), u = p("mouse"), f = p({
2451
+ let o = U(), s = U((e) => e.hideUI), c = U((e) => e.setHideUI), l = U((e) => e.hasMedia), u = p(void 0), f = p("mouse"), m = p({
2451
2452
  x: -1,
2452
2453
  y: -1
2453
- }), m = p(null);
2454
- d(() => () => clearTimeout(l.current), []);
2455
- let h = (t) => {
2456
- m.current = t, typeof e == "function" ? e(t) : e && (e.current = t);
2457
- }, g = () => {
2458
- if (u.current !== "mouse") return !1;
2459
- let { x: e, y: t } = f.current;
2460
- if (!m.current || e < 0) return !1;
2454
+ }), h = p(null);
2455
+ d(() => () => clearTimeout(u.current), []), d(() => {
2456
+ l || (clearTimeout(u.current), c(!1));
2457
+ }, [l, c]);
2458
+ let g = () => {
2459
+ l && c(!0);
2460
+ }, _ = (t) => {
2461
+ h.current = t, typeof e == "function" ? e(t) : e && (e.current = t);
2462
+ }, v = () => {
2463
+ if (f.current !== "mouse") return !1;
2464
+ let { x: e, y: t } = m.current;
2465
+ if (!h.current || e < 0) return !1;
2461
2466
  let n = document.elementFromPoint(e, t);
2462
- return !!n && m.current.contains(n) && !n.closest(".video");
2463
- }, _ = () => {
2464
- if (g()) {
2465
- l.current = setTimeout(_, It);
2467
+ return !!n && h.current.contains(n) && !n.closest(".video");
2468
+ }, y = () => {
2469
+ if (v()) {
2470
+ u.current = setTimeout(y, It);
2466
2471
  return;
2467
2472
  }
2468
- c(!0);
2469
- }, v = () => {
2470
- c(!1), clearTimeout(l.current), l.current = setTimeout(_, It);
2473
+ g();
2474
+ }, b = () => {
2475
+ c(!1), clearTimeout(u.current), u.current = setTimeout(y, It);
2471
2476
  };
2472
2477
  return /* @__PURE__ */ w("div", {
2473
2478
  css: Rt,
2474
- ref: h,
2479
+ ref: _,
2475
2480
  onPointerMove: (e) => {
2476
- u.current = e.pointerType, e.pointerType === "mouse" && (f.current = {
2481
+ f.current = e.pointerType, e.pointerType === "mouse" && (m.current = {
2477
2482
  x: e.clientX,
2478
2483
  y: e.clientY
2479
- }, v());
2484
+ }, b());
2480
2485
  },
2481
2486
  onPointerDown: (e) => {
2482
- u.current = e.pointerType;
2487
+ f.current = e.pointerType;
2483
2488
  },
2484
2489
  onMouseOut: (e) => {
2485
2490
  let t = e.relatedTarget;
2486
2491
  if (t && e.currentTarget.contains(t)) return;
2487
2492
  let { left: n, right: r, top: i, bottom: a } = e.currentTarget.getBoundingClientRect();
2488
- e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(l.current), c(!0));
2493
+ e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(u.current), g());
2489
2494
  },
2490
2495
  className: s ? "hide" : "",
2491
2496
  children: [
2492
- /* @__PURE__ */ C(_e, { onSubtitleRef: n }),
2497
+ /* @__PURE__ */ C(ge, { onSubtitleRef: n }),
2493
2498
  zt(r).map(({ key: e, item: t }) => /* @__PURE__ */ C("div", {
2494
2499
  css: Lt,
2495
2500
  style: { ...s ? {
@@ -2505,11 +2510,11 @@ ${rt}
2505
2510
  /* @__PURE__ */ w("div", {
2506
2511
  className: "video",
2507
2512
  onClick: () => {
2508
- if (u.current === "mouse") {
2513
+ if (f.current === "mouse") {
2509
2514
  o.togglePaused();
2510
2515
  return;
2511
2516
  }
2512
- s ? v() : (clearTimeout(l.current), c(!0));
2517
+ s ? b() : (clearTimeout(u.current), g());
2513
2518
  },
2514
2519
  children: [t ? /* @__PURE__ */ C("video", {
2515
2520
  ref: t,
@@ -2538,31 +2543,33 @@ ${rt}
2538
2543
  k,
2539
2544
  A,
2540
2545
  j
2541
- ]), se(b, S, r);
2542
- let { thumbnails: M, requestThumbnail: N } = ue({
2546
+ ]), oe(b, S, r);
2547
+ let { thumbnails: M, requestThumbnail: N } = le({
2543
2548
  publicPath: c,
2544
2549
  workerUrl: l,
2545
2550
  length: h === !1 ? void 0 : o,
2546
2551
  read: f ?? u,
2547
2552
  downloadedRanges: s
2548
- }), P = n?.thumbnails?.all ?? M, { toggle: F, mode: I, burnedIn: L } = de(b, S), R = U((e) => e.setSourceState);
2553
+ }), P = n?.thumbnails?.all ?? M, { toggle: F, mode: I, burnedIn: L } = ue(b, S), R = U((e) => e.setSourceState), z = n ? !!n.media : o !== void 0;
2549
2554
  d(() => {
2550
2555
  R({
2551
2556
  title: i,
2552
2557
  size: o,
2553
- downloadedRanges: s
2558
+ downloadedRanges: s,
2559
+ hasMedia: z
2554
2560
  });
2555
2561
  }, [
2556
2562
  R,
2557
2563
  i,
2558
2564
  o,
2559
- s
2565
+ s,
2566
+ z
2560
2567
  ]);
2561
- let z = n?.thumbnails?.at;
2568
+ let B = n?.thumbnails?.at;
2562
2569
  d(() => {
2563
2570
  R({
2564
2571
  thumbnails: P,
2565
- thumbnailAt: z,
2572
+ thumbnailAt: B,
2566
2573
  requestThumbnail: N,
2567
2574
  togglePictureInPicture: F,
2568
2575
  pictureInPictureMode: I,
@@ -2571,35 +2578,35 @@ ${rt}
2571
2578
  }, [
2572
2579
  R,
2573
2580
  P,
2574
- z,
2581
+ B,
2575
2582
  N,
2576
2583
  F,
2577
2584
  I,
2578
2585
  L
2579
2586
  ]);
2580
- let B = n?.subtitles?.selection, V = n?.audioTracks?.selection;
2587
+ let V = n?.subtitles?.selection, H = n?.audioTracks?.selection;
2581
2588
  return d(() => {
2582
- B && R({
2583
- subtitleTracks: B.options.map(({ id: e, label: t, disabled: n }) => ({
2589
+ V && R({
2590
+ subtitleTracks: V.options.map(({ id: e, label: t, disabled: n }) => ({
2584
2591
  id: e,
2585
2592
  label: t,
2586
2593
  disabled: n
2587
2594
  })),
2588
- selectedSubtitleTrack: B.selectedId ?? void 0,
2589
- selectSubtitleTrack: (e) => B.select(e == null ? null : String(e)),
2590
- subtitleOffLabel: B.offLabel
2595
+ selectedSubtitleTrack: V.selectedId ?? void 0,
2596
+ selectSubtitleTrack: (e) => V.select(e == null ? null : String(e)),
2597
+ subtitleOffLabel: V.offLabel
2591
2598
  });
2592
- }, [R, B]), d(() => {
2593
- V && R({
2594
- audioTracks: V.options.map(({ id: e, label: t, disabled: n }) => ({
2599
+ }, [R, V]), d(() => {
2600
+ H && R({
2601
+ audioTracks: H.options.map(({ id: e, label: t, disabled: n }) => ({
2595
2602
  id: e,
2596
2603
  label: t,
2597
2604
  disabled: n
2598
2605
  })),
2599
- selectedAudioTrack: V.selectedId ?? void 0,
2600
- selectAudioTrack: (e) => V.select(String(e))
2606
+ selectedAudioTrack: H.selectedId ?? void 0,
2607
+ selectAudioTrack: (e) => H.select(String(e))
2601
2608
  });
2602
- }, [R, V]), d(() => {
2609
+ }, [R, H]), d(() => {
2603
2610
  a && R({ chapters: a });
2604
2611
  }, [R, a]), /* @__PURE__ */ C(Bt, {
2605
2612
  ref: _,
@@ -2726,4 +2733,4 @@ ${rt}
2726
2733
  throw Error(`Unknown source type: ${e}`);
2727
2734
  };
2728
2735
  //#endregion
2729
- export { Ut as MediaPlayer, Ut as default, H as Player, e as SUBTITLES_OFF, a as createPictureInPicture, o as createSubtitleRenderer, r as createThumbnailGenerator, Yt as inputToRemuxerInput, Gt as isDelegatedTracks, Wt as isExternalThumbnails, V as sourceFeature, i as startPlayback, de as usePictureInPicture, U as usePlayer, ue as useSeekThumbnails };
2736
+ export { Ut as MediaPlayer, Ut as default, H as Player, e as SUBTITLES_OFF, a as createPictureInPicture, o as createSubtitleRenderer, r as createThumbnailGenerator, Yt as inputToRemuxerInput, Gt as isDelegatedTracks, Wt as isExternalThumbnails, V as sourceFeature, i as startPlayback, ue as usePictureInPicture, U as usePlayer, le as useSeekThumbnails };
@@ -16,6 +16,7 @@ export declare const Player: import("@videojs/react").CreatePlayerResult<import(
16
16
  selectAudioTrack: (id: string | number) => void | Promise<void>;
17
17
  hideUI: boolean;
18
18
  setHideUI: (hide: boolean) => void;
19
+ hasMedia: boolean;
19
20
  togglePictureInPicture: (() => void) | null;
20
21
  pictureInPictureMode: import("../engine").PictureInPictureMode | null;
21
22
  burnedInSubtitles: boolean;
@@ -116,6 +116,19 @@ export type SourceState = {
116
116
  /** Chrome auto-hide. True means the controls, title and cursor are hidden. */
117
117
  hideUI: boolean;
118
118
  setHideUI: (hide: boolean) => void;
119
+ /**
120
+ * Whether there is anything loaded to play at all.
121
+ *
122
+ * Not the same question as `ready`, which is about a source that HAS been handed over and is still
123
+ * booting. This one separates that from having been given nothing, and the chrome needs the
124
+ * difference: idle-hiding with no media leaves a black rectangle with no controls, no title and no
125
+ * cursor, and no way back, since the only thing that reveals the chrome again is a pointer move over
126
+ * a player the viewer has no reason to think is there.
127
+ *
128
+ * Written by the React layer, which is the only place both arms are visible: bytes for a local
129
+ * source, an element for a media this player does not own.
130
+ */
131
+ hasMedia: boolean;
119
132
  /**
120
133
  * Owned here, not by `pip`: that watches the media element, and the window holds a mirror.
121
134
  *
@@ -234,6 +247,19 @@ export declare const sourceFeature: import("@videojs/react").PlayerFeature<{
234
247
  /** Chrome auto-hide. True means the controls, title and cursor are hidden. */
235
248
  hideUI: boolean;
236
249
  setHideUI: (hide: boolean) => void;
250
+ /**
251
+ * Whether there is anything loaded to play at all.
252
+ *
253
+ * Not the same question as `ready`, which is about a source that HAS been handed over and is still
254
+ * booting. This one separates that from having been given nothing, and the chrome needs the
255
+ * difference: idle-hiding with no media leaves a black rectangle with no controls, no title and no
256
+ * cursor, and no way back, since the only thing that reveals the chrome again is a pointer move over
257
+ * a player the viewer has no reason to think is there.
258
+ *
259
+ * Written by the React layer, which is the only place both arms are visible: bytes for a local
260
+ * source, an element for a media this player does not own.
261
+ */
262
+ hasMedia: boolean;
237
263
  /**
238
264
  * Owned here, not by `pip`: that watches the media element, and the window holds a mirror.
239
265
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banou/media-player",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
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",
@@ -119,6 +119,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, chil
119
119
  const player = usePlayer()
120
120
  const hideUI = usePlayer((state) => state.hideUI)
121
121
  const setHideUI = usePlayer((state) => state.setHideUI)
122
+ const hasMedia = usePlayer((state) => state.hasMedia)
122
123
  const autoHide = useRef<ReturnType<typeof setTimeout>>(undefined)
123
124
  // a tap and a click mean different things, so the last pointer kind is remembered
124
125
  const lastPointerType = useRef<string>('mouse')
@@ -128,6 +129,27 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, chil
128
129
 
129
130
  useEffect(() => () => clearTimeout(autoHide.current), [])
130
131
 
132
+ /*
133
+ * With nothing loaded the chrome never hides, and anything already hidden comes back.
134
+ *
135
+ * Hiding is only ever reversed by a pointer move over the player, which is a fair bet while a video
136
+ * is playing and a bad one when the box is empty: three seconds after mount the controls, the title
137
+ * and the cursor all go, leaving a black rectangle that gives the viewer no reason to think there is
138
+ * a player there to wave at. The effect covers the media going AWAY, which the guard below cannot:
139
+ * its timer may already be in flight, holding a `hasMedia` that was true when it was scheduled.
140
+ */
141
+ useEffect(() => {
142
+ if (hasMedia) return
143
+ clearTimeout(autoHide.current)
144
+ setHideUI(false)
145
+ }, [hasMedia, setHideUI])
146
+
147
+ /** The single door to hiding, so no path can bypass the empty-player case. */
148
+ const hide = () => {
149
+ if (!hasMedia) return
150
+ setHideUI(true)
151
+ }
152
+
131
153
  // The caller's ref still gets the element: video.js attaches the fullscreen container through it,
132
154
  // and this needs the same node to hit test against.
133
155
  const setRoot = (element: HTMLDivElement | null) => {
@@ -165,7 +187,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, chil
165
187
  autoHide.current = setTimeout(hideUnlessTheMouseIsOnAControl, AUTO_HIDE_DELAY)
166
188
  return
167
189
  }
168
- setHideUI(true)
190
+ hide()
169
191
  }
170
192
 
171
193
  const reveal = () => {
@@ -196,7 +218,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, chil
196
218
  if (hideUI) reveal()
197
219
  else {
198
220
  clearTimeout(autoHide.current)
199
- setHideUI(true)
221
+ hide()
200
222
  }
201
223
  }
202
224
 
@@ -209,7 +231,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, chil
209
231
  const inside = ev.clientX >= left && ev.clientX < right && ev.clientY >= top && ev.clientY < bottom
210
232
  if (inside) return
211
233
  clearTimeout(autoHide.current)
212
- setHideUI(true)
234
+ hide()
213
235
  }
214
236
 
215
237
  return (
@@ -127,6 +127,20 @@ export type SourceState = {
127
127
  hideUI: boolean
128
128
  setHideUI: (hide: boolean) => void
129
129
 
130
+ /**
131
+ * Whether there is anything loaded to play at all.
132
+ *
133
+ * Not the same question as `ready`, which is about a source that HAS been handed over and is still
134
+ * booting. This one separates that from having been given nothing, and the chrome needs the
135
+ * difference: idle-hiding with no media leaves a black rectangle with no controls, no title and no
136
+ * cursor, and no way back, since the only thing that reveals the chrome again is a pointer move over
137
+ * a player the viewer has no reason to think is there.
138
+ *
139
+ * Written by the React layer, which is the only place both arms are visible: bytes for a local
140
+ * source, an element for a media this player does not own.
141
+ */
142
+ hasMedia: boolean
143
+
130
144
  /**
131
145
  * Owned here, not by `pip`: that watches the media element, and the window holds a mirror.
132
146
  *
@@ -210,6 +224,7 @@ const initialState: SourceState = {
210
224
  selectAudioTrack: () => {},
211
225
  hideUI: false,
212
226
  setHideUI: () => {},
227
+ hasMedia: false,
213
228
  togglePictureInPicture: null,
214
229
  pictureInPictureMode: null,
215
230
  burnedInSubtitles: false,
@@ -269,9 +269,16 @@ const PlayerRoot = ({ options, children }: { options: MediaPlayerOptions, childr
269
269
  // attaches: when attach swaps in the real setter the identity changes and these publish again.
270
270
  const setSourceState = usePlayer((state) => state.setSourceState)
271
271
 
272
+ /*
273
+ * Read here because this is the only place both arms are in scope: a local source is bytes, and a
274
+ * remote one is an element somebody else owns. `size` alone cannot answer it, since the remote arm
275
+ * legitimately has none, and the chrome would then treat every remote player as having no media.
276
+ */
277
+ const hasMedia = remote ? !!remote.media : size !== undefined
278
+
272
279
  useEffect(() => {
273
- setSourceState({ title, size, downloadedRanges })
274
- }, [setSourceState, title, size, downloadedRanges])
280
+ setSourceState({ title, size, downloadedRanges, hasMedia })
281
+ }, [setSourceState, title, size, downloadedRanges, hasMedia])
275
282
 
276
283
  const thumbnailAt = remote?.thumbnails?.at
277
284
  useEffect(() => {