@banou/media-player 0.6.2 → 0.8.0

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.
Files changed (123) hide show
  1. package/README.md +149 -3
  2. package/dist/engine/index.d.ts +10 -0
  3. package/dist/engine/index.js +2 -0
  4. package/dist/engine/picture-in-picture.d.ts +27 -0
  5. package/dist/engine/playback.d.ts +45 -0
  6. package/dist/engine/source-buffer.d.ts +12 -0
  7. package/dist/engine/subtitles.d.ts +36 -0
  8. package/dist/engine/thumbnails.d.ts +22 -0
  9. package/dist/engine-Dmc7JSd9.js +470 -0
  10. package/dist/index.d.ts +14 -0
  11. package/dist/index.js +1610 -0
  12. package/dist/react/components/chrome.d.ts +12 -0
  13. package/dist/react/components/control-bar.d.ts +2 -0
  14. package/dist/react/components/overlay.d.ts +4 -0
  15. package/dist/react/components/playback-slider.d.ts +2 -0
  16. package/dist/react/components/progress-bar.d.ts +2 -0
  17. package/dist/react/components/settings.d.ts +2 -0
  18. package/dist/react/components/sound.d.ts +7 -0
  19. package/{build → dist/react}/components/tooltip-display.d.ts +8 -8
  20. package/dist/react/components/volume-slider.d.ts +6 -0
  21. package/dist/react/hooks/use-drag-value.d.ts +23 -0
  22. package/dist/react/hooks/use-picture-in-picture.d.ts +2 -0
  23. package/dist/react/hooks/use-playback.d.ts +8 -0
  24. package/dist/react/hooks/use-thumbnails.d.ts +12 -0
  25. package/dist/react/media.d.ts +98 -0
  26. package/dist/react/player.d.ts +26 -0
  27. package/dist/react/source-feature.d.ts +106 -0
  28. package/dist/react/video-player.d.ts +83 -0
  29. package/dist/utils/source.d.ts +30 -0
  30. package/dist/utils/track-label.d.ts +31 -0
  31. package/dist/utils/volume-utils.d.ts +6 -0
  32. package/package.json +58 -37
  33. package/src/lib/engine/index.ts +14 -0
  34. package/src/lib/engine/picture-in-picture.ts +250 -0
  35. package/src/lib/engine/playback.ts +352 -0
  36. package/src/lib/engine/source-buffer.ts +61 -0
  37. package/src/lib/engine/subtitles.ts +210 -0
  38. package/src/lib/engine/thumbnails.ts +137 -0
  39. package/src/lib/globals.d.ts +8 -0
  40. package/src/lib/index.tsx +51 -0
  41. package/src/lib/react/components/chrome.tsx +130 -0
  42. package/src/{components → lib/react/components}/control-bar.tsx +74 -96
  43. package/src/lib/react/components/overlay.tsx +125 -0
  44. package/src/{components → lib/react/components}/playback-slider.tsx +30 -52
  45. package/src/lib/react/components/progress-bar.tsx +341 -0
  46. package/src/lib/react/components/settings.tsx +384 -0
  47. package/src/lib/react/components/sound.tsx +133 -0
  48. package/src/{components → lib/react/components}/tooltip-display.tsx +15 -12
  49. package/src/{components → lib/react/components}/volume-slider.tsx +32 -46
  50. package/src/lib/react/hooks/use-drag-value.ts +73 -0
  51. package/src/lib/react/hooks/use-picture-in-picture.ts +36 -0
  52. package/src/lib/react/hooks/use-playback.ts +146 -0
  53. package/src/lib/react/hooks/use-thumbnails.ts +78 -0
  54. package/src/lib/react/media.ts +124 -0
  55. package/src/lib/react/player.ts +20 -0
  56. package/src/lib/react/source-feature.ts +108 -0
  57. package/src/lib/react/video-player.tsx +223 -0
  58. package/src/lib/utils/fonts.ts +160 -0
  59. package/src/lib/utils/source.ts +60 -0
  60. package/src/lib/utils/track-label.ts +51 -0
  61. package/src/lib/utils/volume-utils.ts +13 -0
  62. package/build/components/chrome.d.ts +0 -7
  63. package/build/components/control-bar.d.ts +0 -6
  64. package/build/components/overlay.d.ts +0 -4
  65. package/build/components/playback-slider.d.ts +0 -2
  66. package/build/components/progress-bar.d.ts +0 -1
  67. package/build/components/settings.d.ts +0 -2
  68. package/build/components/sound.d.ts +0 -4
  69. package/build/components/volume-slider.d.ts +0 -6
  70. package/build/index.d.ts +0 -26
  71. package/build/index.js +0 -3416
  72. package/build/main.d.ts +0 -1
  73. package/build/state-machines/data-source.d.ts +0 -27
  74. package/build/state-machines/index.d.ts +0 -34038
  75. package/build/state-machines/media-properties.d.ts +0 -45
  76. package/build/state-machines/media-source.d.ts +0 -34
  77. package/build/state-machines/media.d.ts +0 -8558
  78. package/build/state-machines/subtitles.d.ts +0 -56
  79. package/build/state-machines/thumbnails.d.ts +0 -24
  80. package/build/state-machines/utils.d.ts +0 -26
  81. package/build/utils/actor-utils.d.ts +0 -2
  82. package/build/utils/context.d.ts +0 -14
  83. package/build/utils/index.d.ts +0 -4
  84. package/build/utils/languages.d.ts +0 -260
  85. package/build/utils/mp4box.d.ts +0 -61
  86. package/build/utils/use-local-storage.d.ts +0 -3
  87. package/build/utils/use-scrub.d.ts +0 -11
  88. package/build/utils/volume-utils.d.ts +0 -17
  89. package/build/utils/window-height.d.ts +0 -2
  90. package/src/components/chrome.tsx +0 -95
  91. package/src/components/overlay.tsx +0 -91
  92. package/src/components/progress-bar.tsx +0 -251
  93. package/src/components/settings.tsx +0 -322
  94. package/src/components/sound.tsx +0 -101
  95. package/src/index.tsx +0 -195
  96. package/src/main.tsx +0 -169
  97. package/src/state-machines/data-source.ts +0 -84
  98. package/src/state-machines/index.ts +0 -5
  99. package/src/state-machines/media-properties.ts +0 -82
  100. package/src/state-machines/media-source.ts +0 -129
  101. package/src/state-machines/media.ts +0 -255
  102. package/src/state-machines/subtitles.ts +0 -285
  103. package/src/state-machines/thumbnails.ts +0 -123
  104. package/src/state-machines/utils.ts +0 -94
  105. package/src/utils/actor-utils.ts +0 -19
  106. package/src/utils/context.ts +0 -23
  107. package/src/utils/fonts.ts +0 -160
  108. package/src/utils/index.ts +0 -229
  109. package/src/utils/languages.ts +0 -262
  110. package/src/utils/mp4box.ts +0 -74
  111. package/src/utils/use-local-storage.ts +0 -30
  112. package/src/utils/use-scrub.ts +0 -40
  113. package/src/utils/volume-utils.ts +0 -39
  114. package/src/utils/window-height.ts +0 -19
  115. package/src/vite-env.d.ts +0 -1
  116. package/tsconfig.json +0 -28
  117. package/tsconfig.node.json +0 -9
  118. /package/{build → dist}/utils/colors.d.ts +0 -0
  119. /package/{build → dist}/utils/fonts.d.ts +0 -0
  120. /package/{build → dist}/utils/time.d.ts +0 -0
  121. /package/src/{assets → lib/assets}/picture-in-picture.svg +0 -0
  122. /package/src/{utils → lib/utils}/colors.ts +0 -0
  123. /package/src/{utils → lib/utils}/time.ts +0 -0
