@banou/media-player 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +2 -2
- package/dist/engine/picture-in-picture.d.ts +18 -6
- package/dist/{engine-Dmc7JSd9.js → engine--OlGG-Ve.js} +128 -97
- package/dist/index.js +757 -448
- package/dist/react/components/burn-in-hint.d.ts +9 -0
- package/dist/react/components/icons.d.ts +12 -0
- package/dist/react/components/subtitles.d.ts +9 -0
- package/dist/react/components/track-menu.d.ts +47 -0
- package/dist/react/hooks/use-picture-in-picture.d.ts +17 -2
- package/dist/react/player.d.ts +2 -0
- package/dist/react/source-feature.d.ts +17 -1
- package/dist/utils/colors.d.ts +2 -0
- package/package.json +1 -1
- package/src/lib/engine/index.ts +2 -2
- package/src/lib/engine/picture-in-picture.ts +183 -49
- package/src/lib/react/components/burn-in-hint.tsx +105 -0
- package/src/lib/react/components/chrome.tsx +3 -0
- package/src/lib/react/components/control-bar.tsx +80 -5
- package/src/lib/react/components/icons.tsx +53 -0
- package/src/lib/react/components/progress-bar.tsx +20 -4
- package/src/lib/react/components/settings.tsx +21 -279
- package/src/lib/react/components/subtitles.tsx +80 -0
- package/src/lib/react/components/track-menu.tsx +277 -0
- package/src/lib/react/hooks/use-picture-in-picture.ts +30 -12
- package/src/lib/react/source-feature.ts +12 -1
- package/src/lib/react/video-player.tsx +54 -3
- package/src/lib/utils/colors.ts +2 -0
package/dist/engine/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export { createThumbnailGenerator } from './thumbnails';
|
|
|
6
6
|
export type { ThumbnailGenerator, ThumbnailGeneratorOptions, ThumbnailImage } from './thumbnails';
|
|
7
7
|
export { getTimeRanges, updateSourceBuffer } from './source-buffer';
|
|
8
8
|
export type { TimeRange } from './source-buffer';
|
|
9
|
-
export { createPictureInPicture } from './picture-in-picture';
|
|
10
|
-
export type { PictureInPictureController, PictureInPictureOptions } from './picture-in-picture';
|
|
9
|
+
export { createPictureInPicture, pictureInPictureMode } from './picture-in-picture';
|
|
10
|
+
export type { PictureInPictureController, PictureInPictureOptions, PictureInPictureMode } from './picture-in-picture';
|
package/dist/engine/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "../engine-
|
|
2
|
-
export {
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "../engine--OlGG-Ve.js";
|
|
2
|
+
export { n as DEFAULT_BUFFER_SIZE, s as SUBTITLES_OFF, c as createPictureInPicture, t as createSubtitleRenderer, o as createThumbnailGenerator, r as getTimeRanges, i as pictureInPictureMode, e as startPlayback, a as terminateRemuxer, l as updateSourceBuffer };
|
|
@@ -6,22 +6,34 @@
|
|
|
6
6
|
* `captureStream()` backs a hidden mirror element, which is the one that enters the window. The
|
|
7
7
|
* original element keeps playing and stays the only audio source.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* `window` opens a real picture in picture window. `burn-in` cannot, and instead paints the
|
|
11
|
+
* composite into the player itself so the browser's OWN picture in picture control carries the
|
|
12
|
+
* subtitles with it.
|
|
13
|
+
*/
|
|
14
|
+
export type PictureInPictureMode = 'window' | 'burn-in';
|
|
9
15
|
export type PictureInPictureOptions = {
|
|
10
16
|
video: HTMLVideoElement;
|
|
11
17
|
/** The subtitle canvas. jassub sizes it to the video's content rect, so it maps 1:1. */
|
|
12
18
|
canvas: HTMLCanvasElement;
|
|
13
19
|
maxWidth?: number;
|
|
14
|
-
/** Where the
|
|
20
|
+
/** Where the mirror is mounted. It must be in the document. Defaults to the video's parent. */
|
|
15
21
|
container?: HTMLElement;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
fallback?: () => Promise<void>;
|
|
22
|
+
/** Overrides detection. Passing a mode the browser cannot honour is the caller's problem. */
|
|
23
|
+
mode?: PictureInPictureMode;
|
|
24
|
+
/** Burn-in only, so the chrome can light the control up and say what happened. */
|
|
25
|
+
onBurnedInChange?: (burnedIn: boolean) => void;
|
|
21
26
|
};
|
|
22
27
|
export type PictureInPictureController = {
|
|
23
28
|
toggle: () => Promise<void>;
|
|
24
29
|
destroy: () => void;
|
|
30
|
+
mode: PictureInPictureMode;
|
|
25
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* null means offer no control at all. That covers Safari, whose picture in picture is
|
|
34
|
+
* `webkitSetPresentationMode` and not the W3C API, so neither arm here can drive it. A dead button
|
|
35
|
+
* is worse than no button.
|
|
36
|
+
*/
|
|
37
|
+
export declare const pictureInPictureMode: () => PictureInPictureMode | null;
|
|
26
38
|
/** Takes over the document's Media Session play/pause handlers while a session is open. */
|
|
27
39
|
export declare const createPictureInPicture: (options: PictureInPictureOptions) => PictureInPictureController;
|
|
@@ -220,82 +220,82 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
220
220
|
i(), n(/* @__PURE__ */ Error("The MediaSource closed before a SourceBuffer could be created"));
|
|
221
221
|
};
|
|
222
222
|
C.addEventListener("sourceopen", a, { once: !0 }), C.addEventListener("sourceclose", o, { once: !0 }), C.addEventListener("error", o, { once: !0 });
|
|
223
|
-
}), { appendBuffer: L, unbufferRange: R, updateTimestampOffset:
|
|
223
|
+
}), { appendBuffer: L, unbufferRange: R, updateTimestampOffset: z, endOfStream: B } = s(T, C);
|
|
224
224
|
await L(e.data), e.subtitles?.length && o.pushFragments(e.subtitles), D?.();
|
|
225
|
-
let
|
|
225
|
+
let V = !1, H = !1, U = !1, W = 0, G = 0, K = null, q = 0, J = 0, Y = !1, X = 0, Z = (e, t) => {
|
|
226
226
|
let n = e?.message === "Cancelled";
|
|
227
|
-
t && n || (console.error(e), !
|
|
228
|
-
},
|
|
227
|
+
t && n || (console.error(e), !Y && (Y = !0, O?.(n ? Error("Reading the video file failed", { cause: e }) : e)));
|
|
228
|
+
}, Q = async (e = !1) => {
|
|
229
229
|
let t = n.currentTime, r = e ? v : h, i = e ? y : g;
|
|
230
230
|
for (let { start: e, end: n } of a(T)) e < t + r && await R(e, t + r), n > t + i && await R(t + i, n);
|
|
231
|
-
},
|
|
231
|
+
}, ee = () => {
|
|
232
232
|
let e = a(T);
|
|
233
233
|
if (!e.length) return !0;
|
|
234
234
|
let t = n.currentTime;
|
|
235
235
|
if (Math.max(...e.map((e) => e.end)) >= t + g) return !1;
|
|
236
236
|
let r = e.find((e) => e.start <= t + S && t < e.end);
|
|
237
237
|
return !r || r.end < t + _;
|
|
238
|
-
},
|
|
238
|
+
}, $ = () => {
|
|
239
239
|
let e = n.currentTime;
|
|
240
240
|
return a(T).find((t) => t.start <= e + S && e < t.end);
|
|
241
|
-
},
|
|
242
|
-
let e =
|
|
243
|
-
return !!e && e.end >=
|
|
244
|
-
},
|
|
241
|
+
}, te = () => {
|
|
242
|
+
let e = $();
|
|
243
|
+
return !!e && e.end >= J - .1;
|
|
244
|
+
}, ne = () => {
|
|
245
245
|
let e = a(T).map((e) => e.end);
|
|
246
|
-
e.length && (
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
let t = ++
|
|
246
|
+
e.length && (J = Math.max(J, ...e));
|
|
247
|
+
}, re = async (e) => {
|
|
248
|
+
H = !0, K = null, q = 0, X = e;
|
|
249
|
+
let t = ++G;
|
|
250
250
|
try {
|
|
251
251
|
let { data: n, pts: r, subtitles: i } = await N.seek(e);
|
|
252
|
-
if (F || t !==
|
|
253
|
-
n.byteLength && (await L(n),
|
|
252
|
+
if (F || t !== G || (i?.length && o.pushFragments(i), await z(r), F || t !== G)) return;
|
|
253
|
+
n.byteLength && (await L(n), ne());
|
|
254
254
|
} catch (e) {
|
|
255
|
-
|
|
255
|
+
Z(e, F || t !== G);
|
|
256
256
|
} finally {
|
|
257
|
-
t ===
|
|
257
|
+
t === G && (H = !1);
|
|
258
258
|
}
|
|
259
259
|
}, ie = async () => {
|
|
260
|
-
let e =
|
|
260
|
+
let e = K;
|
|
261
261
|
if (e) try {
|
|
262
|
-
await L(e),
|
|
262
|
+
await L(e), ne(), K === e && (K = null, q = 0);
|
|
263
263
|
} catch (t) {
|
|
264
|
-
if (
|
|
264
|
+
if (K !== e) return;
|
|
265
265
|
if (t?.name === "QuotaExceededError") {
|
|
266
|
-
let t = await
|
|
267
|
-
if (
|
|
266
|
+
let t = await Q(!0).then(() => !0, () => !1);
|
|
267
|
+
if (K !== e) return;
|
|
268
268
|
if (t) {
|
|
269
|
-
|
|
269
|
+
U = !1, await re($()?.end ?? n.currentTime);
|
|
270
270
|
return;
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
|
-
if (
|
|
274
|
-
throw
|
|
273
|
+
if (q += 1, q < b) return;
|
|
274
|
+
throw K = null, t;
|
|
275
275
|
}
|
|
276
276
|
}, ae = async () => {
|
|
277
|
-
if (
|
|
278
|
-
let e = ++
|
|
279
|
-
|
|
277
|
+
if (V || H || F || !K && (U || !ee())) return;
|
|
278
|
+
let e = ++W, t = G, n = () => F || e !== W || t !== G;
|
|
279
|
+
V = !0;
|
|
280
280
|
try {
|
|
281
|
-
if (!
|
|
281
|
+
if (!K) {
|
|
282
282
|
let { data: e, subtitles: t, finished: r } = await N.read();
|
|
283
|
-
if (n() || (r && (
|
|
284
|
-
|
|
283
|
+
if (n() || (r && (U = !0), t?.length && o.pushFragments(t), !e.byteLength)) return;
|
|
284
|
+
K = e, q = 0;
|
|
285
285
|
}
|
|
286
|
-
await ie(), !n() &&
|
|
286
|
+
await ie(), !n() && Y && (Y = !1, k?.());
|
|
287
287
|
} catch (e) {
|
|
288
|
-
|
|
288
|
+
Z(e, n());
|
|
289
289
|
} finally {
|
|
290
|
-
e ===
|
|
290
|
+
e === W && (V = !1);
|
|
291
291
|
}
|
|
292
|
-
}, oe = (e) => e >=
|
|
292
|
+
}, oe = (e) => e >= X && !!$(), se = () => {
|
|
293
293
|
let t = n.currentTime, r = e.info.input.duration || n.duration;
|
|
294
|
-
r > 0 && A?.(Math.min(Math.max(t / r, 0), 1)), !oe(t) && (
|
|
294
|
+
r > 0 && A?.(Math.min(Math.max(t / r, 0), 1)), !oe(t) && (U = !1, re(t));
|
|
295
295
|
};
|
|
296
296
|
n.addEventListener("seeking", se), P.push(() => n.removeEventListener("seeking", se));
|
|
297
297
|
let ce = setInterval(() => {
|
|
298
|
-
|
|
298
|
+
Q().catch(() => {}), ae().catch((e) => Z(e, !1)), U && !K && te() && C.readyState === "open" && B().catch(() => {});
|
|
299
299
|
}, 100);
|
|
300
300
|
return P.push(() => clearInterval(ce)), {
|
|
301
301
|
destroy: I,
|
|
@@ -383,88 +383,119 @@ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
|
|
|
383
383
|
} catch (e) {
|
|
384
384
|
throw w(c), e;
|
|
385
385
|
}
|
|
386
|
-
}, N = 1280, P =
|
|
387
|
-
let { video: t, canvas: n,
|
|
386
|
+
}, N = 1280, P = 1920, F = 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, B = () => L() ? R() ? "window" : z() ? "burn-in" : null : null, V = (e) => {
|
|
387
|
+
let { video: t, canvas: n } = e, r = e.mode ?? B() ?? "window", i = e.maxWidth ?? (r === "burn-in" ? P : N), 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) => {
|
|
388
388
|
r.drawImage(t, 0, 0, e.width, e.height), n.width > 0 && n.height > 0 && r.drawImage(n, 0, 0, e.width, e.height);
|
|
389
|
-
}, f = () => {
|
|
390
|
-
i && d(i.composite, i.context);
|
|
391
|
-
}, p = "requestVideoFrameCallback" in t, m = () => {
|
|
392
|
-
f(), h();
|
|
393
389
|
}, h = () => {
|
|
394
|
-
|
|
395
|
-
}, g = () => {
|
|
396
|
-
|
|
397
|
-
}, _ = () => {
|
|
398
|
-
"mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
|
|
390
|
+
a && m(a.composite, a.context);
|
|
391
|
+
}, g = "requestVideoFrameCallback" in t, _ = () => {
|
|
392
|
+
h(), v();
|
|
399
393
|
}, v = () => {
|
|
400
|
-
|
|
394
|
+
d || !a || (o = g ? t.requestVideoFrameCallback(_) : requestAnimationFrame(_));
|
|
401
395
|
}, y = () => {
|
|
402
|
-
|
|
403
|
-
s = !1;
|
|
404
|
-
}));
|
|
396
|
+
o !== void 0 && (g ? t.cancelVideoFrameCallback(o) : cancelAnimationFrame(o)), s !== void 0 && clearInterval(s), o = void 0, s = void 0;
|
|
405
397
|
}, b = () => {
|
|
406
|
-
|
|
398
|
+
"mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
|
|
407
399
|
}, x = () => {
|
|
408
|
-
|
|
409
|
-
}, S =
|
|
400
|
+
c || l || !t.paused || t.play().catch(() => {});
|
|
401
|
+
}, S = () => {
|
|
402
|
+
c || l || (t.paused || t.pause(), r !== "burn-in" && (c = !0, a?.mirror.play().catch(() => {}).finally(() => {
|
|
403
|
+
c = !1;
|
|
404
|
+
})));
|
|
405
|
+
}, C = () => {
|
|
406
|
+
b(), a?.mirror.play().catch(() => {});
|
|
407
|
+
}, w = () => {
|
|
408
|
+
b(), r === "burn-in" && a?.mirror.pause();
|
|
409
|
+
}, T = () => {
|
|
410
|
+
if (r !== "burn-in" || !a || !t.paused || l) return;
|
|
411
|
+
l = !0;
|
|
412
|
+
let { mirror: e } = a;
|
|
413
|
+
h(), e.play().then(() => new Promise((e) => {
|
|
414
|
+
setTimeout(e, I);
|
|
415
|
+
})).catch(() => {}).finally(() => {
|
|
416
|
+
e.pause(), setTimeout(() => {
|
|
417
|
+
l = !1;
|
|
418
|
+
}, I);
|
|
419
|
+
});
|
|
420
|
+
}, E = [["play", () => {
|
|
410
421
|
t.play().catch(() => {});
|
|
411
|
-
}], ["pause", () => t.pause()]],
|
|
412
|
-
if ("mediaSession" in navigator) for (let [t, n] of
|
|
422
|
+
}], ["pause", () => t.pause()]], D = (e) => {
|
|
423
|
+
if ("mediaSession" in navigator) for (let [t, n] of E) try {
|
|
413
424
|
navigator.mediaSession.setActionHandler(t, e ? n : null);
|
|
414
425
|
} catch {}
|
|
415
|
-
},
|
|
416
|
-
if (
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
}, O = () => {
|
|
427
|
+
if (y(), D(!1), t.removeEventListener("play", C), t.removeEventListener("pause", w), t.removeEventListener("seeked", T), !a) {
|
|
428
|
+
f?.();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
let { stream: e, mirror: n } = a;
|
|
432
|
+
n.removeEventListener("play", x), n.removeEventListener("pause", S), n.removeEventListener("leavepictureinpicture", k);
|
|
419
433
|
for (let t of e.getTracks()) t.stop();
|
|
420
|
-
n.remove(),
|
|
421
|
-
},
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
434
|
+
n.remove(), a = void 0, f?.();
|
|
435
|
+
}, k = () => O(), A = (e) => {
|
|
436
|
+
let r = t.style.opacity, i = n.style.display;
|
|
437
|
+
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 = () => {
|
|
438
|
+
f = void 0, t.style.opacity = r, n.style.display = i;
|
|
439
|
+
};
|
|
440
|
+
}, j = async () => {
|
|
441
|
+
let n = Math.min(t.videoWidth || i, i), o = Math.round(n * ((t.videoHeight || 9) / (t.videoWidth || 16))), c = document.createElement("canvas");
|
|
442
|
+
c.width = n, c.height = o;
|
|
443
|
+
let l = c.getContext("2d");
|
|
444
|
+
if (!l) throw Error("the picture in picture composite has no 2d context");
|
|
445
|
+
let u = document.createElement("video");
|
|
446
|
+
u.muted = !0, u.playsInline = !0, r === "burn-in" ? A(u) : u.style.cssText = "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;left:-1px;top:-1px", m(c, l);
|
|
447
|
+
let f = {
|
|
448
|
+
composite: c,
|
|
449
|
+
context: l,
|
|
450
|
+
stream: c.captureStream(),
|
|
451
|
+
mirror: u
|
|
452
|
+
};
|
|
453
|
+
if (a = f, u.srcObject = f.stream, (e.container ?? t.parentElement ?? document.body).appendChild(u), v(), s = setInterval(() => {
|
|
454
|
+
t.paused && h();
|
|
455
|
+
}, F), u.play().catch(() => {}), u.readyState === 0 && await new Promise((e) => {
|
|
437
456
|
let t = () => {
|
|
438
457
|
clearTimeout(n), e();
|
|
439
458
|
}, n = setTimeout(t, 1e3);
|
|
440
|
-
|
|
441
|
-
}),
|
|
459
|
+
u.addEventListener("loadedmetadata", t, { once: !0 });
|
|
460
|
+
}), d || a !== f) {
|
|
461
|
+
u.remove();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (u.addEventListener("play", x), u.addEventListener("pause", S), u.addEventListener("leavepictureinpicture", k), t.addEventListener("play", C), t.addEventListener("pause", w), t.addEventListener("seeked", T), D(!0), b(), r === "burn-in") {
|
|
465
|
+
t.paused && u.pause(), e.onBurnedInChange?.(!0);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
await u.requestPictureInPicture();
|
|
442
469
|
};
|
|
443
470
|
return {
|
|
444
471
|
toggle: async () => {
|
|
445
|
-
if (!(
|
|
446
|
-
|
|
447
|
-
await document.exitPictureInPicture().catch(() => {});
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (!F()) {
|
|
451
|
-
await E();
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
document.pictureInPictureElement && await document.exitPictureInPicture().catch(() => {}), c = !0;
|
|
472
|
+
if (!(d || u)) {
|
|
473
|
+
u = !0;
|
|
455
474
|
try {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
475
|
+
if (p()) {
|
|
476
|
+
if (r === "burn-in") {
|
|
477
|
+
O(), e.onBurnedInChange?.(!1);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
await document.exitPictureInPicture().catch(() => {});
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
r !== "burn-in" && document.pictureInPictureElement && await document.exitPictureInPicture().catch(() => {});
|
|
484
|
+
try {
|
|
485
|
+
await j();
|
|
486
|
+
} catch (t) {
|
|
487
|
+
console.warn("subtitle compositing for picture in picture failed", t), O(), r === "burn-in" && e.onBurnedInChange?.(!1);
|
|
488
|
+
}
|
|
459
489
|
} finally {
|
|
460
|
-
|
|
490
|
+
u = !1;
|
|
461
491
|
}
|
|
462
492
|
}
|
|
463
493
|
},
|
|
494
|
+
mode: r,
|
|
464
495
|
destroy: () => {
|
|
465
|
-
|
|
496
|
+
d = !0, r !== "burn-in" && p() && document.exitPictureInPicture().catch(() => {}), O(), r === "burn-in" && e.onBurnedInChange?.(!1);
|
|
466
497
|
}
|
|
467
498
|
};
|
|
468
499
|
};
|
|
469
500
|
//#endregion
|
|
470
|
-
export {
|
|
501
|
+
export { T as a, m as c, C as i, a as l, B as n, w as o, M as r, c as s, V as t, s as u };
|