@kodezen/player 1.0.2 → 1.0.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/README.md +21 -1
- package/dist/index.cjs +244 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +244 -19
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +248 -21
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +248 -21
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +100 -9
- package/dist/{types-TFltFgey.d.cts → types-30yBj4ll.d.cts} +20 -1
- package/dist/{types-TFltFgey.d.ts → types-30yBj4ll.d.ts} +20 -1
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -98,8 +98,7 @@ var Html5Provider = class {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
play() {
|
|
101
|
-
void this.video.play().catch(() =>
|
|
102
|
-
});
|
|
101
|
+
void this.video.play().catch(() => this.emitter.emit("pause"));
|
|
103
102
|
}
|
|
104
103
|
pause() {
|
|
105
104
|
this.video.pause();
|
|
@@ -381,8 +380,8 @@ var VimeoProvider = class {
|
|
|
381
380
|
this.emitter.emit("ready");
|
|
382
381
|
}
|
|
383
382
|
play() {
|
|
384
|
-
var _a, _b;
|
|
385
|
-
|
|
383
|
+
var _a, _b, _c, _d;
|
|
384
|
+
(_d = (_c = (_b = (_a = this.player) == null ? void 0 : _a.play) == null ? void 0 : _b.call(_a)) == null ? void 0 : _c.catch) == null ? void 0 : _d.call(_c, () => this.emitter.emit("pause"));
|
|
386
385
|
}
|
|
387
386
|
pause() {
|
|
388
387
|
var _a, _b;
|
|
@@ -452,13 +451,124 @@ function createProvider(source) {
|
|
|
452
451
|
}
|
|
453
452
|
}
|
|
454
453
|
|
|
454
|
+
// src/core/Captions.ts
|
|
455
|
+
var TIMING = /^\s*((?:\d+:)?\d{1,2}:\d{2}[.,]\d{1,3})\s+-->\s+((?:\d+:)?\d{1,2}:\d{2}[.,]\d{1,3})/;
|
|
456
|
+
var ENTITIES = {
|
|
457
|
+
amp: "&",
|
|
458
|
+
lt: "<",
|
|
459
|
+
gt: ">",
|
|
460
|
+
quot: '"',
|
|
461
|
+
apos: "'",
|
|
462
|
+
nbsp: "\xA0",
|
|
463
|
+
lrm: "\u200E",
|
|
464
|
+
rlm: "\u200F"
|
|
465
|
+
};
|
|
466
|
+
function toSeconds(stamp) {
|
|
467
|
+
const [clock = "", fraction = "0"] = stamp.replace(",", ".").split(".");
|
|
468
|
+
const seconds = clock.split(":").reduce((total, part) => total * 60 + Number(part), 0);
|
|
469
|
+
return seconds + Number(`0.${fraction}`);
|
|
470
|
+
}
|
|
471
|
+
function plainText(text) {
|
|
472
|
+
return text.replace(/<[^>]*>/g, "").replace(/&(amp|lt|gt|quot|apos|nbsp|lrm|rlm);/g, (_, name) => {
|
|
473
|
+
var _a;
|
|
474
|
+
return (_a = ENTITIES[name]) != null ? _a : "";
|
|
475
|
+
}).replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)));
|
|
476
|
+
}
|
|
477
|
+
function parseVtt(input) {
|
|
478
|
+
var _a;
|
|
479
|
+
const cues = [];
|
|
480
|
+
const blocks = input.replace(/^/, "").replace(/\r\n?/g, "\n").split(/\n{2,}/);
|
|
481
|
+
for (const block of blocks) {
|
|
482
|
+
const lines = block.split("\n");
|
|
483
|
+
const timingAt = lines.findIndex((line) => line.includes("-->"));
|
|
484
|
+
const timing = TIMING.exec((_a = lines[timingAt]) != null ? _a : "");
|
|
485
|
+
if (!(timing == null ? void 0 : timing[1]) || !timing[2]) continue;
|
|
486
|
+
const text = plainText(lines.slice(timingAt + 1).join("\n")).trim();
|
|
487
|
+
if (!text) continue;
|
|
488
|
+
cues.push({ start: toSeconds(timing[1]), end: toSeconds(timing[2]), text });
|
|
489
|
+
}
|
|
490
|
+
return cues.sort((a, b) => a.start - b.start);
|
|
491
|
+
}
|
|
492
|
+
var Captions = class {
|
|
493
|
+
/** `onChange` fires when the active track or track availability changes. */
|
|
494
|
+
constructor(tracks, onChange) {
|
|
495
|
+
this.onChange = onChange;
|
|
496
|
+
this.active = -1;
|
|
497
|
+
this.time = 0;
|
|
498
|
+
this.abort = new AbortController();
|
|
499
|
+
this.el = document.createElement("div");
|
|
500
|
+
this.el.className = "kzp-captions";
|
|
501
|
+
this.el.setAttribute("aria-live", "off");
|
|
502
|
+
this.text = document.createElement("span");
|
|
503
|
+
this.text.className = "kzp-captions-text";
|
|
504
|
+
this.text.hidden = true;
|
|
505
|
+
this.el.appendChild(this.text);
|
|
506
|
+
this.tracks = tracks.map((info) => ({ info, cues: null, failed: false }));
|
|
507
|
+
const preferred = tracks.findIndex((t) => t.default);
|
|
508
|
+
this.tracks.forEach((track, index) => {
|
|
509
|
+
void this.load(track).then(() => {
|
|
510
|
+
if (index === preferred && this.active === -1 && track.cues) this.select(index);
|
|
511
|
+
else this.onChange();
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
async load(track) {
|
|
516
|
+
try {
|
|
517
|
+
const res = await fetch(track.info.src, { signal: this.abort.signal, credentials: "same-origin" });
|
|
518
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
519
|
+
track.cues = parseVtt(await res.text());
|
|
520
|
+
} catch (err) {
|
|
521
|
+
if (this.abort.signal.aborted) return;
|
|
522
|
+
track.failed = true;
|
|
523
|
+
console.warn(`@kodezen/player: could not load captions from ${track.info.src}`, err);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/** Labels for the menu, one per track (falls back to the language, then a number). */
|
|
527
|
+
labels() {
|
|
528
|
+
return this.tracks.map((t, i) => t.info.label || t.info.srclang || `Track ${i + 1}`);
|
|
529
|
+
}
|
|
530
|
+
/** Whether each track loaded (a failed track is hidden from the controls). */
|
|
531
|
+
available() {
|
|
532
|
+
return this.tracks.map((t) => !t.failed);
|
|
533
|
+
}
|
|
534
|
+
getActive() {
|
|
535
|
+
return this.active;
|
|
536
|
+
}
|
|
537
|
+
/** Show track `index`, or turn captions off with -1. */
|
|
538
|
+
select(index) {
|
|
539
|
+
var _a;
|
|
540
|
+
const track = this.tracks[index];
|
|
541
|
+
this.active = track && !track.failed ? index : -1;
|
|
542
|
+
this.el.lang = (_a = track == null ? void 0 : track.info.srclang) != null ? _a : "";
|
|
543
|
+
this.render();
|
|
544
|
+
this.onChange();
|
|
545
|
+
}
|
|
546
|
+
/** Called with the playhead position on every tick and after a seek. */
|
|
547
|
+
update(time) {
|
|
548
|
+
this.time = time;
|
|
549
|
+
this.render();
|
|
550
|
+
}
|
|
551
|
+
render() {
|
|
552
|
+
var _a, _b;
|
|
553
|
+
const cues = (_b = (_a = this.tracks[this.active]) == null ? void 0 : _a.cues) != null ? _b : [];
|
|
554
|
+
const text = cues.filter((cue) => this.time >= cue.start && this.time < cue.end).map((cue) => cue.text).join("\n");
|
|
555
|
+
if (text !== this.text.textContent) this.text.textContent = text;
|
|
556
|
+
this.text.hidden = !text;
|
|
557
|
+
}
|
|
558
|
+
destroy() {
|
|
559
|
+
this.abort.abort();
|
|
560
|
+
this.el.remove();
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
455
564
|
// src/core/Controls.ts
|
|
456
565
|
var ICONS = {
|
|
457
566
|
play: '<path d="M8 5v14l11-7z"/>',
|
|
458
567
|
pause: '<path d="M6 5h4v14H6zM14 5h4v14h-4z"/>',
|
|
459
568
|
volume: '<path d="M3 10v4h4l5 5V5L7 10H3zm13.5 2a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z"/>',
|
|
460
569
|
muted: '<path d="M3 10v4h4l5 5V5L7 10H3zm13 2 3 3 1-1-3-3 3-3-1-1-3 3-3-3-1 1 3 3-3 3 1 1z"/>',
|
|
461
|
-
full: '<path d="M4 4h6v2H6v4H4V4zm10 0h6v6h-2V6h-4V4zM4 14h2v4h4v2H4v-6zm14 0h2v6h-6v-2h4v-4z"/>'
|
|
570
|
+
full: '<path d="M4 4h6v2H6v4H4V4zm10 0h6v6h-2V6h-4V4zM4 14h2v4h4v2H4v-6zm14 0h2v6h-6v-2h4v-4z"/>',
|
|
571
|
+
captions: '<path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1z"/>'
|
|
462
572
|
};
|
|
463
573
|
function icon(paths) {
|
|
464
574
|
return `<svg viewBox="0 0 24 24" aria-hidden="true">${paths}</svg>`;
|
|
@@ -478,20 +588,34 @@ var DEFAULT_CONFIG = {
|
|
|
478
588
|
time: true,
|
|
479
589
|
volume: true,
|
|
480
590
|
speed: true,
|
|
591
|
+
captions: true,
|
|
481
592
|
fullscreen: true
|
|
482
593
|
};
|
|
483
594
|
var Controls = class {
|
|
484
595
|
constructor(cb, opts) {
|
|
485
596
|
this.cb = cb;
|
|
597
|
+
this.ccActive = -1;
|
|
598
|
+
// Icons currently drawn, so update() only rewrites a button's SVG when it
|
|
599
|
+
// actually changes (see update()).
|
|
600
|
+
this.playIcon = "";
|
|
601
|
+
this.volIcon = "";
|
|
486
602
|
this.scrubbing = false;
|
|
603
|
+
this.rate = 0;
|
|
604
|
+
this.onOutsideClick = (e) => {
|
|
605
|
+
if (this.el.contains(e.target)) return;
|
|
606
|
+
if (this.speedMenu) this.speedMenu.hidden = true;
|
|
607
|
+
if (this.ccMenu) this.ccMenu.hidden = true;
|
|
608
|
+
};
|
|
487
609
|
this.seekableMax = Infinity;
|
|
610
|
+
var _a;
|
|
488
611
|
this.config = { ...DEFAULT_CONFIG, ...opts.config };
|
|
489
612
|
this.lockSeek = opts.lockSeek;
|
|
490
613
|
this.el = document.createElement("div");
|
|
491
614
|
this.el.className = "kzp-controls";
|
|
492
|
-
this.build(opts.speeds);
|
|
615
|
+
this.build(opts.speeds, (_a = opts.captionLabels) != null ? _a : []);
|
|
616
|
+
document.addEventListener("click", this.onOutsideClick);
|
|
493
617
|
}
|
|
494
|
-
build(speeds) {
|
|
618
|
+
build(speeds, captionLabels) {
|
|
495
619
|
if (this.config.playPause) {
|
|
496
620
|
this.playBtn = this.button("kzp-play", "Play", ICONS.play);
|
|
497
621
|
this.playBtn.addEventListener("click", () => this.cb.onToggle());
|
|
@@ -568,16 +692,72 @@ var Controls = class {
|
|
|
568
692
|
}
|
|
569
693
|
this.speedBtn.addEventListener("click", () => {
|
|
570
694
|
this.speedMenu.hidden = !this.speedMenu.hidden;
|
|
695
|
+
if (this.ccMenu) this.ccMenu.hidden = true;
|
|
571
696
|
});
|
|
572
697
|
wrap.append(this.speedBtn, this.speedMenu);
|
|
573
698
|
this.el.appendChild(wrap);
|
|
574
699
|
}
|
|
700
|
+
if (this.config.captions && captionLabels.length) {
|
|
701
|
+
this.buildCaptions(captionLabels);
|
|
702
|
+
}
|
|
575
703
|
if (this.config.fullscreen) {
|
|
576
704
|
const btn = this.button("kzp-full", "Fullscreen", ICONS.full);
|
|
577
705
|
btn.addEventListener("click", () => this.cb.onFullscreen());
|
|
578
706
|
this.el.appendChild(btn);
|
|
579
707
|
}
|
|
580
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* One track: the CC button toggles it. Several: it opens a menu (Off + each
|
|
711
|
+
* track), styled like the speed menu. Sits left of the speed control.
|
|
712
|
+
*/
|
|
713
|
+
buildCaptions(labels) {
|
|
714
|
+
const wrap = document.createElement("div");
|
|
715
|
+
wrap.className = "kzp-cc";
|
|
716
|
+
this.ccBtn = this.button("kzp-cc-btn", "Subtitles/closed captions", ICONS.captions);
|
|
717
|
+
this.ccBtn.setAttribute("aria-pressed", "false");
|
|
718
|
+
wrap.appendChild(this.ccBtn);
|
|
719
|
+
if (labels.length === 1) {
|
|
720
|
+
this.ccBtn.addEventListener("click", () => this.cb.onCaptions(this.ccActive === -1 ? 0 : -1));
|
|
721
|
+
} else {
|
|
722
|
+
this.ccMenu = document.createElement("div");
|
|
723
|
+
this.ccMenu.className = "kzp-speed-menu kzp-cc-menu";
|
|
724
|
+
this.ccMenu.hidden = true;
|
|
725
|
+
const items = [{ index: -1, label: "Off" }, ...labels.map((label, index) => ({ index, label }))];
|
|
726
|
+
for (const { index, label } of items) {
|
|
727
|
+
const item = document.createElement("button");
|
|
728
|
+
item.type = "button";
|
|
729
|
+
item.textContent = label;
|
|
730
|
+
item.dataset.track = String(index);
|
|
731
|
+
item.addEventListener("click", () => {
|
|
732
|
+
this.cb.onCaptions(index);
|
|
733
|
+
this.ccMenu.hidden = true;
|
|
734
|
+
});
|
|
735
|
+
this.ccMenu.appendChild(item);
|
|
736
|
+
}
|
|
737
|
+
this.ccBtn.addEventListener("click", () => {
|
|
738
|
+
this.ccMenu.hidden = !this.ccMenu.hidden;
|
|
739
|
+
if (this.speedMenu) this.speedMenu.hidden = true;
|
|
740
|
+
});
|
|
741
|
+
wrap.appendChild(this.ccMenu);
|
|
742
|
+
}
|
|
743
|
+
this.ccWrap = wrap;
|
|
744
|
+
const speed = this.el.querySelector(".kzp-speed");
|
|
745
|
+
this.el.insertBefore(wrap, speed);
|
|
746
|
+
}
|
|
747
|
+
/** Reflect the active caption track, and hide tracks (or the whole control) that failed to load. */
|
|
748
|
+
setCaptions(active, available) {
|
|
749
|
+
var _a;
|
|
750
|
+
this.ccActive = active;
|
|
751
|
+
if (!this.ccBtn || !this.ccWrap) return;
|
|
752
|
+
this.ccWrap.hidden = !available.some(Boolean);
|
|
753
|
+
this.ccBtn.classList.toggle("kzp-cc-btn--on", active !== -1);
|
|
754
|
+
this.ccBtn.setAttribute("aria-pressed", String(active !== -1));
|
|
755
|
+
(_a = this.ccMenu) == null ? void 0 : _a.querySelectorAll("button").forEach((item) => {
|
|
756
|
+
const index = Number(item.dataset.track);
|
|
757
|
+
item.hidden = index !== -1 && !available[index];
|
|
758
|
+
item.classList.toggle("kzp-menu-item--active", index === active);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
581
761
|
button(cls, label, svg) {
|
|
582
762
|
const b = document.createElement("button");
|
|
583
763
|
b.type = "button";
|
|
@@ -594,9 +774,14 @@ var Controls = class {
|
|
|
594
774
|
}
|
|
595
775
|
/** Push the latest state into the DOM. Called on every player timeupdate. */
|
|
596
776
|
update(state, extras) {
|
|
777
|
+
var _a;
|
|
597
778
|
if (this.playBtn) {
|
|
598
|
-
|
|
599
|
-
|
|
779
|
+
const next = state.playing ? ICONS.pause : ICONS.play;
|
|
780
|
+
if (next !== this.playIcon) {
|
|
781
|
+
this.playIcon = next;
|
|
782
|
+
this.playBtn.innerHTML = icon(next);
|
|
783
|
+
this.playBtn.setAttribute("aria-label", state.playing ? "Pause" : "Play");
|
|
784
|
+
}
|
|
600
785
|
}
|
|
601
786
|
if (this.scrubber) {
|
|
602
787
|
this.seekableMax = this.lockSeek ? state.furthest : Infinity;
|
|
@@ -607,14 +792,25 @@ var Controls = class {
|
|
|
607
792
|
this.timeEl.textContent = `${fmt(state.current)} / ${fmt(state.duration)}`;
|
|
608
793
|
}
|
|
609
794
|
if (this.volBtn) {
|
|
610
|
-
|
|
795
|
+
const next = extras.muted || extras.volume === 0 ? ICONS.muted : ICONS.volume;
|
|
796
|
+
if (next !== this.volIcon) {
|
|
797
|
+
this.volIcon = next;
|
|
798
|
+
this.volBtn.innerHTML = icon(next);
|
|
799
|
+
}
|
|
611
800
|
}
|
|
612
801
|
if (this.volRange && document.activeElement !== this.volRange) {
|
|
613
802
|
this.volRange.value = String(extras.muted ? 0 : extras.volume);
|
|
614
803
|
}
|
|
615
|
-
if (this.speedBtn
|
|
804
|
+
if (this.speedBtn && extras.rate !== this.rate) {
|
|
805
|
+
this.rate = extras.rate;
|
|
806
|
+
this.speedBtn.textContent = `${extras.rate}x`;
|
|
807
|
+
(_a = this.speedMenu) == null ? void 0 : _a.querySelectorAll("button").forEach((item) => {
|
|
808
|
+
item.classList.toggle("kzp-menu-item--active", Number(item.dataset.rate) === extras.rate);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
616
811
|
}
|
|
617
812
|
destroy() {
|
|
813
|
+
document.removeEventListener("click", this.onOutsideClick);
|
|
618
814
|
this.el.remove();
|
|
619
815
|
}
|
|
620
816
|
};
|
|
@@ -627,13 +823,14 @@ var Player = class {
|
|
|
627
823
|
this.container = container;
|
|
628
824
|
this.options = options;
|
|
629
825
|
this.emitter = new Emitter();
|
|
826
|
+
this.captions = null;
|
|
630
827
|
this.furthest = 0;
|
|
631
828
|
this.lastTick = 0;
|
|
632
829
|
this.duration = 0;
|
|
633
830
|
this.playing = false;
|
|
634
831
|
this.ready = false;
|
|
635
832
|
this.disposers = [];
|
|
636
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
833
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
637
834
|
const speeds = (_a = options.speeds) != null ? _a : DEFAULT_SPEEDS;
|
|
638
835
|
this.root = document.createElement("div");
|
|
639
836
|
this.root.className = "kzp" + (options.className ? ` ${options.className}` : "");
|
|
@@ -648,6 +845,10 @@ var Player = class {
|
|
|
648
845
|
this.stage.appendChild(hitLayer);
|
|
649
846
|
this.provider = createProvider(options.source);
|
|
650
847
|
const caps = this.provider.capabilities;
|
|
848
|
+
if ((_b = options.tracks) == null ? void 0 : _b.length) {
|
|
849
|
+
this.captions = new Captions(options.tracks, () => this.syncCaptions());
|
|
850
|
+
this.root.appendChild(this.captions.el);
|
|
851
|
+
}
|
|
651
852
|
this.controls = new Controls(
|
|
652
853
|
{
|
|
653
854
|
onToggle: () => this.toggle(),
|
|
@@ -665,17 +866,22 @@ var Player = class {
|
|
|
665
866
|
this.provider.setRate(r);
|
|
666
867
|
this.render();
|
|
667
868
|
},
|
|
869
|
+
onCaptions: (index) => {
|
|
870
|
+
var _a2;
|
|
871
|
+
return (_a2 = this.captions) == null ? void 0 : _a2.select(index);
|
|
872
|
+
},
|
|
668
873
|
onFullscreen: () => this.toggleFullscreen()
|
|
669
874
|
},
|
|
670
875
|
{
|
|
671
876
|
config: {
|
|
672
877
|
...options.controls,
|
|
673
|
-
seek: ((
|
|
674
|
-
speed: ((
|
|
675
|
-
volume: ((
|
|
878
|
+
seek: ((_d = (_c = options.controls) == null ? void 0 : _c.seek) != null ? _d : true) && caps.seekable,
|
|
879
|
+
speed: ((_f = (_e = options.controls) == null ? void 0 : _e.speed) != null ? _f : true) && caps.rate,
|
|
880
|
+
volume: ((_h = (_g = options.controls) == null ? void 0 : _g.volume) != null ? _h : true) && caps.volume
|
|
676
881
|
},
|
|
677
882
|
speeds,
|
|
678
|
-
lockSeek: !!options.lockSeek
|
|
883
|
+
lockSeek: !!options.lockSeek,
|
|
884
|
+
captionLabels: (_i = this.captions) == null ? void 0 : _i.labels()
|
|
679
885
|
}
|
|
680
886
|
);
|
|
681
887
|
this.root.appendChild(this.controls.el);
|
|
@@ -728,6 +934,7 @@ var Player = class {
|
|
|
728
934
|
this.render();
|
|
729
935
|
}
|
|
730
936
|
onTimeUpdate(current) {
|
|
937
|
+
var _a;
|
|
731
938
|
const advance = current - this.lastTick;
|
|
732
939
|
if (advance > 0 && advance <= NATURAL_ADVANCE_MAX && current > this.furthest) {
|
|
733
940
|
this.furthest = current;
|
|
@@ -735,9 +942,14 @@ var Player = class {
|
|
|
735
942
|
this.provider.seek(this.furthest);
|
|
736
943
|
}
|
|
737
944
|
this.lastTick = current;
|
|
945
|
+
(_a = this.captions) == null ? void 0 : _a.update(current);
|
|
738
946
|
this.emitter.emit("timeupdate", this.snapshot());
|
|
739
947
|
this.render();
|
|
740
948
|
}
|
|
949
|
+
syncCaptions() {
|
|
950
|
+
if (!this.captions) return;
|
|
951
|
+
this.controls.setCaptions(this.captions.getActive(), this.captions.available());
|
|
952
|
+
}
|
|
741
953
|
snapshot() {
|
|
742
954
|
const current = this.provider.getCurrentTime();
|
|
743
955
|
const duration = this.duration || this.provider.getDuration();
|
|
@@ -760,21 +972,32 @@ var Player = class {
|
|
|
760
972
|
}
|
|
761
973
|
}
|
|
762
974
|
// ---- Public API ----
|
|
975
|
+
// play()/pause() record the intent at once instead of waiting for the
|
|
976
|
+
// provider to confirm: YouTube only reports PLAYING after buffering, and
|
|
977
|
+
// until then a second click read "not playing" and called play() again
|
|
978
|
+
// (a no-op) instead of pausing. The provider's play/pause/ended events
|
|
979
|
+
// still correct it, e.g. when the browser blocks playback.
|
|
763
980
|
play() {
|
|
981
|
+
this.playing = true;
|
|
764
982
|
this.provider.play();
|
|
983
|
+
this.render();
|
|
765
984
|
}
|
|
766
985
|
pause() {
|
|
986
|
+
this.playing = false;
|
|
767
987
|
this.provider.pause();
|
|
988
|
+
this.render();
|
|
768
989
|
}
|
|
769
990
|
toggle() {
|
|
770
|
-
if (this.
|
|
771
|
-
else this.
|
|
991
|
+
if (this.playing) this.pause();
|
|
992
|
+
else this.play();
|
|
772
993
|
}
|
|
773
994
|
seek(seconds) {
|
|
995
|
+
var _a;
|
|
774
996
|
if (!this.provider.capabilities.seekable) return;
|
|
775
997
|
const target = this.options.lockSeek && seconds > this.furthest ? this.furthest : Math.max(0, seconds);
|
|
776
998
|
this.provider.seek(target);
|
|
777
999
|
this.lastTick = target;
|
|
1000
|
+
(_a = this.captions) == null ? void 0 : _a.update(target);
|
|
778
1001
|
this.render();
|
|
779
1002
|
}
|
|
780
1003
|
setLockSeek(locked) {
|
|
@@ -793,9 +1016,11 @@ var Player = class {
|
|
|
793
1016
|
this.emitter.off(event, cb);
|
|
794
1017
|
}
|
|
795
1018
|
destroy() {
|
|
1019
|
+
var _a;
|
|
796
1020
|
for (const dispose of this.disposers) dispose();
|
|
797
1021
|
this.disposers = [];
|
|
798
1022
|
this.provider.destroy();
|
|
1023
|
+
(_a = this.captions) == null ? void 0 : _a.destroy();
|
|
799
1024
|
this.controls.destroy();
|
|
800
1025
|
this.emitter.clear();
|
|
801
1026
|
this.root.remove();
|
|
@@ -808,7 +1033,7 @@ function createPlayer(container, options) {
|
|
|
808
1033
|
}
|
|
809
1034
|
var KodezenPlayer = react.forwardRef(
|
|
810
1035
|
function KodezenPlayer2(props, ref) {
|
|
811
|
-
var _a, _b;
|
|
1036
|
+
var _a, _b, _c;
|
|
812
1037
|
const hostRef = react.useRef(null);
|
|
813
1038
|
const playerRef = react.useRef(null);
|
|
814
1039
|
const propsRef = react.useRef(props);
|
|
@@ -858,7 +1083,8 @@ var KodezenPlayer = react.forwardRef(
|
|
|
858
1083
|
return (_a2 = playerRef.current) == null ? void 0 : _a2.destroy();
|
|
859
1084
|
}
|
|
860
1085
|
}));
|
|
861
|
-
const
|
|
1086
|
+
const trackKey = ((_a = props.tracks) != null ? _a : []).map((t) => t.src).join(",");
|
|
1087
|
+
const sourceKey = `${props.source.type}|${(_b = props.source.url) != null ? _b : ""}|${(_c = props.source.id) != null ? _c : ""}|${trackKey}`;
|
|
862
1088
|
react.useEffect(() => {
|
|
863
1089
|
if (!hostRef.current) return;
|
|
864
1090
|
const p = propsRef.current;
|
|
@@ -870,7 +1096,8 @@ var KodezenPlayer = react.forwardRef(
|
|
|
870
1096
|
muted: p.muted,
|
|
871
1097
|
speeds: p.speeds,
|
|
872
1098
|
controls: p.controls,
|
|
873
|
-
className: p.className
|
|
1099
|
+
className: p.className,
|
|
1100
|
+
tracks: p.tracks
|
|
874
1101
|
});
|
|
875
1102
|
playerRef.current = player;
|
|
876
1103
|
const offs = [
|