@@ -0,0 +1,470 @@
1
+ import { makeRemuxer as e, makeThumbnailer as t } from "libav-wasm";
2
+ import n from "jassub";
3
+ import { parse as r, stringify as i } from "ass-compiler";
4
+ //#region src/lib/engine/source-buffer.ts
5
+ var a = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
6
+ index: n,
7
+ start: e.buffered.start(n),
8
+ end: e.buffered.end(n)
9
+ })), o = (e) => new Promise((t, n) => {
10
+ let r = () => {
11
+ e.removeEventListener("updateend", i), e.removeEventListener("abort", i), e.removeEventListener("error", a);
12
+ }, i = () => {
13
+ r(), t();
14
+ }, a = (e) => {
15
+ r(), n(Error("The browser rejected a media segment", { cause: e }));
16
+ };
17
+ e.addEventListener("updateend", i, { once: !0 }), e.addEventListener("abort", i, { once: !0 }), e.addEventListener("error", a, { once: !0 });
18
+ }), s = (e, t) => {
19
+ let n = Promise.resolve(), r = (e) => {
20
+ let t = n.then(e, e);
21
+ return n = t.catch(() => {}), t;
22
+ };
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))),
26
+ updateTimestampOffset: (t) => r(async () => {
27
+ e.timestampOffset = t;
28
+ }),
29
+ endOfStream: () => r(async () => {
30
+ if (!(t.readyState !== "open" || e.updating)) try {
31
+ t.endOfStream();
32
+ } catch {}
33
+ })
34
+ };
35
+ }, c = -1, l = (e) => new Date(e).toISOString().slice(11, 22), u = (e, t) => e.createStyle({
36
+ ...t,
37
+ treat_fontname_as_pattern: 0,
38
+ Blur: 0,
39
+ Justify: 0,
40
+ FontName: t.Fontname,
41
+ FontSize: Number(t.Fontsize),
42
+ PrimaryColour: Number(t.PrimaryColour),
43
+ SecondaryColour: Number(t.SecondaryColour),
44
+ OutlineColour: Number(t.OutlineColour),
45
+ BackColour: Number(t.BackColour),
46
+ Bold: Number(t.Bold),
47
+ Italic: Number(t.Italic),
48
+ Underline: Number(t.Underline),
49
+ StrikeOut: Number(t.StrikeOut),
50
+ ScaleX: Number(t.ScaleX),
51
+ ScaleY: Number(t.ScaleY),
52
+ Spacing: Number(t.Spacing),
53
+ Angle: Number(t.Angle),
54
+ BorderStyle: Number(t.BorderStyle),
55
+ Outline: Number(t.Outline),
56
+ Shadow: Number(t.Shadow),
57
+ Alignment: Number(t.Alignment),
58
+ MarginL: Number(t.MarginL),
59
+ MarginR: Number(t.MarginR),
60
+ MarginV: Number(t.MarginV),
61
+ Encoding: Number(t.Encoding)
62
+ }), d = (e) => {
63
+ let t = r(e);
64
+ return i({
65
+ ...t,
66
+ info: {
67
+ ...t.info,
68
+ ScaledBorderAndShadow: "no",
69
+ LayoutResX: "",
70
+ LayoutResY: ""
71
+ }
72
+ });
73
+ }, f = (e) => {
74
+ let t = e.content.match(/\r?\n\[Events\]\r?\nFormat: (.*)/)?.[0];
75
+ return t ? {
76
+ type: "header",
77
+ streamIndex: e.streamIndex,
78
+ content: e.content,
79
+ eventsContent: t,
80
+ parsed: r(e.content)
81
+ } : (console.warn(`subtitle stream ${e.streamIndex} has no Events format, ignoring the track`), null);
82
+ }, p = (e, t) => {
83
+ let [n, i] = t.content.split(","), a = Number(n), o = `Dialogue: ${i},${l(t.start)},${l(t.end)},${t.content.replace(`${a},${i},`, "")}`, s = r(`${e.eventsContent}\r\n${o}`).events.dialogue[0];
84
+ if (!s) throw Error("dialogue event is undefined");
85
+ return {
86
+ type: "dialogue",
87
+ streamIndex: t.streamIndex,
88
+ index: a,
89
+ assEvent: {
90
+ ...s,
91
+ Effect: s.Effect ?? "",
92
+ Text: s.Text.raw,
93
+ Duration: (s.End - s.Start) * 1e3,
94
+ Start: s.Start * 1e3,
95
+ End: s.End * 1e3,
96
+ ReadOrder: a,
97
+ _index: a
98
+ }
99
+ };
100
+ }, m = (e) => {
101
+ let { video: t, canvas: r, workerUrl: i, wasmUrl: a, legacyWasmUrl: o, defaultFontUrl: s } = e, c, l = [], m = /* @__PURE__ */ new Map(), h = [], g = /* @__PURE__ */ new Map(), _, v = e.onStreams, y = setInterval(() => {
102
+ c?.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
103
+ }, 100), b = () => c?.setRate(t.playbackRate), x = (e) => {
104
+ c = new n({
105
+ onDemandRender: !1,
106
+ video: t,
107
+ canvas: r,
108
+ subContent: d(e.content),
109
+ workerUrl: i,
110
+ modernWasmUrl: a,
111
+ ...o ? { wasmUrl: o } : {},
112
+ fonts: l.map(([, e]) => e),
113
+ availableFonts: {
114
+ ...Object.fromEntries(l),
115
+ ...s ? { "liberation sans": s } : {}
116
+ }
117
+ }), t.removeEventListener("ratechange", c._boundSetRate), t.addEventListener("ratechange", b);
118
+ for (let t of e.parsed.styles.style) u(c, t);
119
+ };
120
+ return {
121
+ pushAttachments: (e) => {
122
+ l = [...l, ...e.map((e) => [e.filename, new Uint8Array(e.data)])];
123
+ },
124
+ pushFragments: (e) => {
125
+ for (let t of e) if (t.type === "header") {
126
+ if (m.has(t.streamIndex)) continue;
127
+ let e = f(t);
128
+ if (!e) continue;
129
+ m.set(t.streamIndex, e), h.push({
130
+ streamIndex: t.streamIndex,
131
+ title: t.title,
132
+ language: t.language
133
+ }), v?.([...h]), _ === void 0 && (_ = t.streamIndex), c || x(e);
134
+ } else {
135
+ let e = m.get(t.streamIndex);
136
+ if (!e) continue;
137
+ let n = g.get(t.streamIndex);
138
+ n || (n = /* @__PURE__ */ new Map(), g.set(t.streamIndex, n));
139
+ let r = p(e, t);
140
+ if (n.has(r.index)) continue;
141
+ n.set(r.index, r), _ === t.streamIndex && c?.createEvent(r.assEvent);
142
+ }
143
+ },
144
+ selectStream: (e) => {
145
+ let n = e ?? -1;
146
+ if (n === _ || !c) return;
147
+ _ = n, c.freeTrack();
148
+ let r = m.get(n);
149
+ if (r) {
150
+ c.setTrack(d(r.content));
151
+ for (let e of r.parsed.styles.style) u(c, e);
152
+ for (let e of g.get(n)?.values() ?? []) c.createEvent(e.assEvent);
153
+ c.setCurrentTime(t.paused, t.currentTime, t.playbackRate);
154
+ }
155
+ },
156
+ getStreams: () => [...h],
157
+ getSelectedStream: () => _,
158
+ setOnStreams: (e) => {
159
+ v = e;
160
+ },
161
+ destroy: () => {
162
+ clearInterval(y), t.removeEventListener("ratechange", b), c?.destroy(), c = void 0;
163
+ }
164
+ };
165
+ }, h = -20, g = 60, _ = 30, v = -5, y = 20, b = 5, x = 15e3, S = 1, C = 25e5, w = (e) => {
166
+ let { worker: t } = e, n = setTimeout(() => t.terminate(), 2e3);
167
+ e.destroy().catch(() => {}).finally(() => {
168
+ clearTimeout(n), t.terminate();
169
+ });
170
+ }, T = async (t) => {
171
+ let { videoElement: n, canvasElement: r, read: i, length: o, publicPath: c, libavWorkerUrl: l, jassubWorkerUrl: u, jassubWasmUrl: d, jassubLegacyWasmUrl: f, defaultFontUrl: p, bufferSize: T = C, audioStreamIndex: E, onReady: D, onError: O, onRecovered: k, onSeek: A, onSubtitleStreams: j, onAudioStreams: M } = t, N = await e({
172
+ publicPath: c,
173
+ workerUrl: l,
174
+ workerOptions: { type: "module" },
175
+ bufferSize: T,
176
+ length: o,
177
+ audioStreamIndex: E,
178
+ read: i
179
+ }), P = [], F = !1, I = () => {
180
+ if (!F) {
181
+ F = !0;
182
+ for (let e of [...P].reverse()) try {
183
+ e();
184
+ } catch {}
185
+ }
186
+ };
187
+ P.push(() => w(N));
188
+ try {
189
+ let e = await N.init(), t = e.audioStreams ?? [], i = t.some((e) => e.streamIndex === E) ? E : t[0]?.streamIndex ?? -1;
190
+ M?.(t, i);
191
+ let o = m({
192
+ video: n,
193
+ canvas: r,
194
+ workerUrl: u,
195
+ wasmUrl: d,
196
+ legacyWasmUrl: f,
197
+ defaultFontUrl: p,
198
+ onStreams: j
199
+ });
200
+ if (P.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");
201
+ let c = [e.info.output.videoMimeType, e.info.output.audioMimeType].filter(Boolean).join(","), l = `video/mp4; codecs="${c}"`;
202
+ if (!MediaSource.isTypeSupported(l)) throw Error(`This browser cannot play the codecs in this file: ${c}`);
203
+ let C = new MediaSource(), w = URL.createObjectURL(C);
204
+ P.push(() => URL.revokeObjectURL(w)), n.src = w;
205
+ let T = await new Promise((t, n) => {
206
+ let r = setTimeout(() => {
207
+ i(), n(/* @__PURE__ */ Error("The MediaSource never opened"));
208
+ }, x), i = () => {
209
+ clearTimeout(r), C.removeEventListener("sourceopen", a), C.removeEventListener("sourceclose", o), C.removeEventListener("error", o);
210
+ }, a = () => {
211
+ try {
212
+ let n = C.addSourceBuffer(l);
213
+ n.mode = "segments";
214
+ let r = e.info.input.duration;
215
+ Number.isFinite(r) && r > 0 && (C.duration = r), i(), t(n);
216
+ } catch (e) {
217
+ i(), n(e);
218
+ }
219
+ }, o = () => {
220
+ i(), n(/* @__PURE__ */ Error("The MediaSource closed before a SourceBuffer could be created"));
221
+ };
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: ee, endOfStream: te } = s(T, C);
224
+ await L(e.data), e.subtitles?.length && o.pushFragments(e.subtitles), D?.();
225
+ let z = !1, B = !1, V = !1, H = 0, U = 0, W = null, G = 0, K = 0, q = !1, J = 0, Y = (e, t) => {
226
+ let n = e?.message === "Cancelled";
227
+ t && n || (console.error(e), !q && (q = !0, O?.(n ? Error("Reading the video file failed", { cause: e }) : e)));
228
+ }, X = async (e = !1) => {
229
+ let t = n.currentTime, r = e ? v : h, i = e ? y : g;
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
+ }, ne = () => {
232
+ let e = a(T);
233
+ if (!e.length) return !0;
234
+ let t = n.currentTime;
235
+ if (Math.max(...e.map((e) => e.end)) >= t + g) return !1;
236
+ let r = e.find((e) => e.start <= t + S && t < e.end);
237
+ return !r || r.end < t + _;
238
+ }, Z = () => {
239
+ let e = n.currentTime;
240
+ return a(T).find((t) => t.start <= e + S && e < t.end);
241
+ }, re = () => {
242
+ let e = Z();
243
+ return !!e && e.end >= K - .1;
244
+ }, Q = () => {
245
+ let e = a(T).map((e) => e.end);
246
+ e.length && (K = Math.max(K, ...e));
247
+ }, $ = async (e) => {
248
+ B = !0, W = null, G = 0, J = e;
249
+ let t = ++U;
250
+ try {
251
+ let { data: n, pts: r, subtitles: i } = await N.seek(e);
252
+ if (F || t !== U || (i?.length && o.pushFragments(i), await ee(r), F || t !== U)) return;
253
+ n.byteLength && (await L(n), Q());
254
+ } catch (e) {
255
+ Y(e, F || t !== U);
256
+ } finally {
257
+ t === U && (B = !1);
258
+ }
259
+ }, ie = async () => {
260
+ let e = W;
261
+ if (e) try {
262
+ await L(e), Q(), W === e && (W = null, G = 0);
263
+ } catch (t) {
264
+ if (W !== e) return;
265
+ if (t?.name === "QuotaExceededError") {
266
+ let t = await X(!0).then(() => !0, () => !1);
267
+ if (W !== e) return;
268
+ if (t) {
269
+ V = !1, await $(Z()?.end ?? n.currentTime);
270
+ return;
271
+ }
272
+ }
273
+ if (G += 1, G < b) return;
274
+ throw W = null, t;
275
+ }
276
+ }, ae = async () => {
277
+ if (z || B || F || !W && (V || !ne())) return;
278
+ let e = ++H, t = U, n = () => F || e !== H || t !== U;
279
+ z = !0;
280
+ try {
281
+ if (!W) {
282
+ let { data: e, subtitles: t, finished: r } = await N.read();
283
+ if (n() || (r && (V = !0), t?.length && o.pushFragments(t), !e.byteLength)) return;
284
+ W = e, G = 0;
285
+ }
286
+ await ie(), !n() && q && (q = !1, k?.());
287
+ } catch (e) {
288
+ Y(e, n());
289
+ } finally {
290
+ e === H && (z = !1);
291
+ }
292
+ }, oe = (e) => e >= J && !!Z(), se = () => {
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) && (V = !1, $(t));
295
+ };
296
+ n.addEventListener("seeking", se), P.push(() => n.removeEventListener("seeking", se));
297
+ let ce = setInterval(() => {
298
+ X().catch(() => {}), ae().catch((e) => Y(e, !1)), V && !W && re() && C.readyState === "open" && te().catch(() => {});
299
+ }, 100);
300
+ return P.push(() => clearInterval(ce)), {
301
+ destroy: I,
302
+ selectSubtitleStream: (e) => o.selectStream(e),
303
+ indexes: e.indexes ?? [],
304
+ duration: e.info.input.duration,
305
+ videoMimeType: e.info.output.videoMimeType,
306
+ audioMimeType: e.info.output.audioMimeType
307
+ };
308
+ } catch (e) {
309
+ throw I(), e;
310
+ }
311
+ }, E = 5, D = 500, O = 320, k = 1e6, A = 3, j = 1e4, M = async (e) => {
312
+ let { publicPath: n, workerUrl: r, length: i, read: a, onThumbnails: o, width: s = O } = e, c = await t({
313
+ publicPath: n,
314
+ workerUrl: r,
315
+ workerOptions: { type: "module" },
316
+ length: i,
317
+ read: a
318
+ });
319
+ try {
320
+ let t = await c.init(), n = t.duration, r = Math.max(e.interval ?? E, n / D), a = [];
321
+ for (let [e, o] of t.indexes.entries()) {
322
+ let s = a.at(-1);
323
+ s && o.timestamp - s.timestamp < r || a.push({
324
+ timestamp: o.timestamp,
325
+ endTime: n,
326
+ startByte: o.pos,
327
+ endByte: Math.min((t.indexes[e + 1]?.pos ?? i) + k, i),
328
+ done: !1,
329
+ attempts: 0
330
+ });
331
+ }
332
+ for (let [e, t] of a.entries()) t.endTime = a[e + 1]?.timestamp ?? n;
333
+ a.length > 1 && a.at(-1).timestamp > n - r * 2 && a.pop();
334
+ let l = [], u = !1, d = Promise.resolve(), f = () => {
335
+ let e = [];
336
+ for (let [t, n] of l.entries()) {
337
+ n.startTime - (e.at(-1)?.endTime ?? 0) > .01 && e.push({
338
+ url: "",
339
+ startTime: e.at(-1)?.endTime ?? 0,
340
+ endTime: n.startTime
341
+ }), e.push(n);
342
+ let r = l[t + 1];
343
+ r && r.startTime - n.endTime > .01 && e.push({
344
+ url: "",
345
+ startTime: n.endTime,
346
+ endTime: r.startTime
347
+ });
348
+ }
349
+ let t = e.at(-1)?.endTime ?? 0;
350
+ n - t > .01 && e.push({
351
+ url: "",
352
+ startTime: t,
353
+ endTime: n
354
+ }), o(e);
355
+ }, p = (e) => {
356
+ e.done = !0, d = d.then(async () => {
357
+ if (u) return;
358
+ let t = await Promise.race([c.readKeyframe(e.timestamp), new Promise((e, t) => setTimeout(() => t(/* @__PURE__ */ Error("timed out")), j))]), n = await createImageBitmap(new Blob([t], { type: "image/png" })), r = new OffscreenCanvas(s, Math.max(1, Math.round(n.height * (s / n.width))));
359
+ r.getContext("2d").drawImage(n, 0, 0, r.width, r.height), n.close();
360
+ let i = await r.convertToBlob({
361
+ type: "image/webp",
362
+ quality: .7
363
+ });
364
+ u || (l = [...l, {
365
+ url: URL.createObjectURL(i),
366
+ startTime: e.timestamp,
367
+ endTime: e.endTime
368
+ }].sort((e, t) => e.startTime - t.startTime), f());
369
+ }).catch(() => {
370
+ e.attempts += 1, e.done = e.attempts >= A;
371
+ });
372
+ };
373
+ return f(), {
374
+ update: (e) => {
375
+ if (!u) for (let t of a) t.done || (!e || e.some(([e, n]) => e <= t.startByte && t.endByte <= n)) && p(t);
376
+ },
377
+ destroy: () => {
378
+ u = !0;
379
+ for (let e of l) URL.revokeObjectURL(e.url);
380
+ l = [], w(c);
381
+ }
382
+ };
383
+ } catch (e) {
384
+ throw w(c), e;
385
+ }
386
+ }, N = 1280, P = 250, F = () => typeof HTMLCanvasElement < "u" && "captureStream" in HTMLCanvasElement.prototype && typeof document < "u" && document.pictureInPictureEnabled, I = (e) => {
387
+ let { video: t, canvas: n, maxWidth: r = N } = e, i, a, o, s = !1, c = !1, l = !1, u = () => !!i && document.pictureInPictureElement === i.mirror, d = (e, r) => {
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
+ }, h = () => {
394
+ l || !i || (a = p ? t.requestVideoFrameCallback(m) : requestAnimationFrame(m));
395
+ }, g = () => {
396
+ a !== void 0 && (p ? t.cancelVideoFrameCallback(a) : cancelAnimationFrame(a)), o !== void 0 && clearInterval(o), a = void 0, o = void 0;
397
+ }, _ = () => {
398
+ "mediaSession" in navigator && (navigator.mediaSession.playbackState = t.paused ? "paused" : "playing");
399
+ }, v = () => {
400
+ s || !t.paused || t.play().catch(() => {});
401
+ }, y = () => {
402
+ s || (t.paused || t.pause(), s = !0, i?.mirror.play().catch(() => {}).finally(() => {
403
+ s = !1;
404
+ }));
405
+ }, b = () => {
406
+ _(), i?.mirror.play().catch(() => {});
407
+ }, x = () => {
408
+ _();
409
+ }, S = [["play", () => {
410
+ t.play().catch(() => {});
411
+ }], ["pause", () => t.pause()]], C = (e) => {
412
+ if ("mediaSession" in navigator) for (let [t, n] of S) try {
413
+ navigator.mediaSession.setActionHandler(t, e ? n : null);
414
+ } catch {}
415
+ }, w = () => {
416
+ if (g(), C(!1), t.removeEventListener("play", b), t.removeEventListener("pause", x), !i) return;
417
+ let { stream: e, mirror: n } = i;
418
+ n.removeEventListener("play", v), n.removeEventListener("pause", y), n.removeEventListener("leavepictureinpicture", T);
419
+ for (let t of e.getTracks()) t.stop();
420
+ n.remove(), i = void 0;
421
+ }, T = () => w(), E = e.fallback ?? (async () => {
422
+ document.pictureInPictureElement === t ? await document.exitPictureInPicture() : await t.requestPictureInPicture();
423
+ }), D = async () => {
424
+ let n = Math.min(t.videoWidth || r, r), a = Math.round(n * ((t.videoHeight || 9) / (t.videoWidth || 16))), s = document.createElement("canvas");
425
+ s.width = n, s.height = a;
426
+ let c = s.getContext("2d");
427
+ if (!c) throw Error("the picture in picture composite has no 2d context");
428
+ let l = document.createElement("video");
429
+ l.muted = !0, l.playsInline = !0, l.style.cssText = "position:fixed;width:1px;height:1px;opacity:0;pointer-events:none;left:-1px;top:-1px", d(s, c), i = {
430
+ composite: s,
431
+ context: c,
432
+ stream: s.captureStream(),
433
+ mirror: l
434
+ }, l.srcObject = i.stream, (e.container ?? t.parentElement ?? document.body).appendChild(l), h(), o = setInterval(() => {
435
+ t.paused && f();
436
+ }, P), l.play().catch(() => {}), l.readyState === 0 && await new Promise((e) => {
437
+ let t = () => {
438
+ clearTimeout(n), e();
439
+ }, n = setTimeout(t, 1e3);
440
+ l.addEventListener("loadedmetadata", t, { once: !0 });
441
+ }), l.addEventListener("play", v), l.addEventListener("pause", y), l.addEventListener("leavepictureinpicture", T), t.addEventListener("play", b), t.addEventListener("pause", x), C(!0), _(), await l.requestPictureInPicture();
442
+ };
443
+ return {
444
+ toggle: async () => {
445
+ if (!(l || c)) {
446
+ if (u()) {
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;
455
+ try {
456
+ await D();
457
+ } catch (e) {
458
+ console.warn("subtitle compositing for picture in picture failed, falling back", e), w(), await E().catch(() => {});
459
+ } finally {
460
+ c = !1;
461
+ }
462
+ }
463
+ },
464
+ destroy: () => {
465
+ l = !0, u() && document.exitPictureInPicture().catch(() => {}), w();
466
+ }
467
+ };
468
+ };
469
+ //#endregion
470
+ export { w as a, a as c, T as i, s as l, M as n, c as o, C as r, m as s, I as t };
@@ -0,0 +1,14 @@
1
+ export { MediaPlayer, default } from './react/video-player';
2
+ export type { MediaPlayerOptions, MediaPlayerLocalOptions, MediaPlayerRemoteOptions, MediaPlayerSource, } from './react/video-player';
3
+ export { isDelegatedTracks, isExternalThumbnails } from './react/media';
4
+ export type { DelegatedSelection, DelegatedTracks, ExternalThumbnails, PlayerMedia, TimeRangesLike, } from './react/media';
5
+ export { Player, usePlayer } from './react/player';
6
+ export type { PlayerStore } from './react/player';
7
+ export { sourceFeature } from './react/source-feature';
8
+ export type { DownloadedRange, SourceState } from './react/source-feature';
9
+ export { useSeekThumbnails } from './react/hooks/use-thumbnails';
10
+ export { usePictureInPicture } from './react/hooks/use-picture-in-picture';
11
+ export { inputToRemuxerInput } from './utils/source';
12
+ export type { RemuxerInput } from './utils/source';
13
+ export type { AudioStream, MediaIndex, PictureInPictureController, PlaybackController, PlaybackOptions, SubtitleStream, ThumbnailImage, } from './engine';
14
+ export { startPlayback, createThumbnailGenerator, createSubtitleRenderer, createPictureInPicture, SUBTITLES_OFF, } from './engine';