@sweet-player/core 0.2.1 → 0.4.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.
- package/README.md +29 -7
- package/dist/index.cjs +524 -144
- package/dist/index.d.cts +25 -4
- package/dist/index.d.ts +25 -4
- package/dist/index.js +523 -144
- package/dist/sweet-player.global.js +291 -28
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
SweetPlayer: () => SweetPlayer,
|
|
34
|
+
VERSION: () => VERSION,
|
|
34
35
|
registerLocale: () => registerLocale
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -62,6 +63,25 @@ var EventEmitter = class {
|
|
|
62
63
|
|
|
63
64
|
// src/core/media.ts
|
|
64
65
|
var import_hls = __toESM(require("hls.js"), 1);
|
|
66
|
+
|
|
67
|
+
// src/logger.ts
|
|
68
|
+
var MAX_ENTRIES = 2e3;
|
|
69
|
+
var entries = [];
|
|
70
|
+
function pad2(n) {
|
|
71
|
+
return n < 10 ? `0${n}` : String(n);
|
|
72
|
+
}
|
|
73
|
+
function formatTs(d) {
|
|
74
|
+
return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
75
|
+
}
|
|
76
|
+
function log(category, message) {
|
|
77
|
+
if (entries.length >= MAX_ENTRIES) entries.shift();
|
|
78
|
+
entries.push({ time: /* @__PURE__ */ new Date(), category, message });
|
|
79
|
+
}
|
|
80
|
+
function getLogText() {
|
|
81
|
+
return entries.map((e) => `[${formatTs(e.time)}] [${e.category}]\uFF1A${e.message}`).join("\n");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/core/media.ts
|
|
65
85
|
function isHlsSource(src) {
|
|
66
86
|
return /\.m3u8($|\?)/i.test(src);
|
|
67
87
|
}
|
|
@@ -78,10 +98,15 @@ var MediaController = class {
|
|
|
78
98
|
load(src) {
|
|
79
99
|
this.detachHls();
|
|
80
100
|
this.currentSrc = src;
|
|
101
|
+
log("\u64AD\u653E\u5668", `\u52A0\u8F7D\u6E90: ${src}`);
|
|
81
102
|
if (isHlsSource(src)) {
|
|
82
103
|
if (import_hls.default.isSupported()) {
|
|
83
104
|
this.hls = new import_hls.default(this.hlsConfig);
|
|
105
|
+
this.hls.on(import_hls.default.Events.MEDIA_ATTACHED, () => {
|
|
106
|
+
log("hls\u4E8B\u4EF6", "\u5A92\u4F53\u6210\u529F\u9644\u52A0\u5230\u64AD\u653E\u5668");
|
|
107
|
+
});
|
|
84
108
|
this.hls.on(import_hls.default.Events.ERROR, (_event, data) => {
|
|
109
|
+
log("hls\u4E8B\u4EF6", `${data.fatal ? "\u81F4\u547D" : ""}\u9519\u8BEF: ${data.type} - ${data.details}`);
|
|
85
110
|
if (data.fatal) {
|
|
86
111
|
switch (data.type) {
|
|
87
112
|
case import_hls.default.ErrorTypes.NETWORK_ERROR:
|
|
@@ -97,8 +122,17 @@ var MediaController = class {
|
|
|
97
122
|
}
|
|
98
123
|
});
|
|
99
124
|
this.hls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
|
|
125
|
+
log("hls\u4E8B\u4EF6", "\u64AD\u653E\u6E05\u5355\u89E3\u6790\u5B8C\u6BD5");
|
|
100
126
|
this.notifyTracks();
|
|
101
127
|
});
|
|
128
|
+
this.hls.on(import_hls.default.Events.BUFFER_CREATED, () => {
|
|
129
|
+
log("hls\u4E8B\u4EF6", "\u7F13\u51B2\u533A\u521B\u5EFA");
|
|
130
|
+
});
|
|
131
|
+
this.hls.on(import_hls.default.Events.LEVEL_SWITCHED, (_event, data) => {
|
|
132
|
+
const level = this.hls?.levels[data.level];
|
|
133
|
+
const label = level?.height ? `${level.height}P` : `Level ${data.level}`;
|
|
134
|
+
log("hls\u4E8B\u4EF6", `\u753B\u8D28\u6863\u4F4D\u5207\u6362: ${label}`);
|
|
135
|
+
});
|
|
102
136
|
this.hls.loadSource(src);
|
|
103
137
|
this.hls.attachMedia(this.video);
|
|
104
138
|
return;
|
|
@@ -461,69 +495,14 @@ var icons = {
|
|
|
461
495
|
),
|
|
462
496
|
audio: createSvgIcon(
|
|
463
497
|
"M12 3a9 9 0 0 0-9 9v7c0 1.1.9 2 2 2h4v-8H5v-1c0-3.87 3.13-7 7-7s7 3.13 7 7v1h-4v8h4c1.1 0 2-.9 2-2v-7a9 9 0 0 0-9-9z"
|
|
464
|
-
)
|
|
498
|
+
),
|
|
499
|
+
settings: createSvgIcon(
|
|
500
|
+
"M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65a.5.5 0 0 0 .12-.64l-2-3.46a.5.5 0 0 0-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.49.49 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1a.49.49 0 0 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65a.5.5 0 0 0-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46a.5.5 0 0 0-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"
|
|
501
|
+
),
|
|
502
|
+
chevronRight: createSvgIcon("M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"),
|
|
503
|
+
chevronLeft: createSvgIcon("M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z")
|
|
465
504
|
};
|
|
466
505
|
|
|
467
|
-
// src/ui/components/menu.ts
|
|
468
|
-
function createPopupMenu(opts) {
|
|
469
|
-
const root = createEl("div", { className: "sp-menu" });
|
|
470
|
-
const btn = createEl("button", {
|
|
471
|
-
className: "sp-btn sp-menu-btn",
|
|
472
|
-
html: opts.buttonHtml,
|
|
473
|
-
attrs: { type: "button", title: opts.title, "aria-label": opts.title },
|
|
474
|
-
parent: root
|
|
475
|
-
});
|
|
476
|
-
const panel = createEl("div", { className: "sp-menu-panel", parent: root });
|
|
477
|
-
let items = [];
|
|
478
|
-
let activeValue;
|
|
479
|
-
function render() {
|
|
480
|
-
panel.innerHTML = "";
|
|
481
|
-
if (items.length === 0) {
|
|
482
|
-
createEl("div", { className: "sp-menu-empty", text: opts.emptyText ?? "-", parent: panel });
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
for (const item of items) {
|
|
486
|
-
const itemBtn = createEl("button", {
|
|
487
|
-
className: "sp-menu-item" + (item.value === activeValue ? " sp-active" : ""),
|
|
488
|
-
text: item.label,
|
|
489
|
-
attrs: { type: "button" },
|
|
490
|
-
parent: panel
|
|
491
|
-
});
|
|
492
|
-
itemBtn.addEventListener("click", (e) => {
|
|
493
|
-
e.stopPropagation();
|
|
494
|
-
close();
|
|
495
|
-
opts.onSelect(item);
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function close() {
|
|
500
|
-
root.classList.remove("sp-open");
|
|
501
|
-
}
|
|
502
|
-
btn.addEventListener("click", (e) => {
|
|
503
|
-
e.stopPropagation();
|
|
504
|
-
const willOpen = !root.classList.contains("sp-open");
|
|
505
|
-
root.closest(".sp-controls")?.querySelectorAll(".sp-menu.sp-open").forEach((m) => m.classList.remove("sp-open"));
|
|
506
|
-
root.classList.toggle("sp-open", willOpen);
|
|
507
|
-
});
|
|
508
|
-
document.addEventListener("click", close);
|
|
509
|
-
render();
|
|
510
|
-
return {
|
|
511
|
-
el: root,
|
|
512
|
-
setItems(next) {
|
|
513
|
-
items = next;
|
|
514
|
-
render();
|
|
515
|
-
},
|
|
516
|
-
setActive(value) {
|
|
517
|
-
activeValue = value;
|
|
518
|
-
render();
|
|
519
|
-
},
|
|
520
|
-
setButtonContent(html) {
|
|
521
|
-
btn.innerHTML = html;
|
|
522
|
-
},
|
|
523
|
-
close
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
|
|
527
506
|
// src/ui/components/progressBar.ts
|
|
528
507
|
function createProgressBar(video, onSeek) {
|
|
529
508
|
const root = createEl("div", { className: "sp-progress" });
|
|
@@ -631,87 +610,299 @@ function createVolumeControl(opts) {
|
|
|
631
610
|
};
|
|
632
611
|
}
|
|
633
612
|
|
|
613
|
+
// src/ui/components/settingsPanel.ts
|
|
614
|
+
function createSettingsPanel(opts) {
|
|
615
|
+
const root = createEl("div", { className: "sp-menu sp-settings" });
|
|
616
|
+
const btn = createEl("button", {
|
|
617
|
+
className: "sp-btn",
|
|
618
|
+
html: icons.settings,
|
|
619
|
+
attrs: { type: "button", title: opts.buttonTitle, "aria-label": opts.buttonTitle },
|
|
620
|
+
parent: root
|
|
621
|
+
});
|
|
622
|
+
const panel = createEl("div", { className: "sp-settings-panel", parent: root });
|
|
623
|
+
const mainView = createEl("div", { className: "sp-settings-main", parent: panel });
|
|
624
|
+
const subView = createEl("div", { className: "sp-settings-sub", parent: panel });
|
|
625
|
+
let sections = opts.sections;
|
|
626
|
+
let activeSub = null;
|
|
627
|
+
function renderMain() {
|
|
628
|
+
mainView.innerHTML = "";
|
|
629
|
+
for (const sec of sections) {
|
|
630
|
+
if (sec.toggle) {
|
|
631
|
+
const row2 = createEl("div", {
|
|
632
|
+
className: "sp-settings-row sp-settings-toggle-row",
|
|
633
|
+
attrs: { "data-key": sec.key },
|
|
634
|
+
parent: mainView
|
|
635
|
+
});
|
|
636
|
+
createEl("span", { className: "sp-settings-label", text: sec.label, parent: row2 });
|
|
637
|
+
const track = createEl("div", {
|
|
638
|
+
className: "sp-toggle" + (sec.toggle.checked ? " sp-on" : ""),
|
|
639
|
+
attrs: { role: "switch", "aria-checked": String(sec.toggle.checked), tabindex: "0" },
|
|
640
|
+
parent: row2
|
|
641
|
+
});
|
|
642
|
+
createEl("span", { className: "sp-toggle-thumb", parent: track });
|
|
643
|
+
const handler = (e) => {
|
|
644
|
+
e.stopPropagation();
|
|
645
|
+
sec.toggle.onToggle();
|
|
646
|
+
};
|
|
647
|
+
row2.addEventListener("click", handler);
|
|
648
|
+
track.addEventListener("keydown", (e) => {
|
|
649
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
650
|
+
e.preventDefault();
|
|
651
|
+
handler(e);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (sec.items.length === 0) continue;
|
|
657
|
+
const row = createEl("button", {
|
|
658
|
+
className: "sp-settings-row",
|
|
659
|
+
attrs: { type: "button", "data-key": sec.key },
|
|
660
|
+
parent: mainView
|
|
661
|
+
});
|
|
662
|
+
createEl("span", { className: "sp-settings-label", text: sec.label, parent: row });
|
|
663
|
+
createEl("span", { className: "sp-settings-value", text: sec.currentValue, parent: row });
|
|
664
|
+
const arrow = createEl("span", { className: "sp-settings-arrow", parent: row });
|
|
665
|
+
arrow.innerHTML = icons.chevronRight;
|
|
666
|
+
row.addEventListener("click", (e) => {
|
|
667
|
+
e.stopPropagation();
|
|
668
|
+
showSub(sec.key);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function renderSub(key) {
|
|
673
|
+
subView.innerHTML = "";
|
|
674
|
+
const sec = sections.find((s) => s.key === key);
|
|
675
|
+
if (!sec) return;
|
|
676
|
+
const back = createEl("button", {
|
|
677
|
+
className: "sp-settings-back",
|
|
678
|
+
attrs: { type: "button" },
|
|
679
|
+
parent: subView
|
|
680
|
+
});
|
|
681
|
+
const backArrow = createEl("span", { className: "sp-settings-back-arrow", parent: back });
|
|
682
|
+
backArrow.innerHTML = icons.chevronLeft;
|
|
683
|
+
createEl("span", { text: sec.label, parent: back });
|
|
684
|
+
back.addEventListener("click", (e) => {
|
|
685
|
+
e.stopPropagation();
|
|
686
|
+
showMain();
|
|
687
|
+
});
|
|
688
|
+
for (const item of sec.items) {
|
|
689
|
+
const itemBtn = createEl("button", {
|
|
690
|
+
className: "sp-menu-item" + (item.value === sec.activeValue ? " sp-active" : ""),
|
|
691
|
+
text: item.label,
|
|
692
|
+
attrs: { type: "button" },
|
|
693
|
+
parent: subView
|
|
694
|
+
});
|
|
695
|
+
itemBtn.addEventListener("click", (e) => {
|
|
696
|
+
e.stopPropagation();
|
|
697
|
+
close();
|
|
698
|
+
sec.onSelect(item);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function showSub(key) {
|
|
703
|
+
activeSub = key;
|
|
704
|
+
renderSub(key);
|
|
705
|
+
mainView.style.display = "none";
|
|
706
|
+
subView.style.display = "";
|
|
707
|
+
}
|
|
708
|
+
function showMain() {
|
|
709
|
+
activeSub = null;
|
|
710
|
+
mainView.style.display = "";
|
|
711
|
+
subView.style.display = "none";
|
|
712
|
+
}
|
|
713
|
+
function close() {
|
|
714
|
+
root.classList.remove("sp-open");
|
|
715
|
+
showMain();
|
|
716
|
+
}
|
|
717
|
+
btn.addEventListener("click", (e) => {
|
|
718
|
+
e.stopPropagation();
|
|
719
|
+
const willOpen = !root.classList.contains("sp-open");
|
|
720
|
+
root.closest(".sp-controls")?.querySelectorAll(".sp-menu.sp-open").forEach((m) => m.classList.remove("sp-open"));
|
|
721
|
+
if (willOpen) {
|
|
722
|
+
renderMain();
|
|
723
|
+
showMain();
|
|
724
|
+
}
|
|
725
|
+
root.classList.toggle("sp-open", willOpen);
|
|
726
|
+
});
|
|
727
|
+
const onDocClick = () => close();
|
|
728
|
+
document.addEventListener("click", onDocClick);
|
|
729
|
+
panel.addEventListener("click", (e) => e.stopPropagation());
|
|
730
|
+
renderMain();
|
|
731
|
+
showMain();
|
|
732
|
+
return {
|
|
733
|
+
el: root,
|
|
734
|
+
updateSection(key, patch) {
|
|
735
|
+
const sec = sections.find((s) => s.key === key);
|
|
736
|
+
if (!sec) return;
|
|
737
|
+
if (patch.items !== void 0) sec.items = patch.items;
|
|
738
|
+
if (patch.currentValue !== void 0) sec.currentValue = patch.currentValue;
|
|
739
|
+
if (patch.activeValue !== void 0) sec.activeValue = patch.activeValue;
|
|
740
|
+
if (patch.toggle) {
|
|
741
|
+
if (sec.toggle) {
|
|
742
|
+
sec.toggle.checked = patch.toggle.checked;
|
|
743
|
+
if (patch.toggle.onToggle) sec.toggle.onToggle = patch.toggle.onToggle;
|
|
744
|
+
} else {
|
|
745
|
+
sec.toggle = { checked: patch.toggle.checked, onToggle: patch.toggle.onToggle ?? (() => {
|
|
746
|
+
}) };
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (sec.toggle) {
|
|
750
|
+
if (!activeSub) renderMain();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (activeSub === key) renderSub(key);
|
|
754
|
+
if (!activeSub) renderMain();
|
|
755
|
+
},
|
|
756
|
+
close,
|
|
757
|
+
destroy() {
|
|
758
|
+
document.removeEventListener("click", onDocClick);
|
|
759
|
+
root.remove();
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
634
764
|
// src/ui/controls.ts
|
|
635
765
|
function createControls(ctx) {
|
|
636
|
-
const { video, actions, i18n } = ctx;
|
|
766
|
+
const { video, actions, i18n, hidden } = ctx;
|
|
767
|
+
const show = (name) => !hidden.has(name);
|
|
637
768
|
const topEl = createEl("div", { className: "sp-top" });
|
|
638
|
-
|
|
639
|
-
titleEl.addEventListener("click", actions.onTitleClick);
|
|
769
|
+
if (show("title")) createEl("div", { className: "sp-title", text: ctx.title, parent: topEl });
|
|
640
770
|
const bottomEl = createEl("div", { className: "sp-bottom" });
|
|
641
771
|
const progress = createProgressBar(video, (t) => {
|
|
642
772
|
video.currentTime = t;
|
|
643
773
|
});
|
|
644
|
-
bottomEl.appendChild(progress.el);
|
|
774
|
+
if (show("progress")) bottomEl.appendChild(progress.el);
|
|
645
775
|
const row = createEl("div", { className: "sp-controls", parent: bottomEl });
|
|
646
|
-
const button = (html, title, onClick, disabled = false) => {
|
|
776
|
+
const button = (html, title, onClick, disabled = false, visible = true) => {
|
|
647
777
|
const btn = createEl("button", {
|
|
648
778
|
className: "sp-btn",
|
|
649
779
|
html,
|
|
650
780
|
attrs: { type: "button", title, "aria-label": title },
|
|
651
|
-
parent: row
|
|
781
|
+
parent: visible ? row : void 0
|
|
652
782
|
});
|
|
653
783
|
btn.disabled = disabled;
|
|
654
784
|
btn.addEventListener("click", onClick);
|
|
655
785
|
return btn;
|
|
656
786
|
};
|
|
657
|
-
button(icons.prev, i18n.t("prev"), () => actions.onPrev?.(), !actions.onPrev);
|
|
658
|
-
button(icons.seekBack, i18n.t("seekBack", { n: ctx.seekStep }), () => actions.seekBy(-ctx.seekStep));
|
|
659
|
-
const playBtn = button(icons.play, i18n.t("playPause"), actions.togglePlay);
|
|
660
|
-
button(icons.seekForward, i18n.t("seekForward", { n: ctx.seekStep }), () => actions.seekBy(ctx.seekStep));
|
|
661
|
-
button(icons.next, i18n.t("next"), () => actions.onNext?.(), !actions.onNext);
|
|
662
|
-
const timeEl = createEl("span", {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
title: i18n.t("speed"),
|
|
667
|
-
emptyText: i18n.t("empty"),
|
|
668
|
-
onSelect: (item) => actions.setRate(item.value)
|
|
787
|
+
button(icons.prev, i18n.t("prev"), () => actions.onPrev?.(), !actions.onPrev, show("prev"));
|
|
788
|
+
button(icons.seekBack, i18n.t("seekBack", { n: ctx.seekStep }), () => actions.seekBy(-ctx.seekStep), false, show("seekBack"));
|
|
789
|
+
const playBtn = button(icons.play, i18n.t("playPause"), actions.togglePlay, false, show("play"));
|
|
790
|
+
button(icons.seekForward, i18n.t("seekForward", { n: ctx.seekStep }), () => actions.seekBy(ctx.seekStep), false, show("seekForward"));
|
|
791
|
+
button(icons.next, i18n.t("next"), () => actions.onNext?.(), !actions.onNext, show("next"));
|
|
792
|
+
const timeEl = createEl("span", {
|
|
793
|
+
className: "sp-time",
|
|
794
|
+
text: "0:00 / 0:00",
|
|
795
|
+
parent: show("time") ? row : void 0
|
|
669
796
|
});
|
|
670
|
-
|
|
671
|
-
rateMenu.setActive(video.playbackRate);
|
|
672
|
-
row.appendChild(rateMenu.el);
|
|
673
|
-
const qualityMenu = createPopupMenu({
|
|
674
|
-
buttonHtml: i18n.t("quality"),
|
|
675
|
-
title: i18n.t("quality"),
|
|
676
|
-
emptyText: i18n.t("empty"),
|
|
677
|
-
onSelect: (item) => actions.selectQuality(item.value)
|
|
678
|
-
});
|
|
679
|
-
row.appendChild(qualityMenu.el);
|
|
680
|
-
const ratioMenu = createPopupMenu({
|
|
681
|
-
buttonHtml: i18n.t("aspectRatio"),
|
|
682
|
-
title: i18n.t("aspectRatio"),
|
|
683
|
-
emptyText: i18n.t("empty"),
|
|
684
|
-
onSelect: (item) => actions.setAspectRatio(item.value)
|
|
685
|
-
});
|
|
686
|
-
ratioMenu.setItems(
|
|
687
|
-
ctx.aspectRatios.map((r) => ({ label: r === "original" ? i18n.t("ratioOriginal") : r, value: r }))
|
|
688
|
-
);
|
|
689
|
-
ratioMenu.setActive("original");
|
|
690
|
-
row.appendChild(ratioMenu.el);
|
|
691
|
-
const audioMenu = createPopupMenu({
|
|
692
|
-
buttonHtml: icons.audio,
|
|
693
|
-
title: i18n.t("audioTrack"),
|
|
694
|
-
emptyText: i18n.t("empty"),
|
|
695
|
-
onSelect: (item) => actions.selectAudioTrack(item.value)
|
|
696
|
-
});
|
|
697
|
-
row.appendChild(audioMenu.el);
|
|
797
|
+
createEl("div", { className: "sp-controls-spacer", parent: row });
|
|
698
798
|
const volume = createVolumeControl({
|
|
699
799
|
muteTitle: i18n.t("mute"),
|
|
700
800
|
onVolumeChange: actions.setVolume,
|
|
701
801
|
onToggleMute: actions.toggleMute
|
|
702
802
|
});
|
|
703
|
-
row.appendChild(volume.el);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
803
|
+
if (show("volume")) row.appendChild(volume.el);
|
|
804
|
+
let currentRate = video.playbackRate;
|
|
805
|
+
let qualityItems = [];
|
|
806
|
+
let activeQuality;
|
|
807
|
+
let audioItems = [];
|
|
808
|
+
let activeAudio;
|
|
809
|
+
const settingsPanel = createSettingsPanel({
|
|
810
|
+
buttonTitle: i18n.t("settings"),
|
|
811
|
+
sections: [
|
|
812
|
+
...show("rate") ? [{
|
|
813
|
+
key: "rate",
|
|
814
|
+
label: i18n.t("speed"),
|
|
815
|
+
currentValue: `${currentRate}x`,
|
|
816
|
+
items: ctx.playbackRates.map((r) => ({ label: `${r}x`, value: r })),
|
|
817
|
+
activeValue: currentRate,
|
|
818
|
+
onSelect: (item) => actions.setRate(item.value)
|
|
819
|
+
}] : [],
|
|
820
|
+
...show("quality") ? [{
|
|
821
|
+
key: "quality",
|
|
822
|
+
label: i18n.t("quality"),
|
|
823
|
+
currentValue: i18n.t("qualityAuto"),
|
|
824
|
+
items: qualityItems,
|
|
825
|
+
activeValue: activeQuality,
|
|
826
|
+
onSelect: (item) => actions.selectQuality(item.value)
|
|
827
|
+
}] : [],
|
|
828
|
+
...show("ratio") ? [{
|
|
829
|
+
key: "ratio",
|
|
830
|
+
label: i18n.t("aspectRatio"),
|
|
831
|
+
currentValue: i18n.t("ratioOriginal"),
|
|
832
|
+
items: ctx.aspectRatios.map((r) => ({
|
|
833
|
+
label: r === "original" ? i18n.t("ratioOriginal") : r,
|
|
834
|
+
value: r
|
|
835
|
+
})),
|
|
836
|
+
activeValue: "original",
|
|
837
|
+
onSelect: (item) => actions.setAspectRatio(item.value)
|
|
838
|
+
}] : [],
|
|
839
|
+
...show("audioTrack") ? [{
|
|
840
|
+
key: "audioTrack",
|
|
841
|
+
label: i18n.t("audioTrack"),
|
|
842
|
+
currentValue: "-",
|
|
843
|
+
items: audioItems,
|
|
844
|
+
activeValue: activeAudio,
|
|
845
|
+
onSelect: (item) => actions.selectAudioTrack(item.value)
|
|
846
|
+
}] : [],
|
|
847
|
+
...show("pip") && document.pictureInPictureEnabled ? [{
|
|
848
|
+
key: "pip",
|
|
849
|
+
label: i18n.t("pip"),
|
|
850
|
+
currentValue: "",
|
|
851
|
+
items: [],
|
|
852
|
+
activeValue: void 0,
|
|
853
|
+
onSelect: () => {
|
|
854
|
+
},
|
|
855
|
+
toggle: {
|
|
856
|
+
checked: false,
|
|
857
|
+
onToggle: () => actions.togglePip()
|
|
858
|
+
}
|
|
859
|
+
}] : []
|
|
860
|
+
]
|
|
861
|
+
});
|
|
862
|
+
if (show("settings")) row.appendChild(settingsPanel.el);
|
|
863
|
+
const fsBtn = button(icons.fullscreen, i18n.t("fullscreen"), actions.toggleFullscreen, false, show("fullscreen"));
|
|
864
|
+
const qualityMenuAdapter = {
|
|
865
|
+
el: document.createElement("div"),
|
|
866
|
+
setItems(items) {
|
|
867
|
+
qualityItems = items;
|
|
868
|
+
const cur = activeQuality ? items.find((i) => i.value === activeQuality)?.label : i18n.t("qualityAuto");
|
|
869
|
+
settingsPanel.updateSection("quality", { items, currentValue: cur ?? i18n.t("qualityAuto") });
|
|
870
|
+
},
|
|
871
|
+
setActive(value) {
|
|
872
|
+
activeQuality = value;
|
|
873
|
+
const label = qualityItems.find((i) => i.value === value)?.label ?? i18n.t("qualityAuto");
|
|
874
|
+
settingsPanel.updateSection("quality", { activeValue: value, currentValue: label });
|
|
875
|
+
},
|
|
876
|
+
setButtonContent() {
|
|
877
|
+
},
|
|
878
|
+
close() {
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
const audioMenuAdapter = {
|
|
882
|
+
el: document.createElement("div"),
|
|
883
|
+
setItems(items) {
|
|
884
|
+
audioItems = items;
|
|
885
|
+
const cur = activeAudio ? items.find((i) => i.value === activeAudio)?.label : "-";
|
|
886
|
+
settingsPanel.updateSection("audioTrack", { items, currentValue: cur ?? "-" });
|
|
887
|
+
},
|
|
888
|
+
setActive(value) {
|
|
889
|
+
activeAudio = value;
|
|
890
|
+
const label = audioItems.find((i) => i.value === value)?.label ?? "-";
|
|
891
|
+
settingsPanel.updateSection("audioTrack", { activeValue: value, currentValue: label });
|
|
892
|
+
},
|
|
893
|
+
setButtonContent() {
|
|
894
|
+
},
|
|
895
|
+
close() {
|
|
896
|
+
}
|
|
897
|
+
};
|
|
708
898
|
return {
|
|
709
899
|
topEl,
|
|
710
900
|
bottomEl,
|
|
711
901
|
progress,
|
|
712
902
|
volume,
|
|
713
|
-
qualityMenu,
|
|
714
|
-
audioMenu,
|
|
903
|
+
qualityMenu: qualityMenuAdapter,
|
|
904
|
+
audioMenu: audioMenuAdapter,
|
|
905
|
+
settingsPanel,
|
|
715
906
|
updatePlayState(playing) {
|
|
716
907
|
playBtn.innerHTML = playing ? icons.pause : icons.play;
|
|
717
908
|
},
|
|
@@ -720,16 +911,21 @@ function createControls(ctx) {
|
|
|
720
911
|
progress.update();
|
|
721
912
|
},
|
|
722
913
|
updateRate(rate) {
|
|
723
|
-
|
|
724
|
-
|
|
914
|
+
currentRate = rate;
|
|
915
|
+
settingsPanel.updateSection("rate", { activeValue: rate, currentValue: `${rate}x` });
|
|
725
916
|
},
|
|
726
917
|
updateFullscreen(fs) {
|
|
727
918
|
fsBtn.innerHTML = fs ? icons.fullscreenExit : icons.fullscreen;
|
|
728
919
|
},
|
|
920
|
+
updatePip(pip) {
|
|
921
|
+
settingsPanel.updateSection("pip", { toggle: { checked: pip } });
|
|
922
|
+
},
|
|
729
923
|
updateRatio(ratio) {
|
|
730
|
-
|
|
924
|
+
const label = ratio === "original" ? i18n.t("ratioOriginal") : ratio;
|
|
925
|
+
settingsPanel.updateSection("ratio", { activeValue: ratio, currentValue: label });
|
|
731
926
|
},
|
|
732
927
|
destroy() {
|
|
928
|
+
settingsPanel.destroy();
|
|
733
929
|
topEl.remove();
|
|
734
930
|
bottomEl.remove();
|
|
735
931
|
}
|
|
@@ -762,6 +958,94 @@ function createOsd() {
|
|
|
762
958
|
};
|
|
763
959
|
}
|
|
764
960
|
|
|
961
|
+
// src/ui/components/contextMenu.ts
|
|
962
|
+
function createContextMenu(container, items) {
|
|
963
|
+
const menu = createEl("div", { className: "sp-context-menu", parent: container });
|
|
964
|
+
for (const item of items) {
|
|
965
|
+
const btn = createEl("button", {
|
|
966
|
+
className: "sp-context-item",
|
|
967
|
+
text: item.label,
|
|
968
|
+
attrs: { type: "button" },
|
|
969
|
+
parent: menu
|
|
970
|
+
});
|
|
971
|
+
btn.addEventListener("click", () => {
|
|
972
|
+
hide();
|
|
973
|
+
item.onClick();
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
function show(clientX, clientY) {
|
|
977
|
+
menu.classList.add("sp-visible");
|
|
978
|
+
const rect = container.getBoundingClientRect();
|
|
979
|
+
const x = Math.max(4, Math.min(clientX - rect.left, rect.width - menu.offsetWidth - 4));
|
|
980
|
+
const y = Math.max(4, Math.min(clientY - rect.top, rect.height - menu.offsetHeight - 4));
|
|
981
|
+
menu.style.left = `${x}px`;
|
|
982
|
+
menu.style.top = `${y}px`;
|
|
983
|
+
}
|
|
984
|
+
function hide() {
|
|
985
|
+
menu.classList.remove("sp-visible");
|
|
986
|
+
}
|
|
987
|
+
const onContextMenu = (e) => {
|
|
988
|
+
e.preventDefault();
|
|
989
|
+
show(e.clientX, e.clientY);
|
|
990
|
+
};
|
|
991
|
+
const onPointerDown = (e) => {
|
|
992
|
+
if (!menu.contains(e.target)) hide();
|
|
993
|
+
};
|
|
994
|
+
container.addEventListener("contextmenu", onContextMenu);
|
|
995
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
996
|
+
return {
|
|
997
|
+
hide,
|
|
998
|
+
destroy() {
|
|
999
|
+
container.removeEventListener("contextmenu", onContextMenu);
|
|
1000
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
1001
|
+
menu.remove();
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// src/ui/components/shortcutsOverlay.ts
|
|
1007
|
+
function createShortcutsOverlay(container, i18n, seekStep) {
|
|
1008
|
+
let root = null;
|
|
1009
|
+
function show() {
|
|
1010
|
+
if (root) return;
|
|
1011
|
+
root = createEl("div", { className: "sp-shortcuts", parent: container });
|
|
1012
|
+
const closeBtn = createEl("button", {
|
|
1013
|
+
className: "sp-shortcuts-close",
|
|
1014
|
+
text: "\u2715",
|
|
1015
|
+
attrs: { type: "button", "aria-label": i18n.t("close") },
|
|
1016
|
+
parent: root
|
|
1017
|
+
});
|
|
1018
|
+
closeBtn.addEventListener("click", hide);
|
|
1019
|
+
createEl("div", { className: "sp-shortcuts-title", text: i18n.t("shortcuts"), parent: root });
|
|
1020
|
+
const dl = createEl("dl", { className: "sp-shortcuts-body", parent: root });
|
|
1021
|
+
const rows = [
|
|
1022
|
+
[i18n.t("scKeySpace"), i18n.t("scPlayPause")],
|
|
1023
|
+
["\u2190 / \u2192", i18n.t("scSeek", { n: seekStep })],
|
|
1024
|
+
[i18n.t("scKeyHold"), i18n.t("scLongSeek")],
|
|
1025
|
+
["\u2191 / \u2193", i18n.t("scVolume")],
|
|
1026
|
+
["F", i18n.t("scFullscreen")],
|
|
1027
|
+
["M", i18n.t("scMute")]
|
|
1028
|
+
];
|
|
1029
|
+
for (const [key, action] of rows) {
|
|
1030
|
+
const row = createEl("div", { className: "sp-shortcuts-row", parent: dl });
|
|
1031
|
+
const dt = createEl("dt", { parent: row });
|
|
1032
|
+
createEl("span", { className: "sp-kbd", text: key, parent: dt });
|
|
1033
|
+
createEl("dd", { text: action, parent: row });
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function hide() {
|
|
1037
|
+
root?.remove();
|
|
1038
|
+
root = null;
|
|
1039
|
+
}
|
|
1040
|
+
return {
|
|
1041
|
+
toggle() {
|
|
1042
|
+
root ? hide() : show();
|
|
1043
|
+
},
|
|
1044
|
+
hide,
|
|
1045
|
+
destroy: hide
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
765
1049
|
// src/ui/components/statsOverlay.ts
|
|
766
1050
|
function createStatsOverlay(container, video, media, i18n) {
|
|
767
1051
|
let root = null;
|
|
@@ -800,11 +1084,31 @@ function createStatsOverlay(container, video, media, i18n) {
|
|
|
800
1084
|
function show() {
|
|
801
1085
|
if (root) return;
|
|
802
1086
|
root = createEl("div", { className: "sp-stats", parent: container });
|
|
1087
|
+
const header = createEl("div", { className: "sp-stats-header", parent: root });
|
|
1088
|
+
const copyBtn = createEl("button", {
|
|
1089
|
+
className: "sp-stats-copy",
|
|
1090
|
+
text: i18n.t("copyLog"),
|
|
1091
|
+
attrs: { type: "button" },
|
|
1092
|
+
parent: header
|
|
1093
|
+
});
|
|
1094
|
+
copyBtn.addEventListener("click", () => {
|
|
1095
|
+
const text = getLogText();
|
|
1096
|
+
void navigator.clipboard.writeText(text || "(empty)").then(
|
|
1097
|
+
() => {
|
|
1098
|
+
copyBtn.textContent = i18n.t("logCopied");
|
|
1099
|
+
setTimeout(() => {
|
|
1100
|
+
copyBtn.textContent = i18n.t("copyLog");
|
|
1101
|
+
}, 1500);
|
|
1102
|
+
},
|
|
1103
|
+
() => {
|
|
1104
|
+
}
|
|
1105
|
+
);
|
|
1106
|
+
});
|
|
803
1107
|
const closeBtn = createEl("button", {
|
|
804
1108
|
className: "sp-stats-close",
|
|
805
1109
|
text: "\u2715",
|
|
806
1110
|
attrs: { type: "button", "aria-label": i18n.t("closeStats") },
|
|
807
|
-
parent:
|
|
1111
|
+
parent: header
|
|
808
1112
|
});
|
|
809
1113
|
closeBtn.addEventListener("click", hide);
|
|
810
1114
|
createEl("dl", { className: "sp-stats-body", parent: root });
|
|
@@ -969,7 +1273,22 @@ var zhCN = {
|
|
|
969
1273
|
autoNextIn: "{n} \u79D2\u540E\u64AD\u653E\u4E0B\u4E00\u4E2A",
|
|
970
1274
|
cancel: "\u53D6\u6D88",
|
|
971
1275
|
loadError: "\u89C6\u9891\u52A0\u8F7D\u5931\u8D25",
|
|
972
|
-
retry: "\u91CD\u8BD5"
|
|
1276
|
+
retry: "\u91CD\u8BD5",
|
|
1277
|
+
changelog: "\u66F4\u65B0\u8BB0\u5F55",
|
|
1278
|
+
videoInfo: "\u89C6\u9891\u4FE1\u606F",
|
|
1279
|
+
shortcuts: "\u5FEB\u6377\u952E",
|
|
1280
|
+
close: "\u5173\u95ED",
|
|
1281
|
+
scKeySpace: "\u7A7A\u683C",
|
|
1282
|
+
scKeyHold: "\u2190 / \u2192 \u957F\u6309",
|
|
1283
|
+
scPlayPause: "\u64AD\u653E / \u6682\u505C",
|
|
1284
|
+
scSeek: "\u5FEB\u9000 / \u5FEB\u8FDB {n} \u79D2",
|
|
1285
|
+
scLongSeek: "\u9636\u68AF\u7D2F\u8BA1\u5FEB\u8FDB\u5FEB\u9000\uFF0C\u677E\u5F00\u6267\u884C",
|
|
1286
|
+
scVolume: "\u97F3\u91CF \xB15",
|
|
1287
|
+
scFullscreen: "\u5168\u5C4F\u5207\u6362",
|
|
1288
|
+
scMute: "\u9759\u97F3\u5207\u6362",
|
|
1289
|
+
settings: "\u8BBE\u7F6E",
|
|
1290
|
+
copyLog: "\u590D\u5236\u65E5\u5FD7",
|
|
1291
|
+
logCopied: "\u5DF2\u590D\u5236"
|
|
973
1292
|
};
|
|
974
1293
|
var en = {
|
|
975
1294
|
play: "Play",
|
|
@@ -998,7 +1317,22 @@ var en = {
|
|
|
998
1317
|
autoNextIn: "Playing next in {n}s",
|
|
999
1318
|
cancel: "Cancel",
|
|
1000
1319
|
loadError: "Failed to load video",
|
|
1001
|
-
retry: "Retry"
|
|
1320
|
+
retry: "Retry",
|
|
1321
|
+
changelog: "Changelog",
|
|
1322
|
+
videoInfo: "Video info",
|
|
1323
|
+
shortcuts: "Shortcuts",
|
|
1324
|
+
close: "Close",
|
|
1325
|
+
scKeySpace: "Space",
|
|
1326
|
+
scKeyHold: "Hold \u2190 / \u2192",
|
|
1327
|
+
scPlayPause: "Play / Pause",
|
|
1328
|
+
scSeek: "Rewind / forward {n}s",
|
|
1329
|
+
scLongSeek: "Accelerating seek, release to apply",
|
|
1330
|
+
scVolume: "Volume \xB15",
|
|
1331
|
+
scFullscreen: "Toggle fullscreen",
|
|
1332
|
+
scMute: "Toggle mute",
|
|
1333
|
+
settings: "Settings",
|
|
1334
|
+
copyLog: "Copy log",
|
|
1335
|
+
logCopied: "Copied"
|
|
1002
1336
|
};
|
|
1003
1337
|
var locales = { "zh-CN": zhCN, en };
|
|
1004
1338
|
var I18n = class {
|
|
@@ -1071,8 +1405,11 @@ function clearProgress(id) {
|
|
|
1071
1405
|
}
|
|
1072
1406
|
}
|
|
1073
1407
|
|
|
1408
|
+
// src/version.ts
|
|
1409
|
+
var VERSION = true ? "0.4.0" : "dev";
|
|
1410
|
+
|
|
1074
1411
|
// src/styles/player.css
|
|
1075
|
-
var player_default = ".sweet-player {\n --sp-accent: #ff4d6d;\n --sp-bg: #000;\n --sp-text: #fff;\n --sp-bar-height: 4px;\n --sp-bar-hover-height: 6px;\n --sp-control-size: 36px;\n --sp-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--sp-bg);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 13px;\n overflow: hidden;\n user-select: none;\n -webkit-user-select: none;\n outline: none;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sweet-player *,\n.sweet-player *::before,\n.sweet-player *::after {\n box-sizing: border-box;\n}\n\n.sp-video {\n width: 100%;\n height: 100%;\n object-fit: contain;\n display: block;\n}\n\n/* \u5F3A\u5236\u753B\u9762\u6BD4\u4F8B\uFF1Avideo \u5C45\u4E2D\u3001\u6309\u6BD4\u4F8B\u9650\u5236\u5C3A\u5BF8\u5E76\u62C9\u4F38\u586B\u5145 */\n.sweet-player[data-ratio]:not([data-ratio='original']) .sp-video {\n width: auto;\n height: auto;\n max-width: 100%;\n max-height: 100%;\n aspect-ratio: var(--sp-forced-ratio);\n object-fit: fill;\n}\n\n/* ---------- \u9876\u90E8\u6807\u9898\u533A ---------- */\n.sp-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 12px 16px 28px;\n background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);\n transition: opacity 0.25s ease;\n pointer-events: none;\n}\n\n.sp-title {\n display: inline-block;\n max-width: 70%;\n font-size: 15px;\n font-weight: 500;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: auto;\n cursor: default;\n}\n\n/* ---------- \u7EDF\u8BA1\u4FE1\u606F\u8499\u5C42 ---------- */\n.sp-stats {\n position: absolute;\n top: 12px;\n left: 12px;\n z-index: 30;\n min-width: 300px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 6px;\n font-size: 12px;\n line-height: 1.7;\n font-family: 'SFMono-Regular', Consolas, monospace;\n pointer-events: auto;\n}\n\n.sp-stats-close {\n position: absolute;\n top: 6px;\n right: 6px;\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n}\n\n.sp-stats-close:hover {\n opacity: 1;\n}\n\n.sp-stats-row {\n display: flex;\n gap: 12px;\n}\n\n.sp-stats-row dt {\n flex: 0 0 110px;\n opacity: 0.65;\n margin: 0;\n}\n\n.sp-stats-row dd {\n margin: 0;\n word-break: break-all;\n}\n\n/* ---------- \u4E2D\u592E OSD \u53CD\u9988 ---------- */\n.sp-osd {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 20;\n padding: 10px 18px;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n opacity: 0;\n transition: opacity 0.15s ease;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-osd.sp-visible {\n opacity: 1;\n}\n\n/* ---------- \u5E95\u90E8\u63A7\u5236\u533A ---------- */\n.sp-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 24px 12px 6px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);\n transition: opacity 0.25s ease;\n z-index: 10;\n}\n\n.sweet-player.sp-controls-hidden .sp-bottom,\n.sweet-player.sp-controls-hidden .sp-top {\n opacity: 0;\n pointer-events: none;\n}\n\n.sweet-player.sp-controls-hidden {\n cursor: none;\n}\n\n/* ---------- \u8FDB\u5EA6\u6761 ---------- */\n.sp-progress {\n position: relative;\n height: 14px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-bottom: 4px;\n}\n\n.sp-progress-track {\n position: relative;\n width: 100%;\n height: var(--sp-bar-height);\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n overflow: visible;\n transition: height 0.1s ease;\n}\n\n.sp-progress:hover .sp-progress-track {\n height: var(--sp-bar-hover-height);\n}\n\n.sp-progress-buffered,\n.sp-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n}\n\n.sp-progress-buffered {\n background: rgba(255, 255, 255, 0.35);\n}\n\n.sp-progress-played {\n background: var(--sp-accent);\n}\n\n.sp-progress-thumb {\n position: absolute;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--sp-accent);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.1s ease;\n pointer-events: none;\n}\n\n.sp-progress:hover .sp-progress-thumb,\n.sp-progress.sp-dragging .sp-progress-thumb {\n transform: translate(-50%, -50%) scale(1);\n}\n\n.sp-progress-tooltip {\n position: absolute;\n bottom: 18px;\n transform: translateX(-50%);\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 3px;\n font-size: 12px;\n display: none;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-progress:hover .sp-progress-tooltip {\n display: block;\n}\n\n/* ---------- \u63A7\u5236\u6309\u94AE\u884C ---------- */\n.sp-controls {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.sp-controls-spacer {\n flex: 1;\n}\n\n.sp-btn {\n width: var(--sp-control-size);\n height: var(--sp-control-size);\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0;\n opacity: 0.9;\n transition: opacity 0.1s ease, background 0.1s ease;\n}\n\n.sp-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.sp-btn:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.sp-time {\n padding: 0 8px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n opacity: 0.95;\n}\n\n/* ---------- \u5F39\u51FA\u83DC\u5355 ---------- */\n.sp-menu {\n position: relative;\n}\n\n.sp-menu-btn {\n min-width: var(--sp-control-size);\n width: auto;\n padding: 0 8px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.sp-menu-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n min-width: 80px;\n max-height: 220px;\n overflow-y: auto;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-menu.sp-open .sp-menu-panel {\n display: block;\n}\n\n.sp-menu-item {\n display: block;\n width: 100%;\n padding: 7px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: center;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-menu-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-menu-item.sp-active {\n color: var(--sp-accent);\n font-weight: 600;\n}\n\n.sp-menu-empty {\n padding: 7px 16px;\n opacity: 0.5;\n white-space: nowrap;\n}\n\n/* ---------- \u72B6\u6001\u8499\u5C42\uFF1Aloading / \u9519\u8BEF / \u7ED3\u675F ---------- */\n.sp-state {\n position: absolute;\n inset: 0;\n z-index: 15;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.sp-spinner {\n opacity: 0;\n transition: opacity 0.2s ease 0.3s; /* \u5EF6\u8FDF\u51FA\u73B0\uFF0C\u907F\u514D\u77ED\u6682 seek \u95EA\u70C1 */\n}\n\n.sp-state.sp-loading .sp-spinner {\n opacity: 1;\n}\n\n.sp-spinner-ring {\n width: 44px;\n height: 44px;\n border: 4px solid rgba(255, 255, 255, 0.25);\n border-top-color: var(--sp-accent);\n border-radius: 50%;\n animation: sp-spin 0.8s linear infinite;\n}\n\n@keyframes sp-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sp-state-panel {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 14px;\n background: rgba(0, 0, 0, 0.55);\n pointer-events: auto;\n}\n\n.sp-state-message {\n font-size: 15px;\n opacity: 0.95;\n}\n\n.sp-state-actions {\n display: flex;\n gap: 12px;\n}\n\n.sp-state-btn {\n padding: 8px 22px;\n border: 1px solid rgba(255, 255, 255, 0.5);\n border-radius: 18px;\n background: transparent;\n color: var(--sp-text);\n font-size: 14px;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.sp-state-btn:hover {\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-state-btn-primary {\n background: var(--sp-accent);\n border-color: var(--sp-accent);\n}\n\n.sp-state-btn-primary:hover {\n background: var(--sp-accent);\n filter: brightness(1.1);\n}\n\n.sp-state-countdown {\n font-size: 13px;\n opacity: 0.85;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.sp-state-cancel {\n border: none;\n background: transparent;\n color: var(--sp-accent);\n cursor: pointer;\n font-size: 13px;\n padding: 2px 6px;\n}\n\n/* ---------- \u97F3\u91CF ---------- */\n.sp-volume {\n display: flex;\n align-items: center;\n}\n\n.sp-volume-slider {\n width: 0;\n overflow: hidden;\n transition: width 0.15s ease;\n display: flex;\n align-items: center;\n}\n\n.sp-volume:hover .sp-volume-slider,\n.sp-volume.sp-dragging .sp-volume-slider {\n width: 64px;\n}\n\n.sp-volume-track {\n position: relative;\n width: 56px;\n height: var(--sp-bar-height);\n margin: 0 4px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.sp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-text);\n border-radius: 2px;\n}\n\n.sp-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--sp-text);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n";
|
|
1412
|
+
var player_default = ".sweet-player {\n --sp-accent: #ff4d6d;\n --sp-bg: #000;\n --sp-text: #fff;\n --sp-bar-height: 4px;\n --sp-bar-hover-height: 6px;\n --sp-control-size: 36px;\n --sp-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n\n position: relative;\n width: 100%;\n height: 100%;\n background: var(--sp-bg);\n color: var(--sp-text);\n font-family: var(--sp-font);\n font-size: 13px;\n overflow: hidden;\n user-select: none;\n -webkit-user-select: none;\n outline: none;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.sweet-player *,\n.sweet-player *::before,\n.sweet-player *::after {\n box-sizing: border-box;\n}\n\n.sp-video {\n width: 100%;\n height: 100%;\n object-fit: contain;\n display: block;\n}\n\n/* \u5F3A\u5236\u753B\u9762\u6BD4\u4F8B\uFF1Avideo \u5C45\u4E2D\u3001\u6309\u6BD4\u4F8B\u9650\u5236\u5C3A\u5BF8\u5E76\u62C9\u4F38\u586B\u5145 */\n.sweet-player[data-ratio]:not([data-ratio='original']) .sp-video {\n width: auto;\n height: auto;\n max-width: 100%;\n max-height: 100%;\n aspect-ratio: var(--sp-forced-ratio);\n object-fit: fill;\n}\n\n/* ---------- \u9876\u90E8\u6807\u9898\u533A ---------- */\n.sp-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: 12px 16px 28px;\n background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);\n transition: opacity 0.25s ease;\n pointer-events: none;\n}\n\n.sp-title {\n display: inline-block;\n max-width: 70%;\n font-size: 15px;\n font-weight: 500;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: auto;\n cursor: default;\n}\n\n/* ---------- \u7EDF\u8BA1\u4FE1\u606F\u8499\u5C42 ---------- */\n.sp-stats {\n position: absolute;\n top: 12px;\n left: 12px;\n z-index: 30;\n min-width: 300px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 6px;\n font-size: 12px;\n line-height: 1.7;\n font-family: 'SFMono-Regular', Consolas, monospace;\n pointer-events: auto;\n}\n\n.sp-stats-header {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 4px;\n margin: -4px -6px 4px 0;\n}\n\n.sp-stats-copy {\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 12px;\n line-height: 22px;\n padding: 0 6px;\n border-radius: 3px;\n white-space: nowrap;\n}\n\n.sp-stats-copy:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.1);\n}\n\n.sp-stats-close {\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.sp-stats-close:hover {\n opacity: 1;\n}\n\n.sp-stats-row {\n display: flex;\n gap: 12px;\n}\n\n.sp-stats-row dt {\n flex: 0 0 110px;\n opacity: 0.65;\n margin: 0;\n}\n\n.sp-stats-row dd {\n margin: 0;\n word-break: break-all;\n}\n\n/* ---------- \u5FEB\u6377\u952E\u9762\u677F ---------- */\n.sp-shortcuts {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 30;\n min-width: 280px;\n max-width: calc(100% - 24px);\n max-height: calc(100% - 24px);\n overflow-y: auto;\n padding: 14px 20px 14px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 8px;\n font-size: 13px;\n line-height: 2.1;\n pointer-events: auto;\n}\n\n.sp-shortcuts-title {\n font-size: 14px;\n font-weight: 600;\n margin-bottom: 6px;\n}\n\n.sp-shortcuts-body {\n margin: 0;\n}\n\n.sp-shortcuts-row {\n display: flex;\n align-items: center;\n gap: 16px;\n}\n\n.sp-shortcuts-row dt {\n flex: 0 0 100px;\n margin: 0;\n}\n\n.sp-shortcuts-row dd {\n margin: 0;\n opacity: 0.85;\n}\n\n.sp-kbd {\n display: inline-block;\n padding: 0 7px;\n background: rgba(255, 255, 255, 0.15);\n border-radius: 4px;\n font-family: 'SFMono-Regular', Consolas, monospace;\n font-size: 12px;\n line-height: 20px;\n white-space: nowrap;\n}\n\n.sp-shortcuts-close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n opacity: 0.7;\n font-size: 16px;\n line-height: 1;\n}\n\n.sp-shortcuts-close:hover {\n opacity: 1;\n}\n\n/* ---------- \u53F3\u952E\u83DC\u5355 ---------- */\n.sp-context-menu {\n position: absolute;\n z-index: 40;\n min-width: 150px;\n padding: 4px 0;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n display: none;\n}\n\n.sp-context-menu.sp-visible {\n display: block;\n}\n\n.sp-context-item {\n display: block;\n width: 100%;\n padding: 8px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: left;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-context-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n/* ---------- \u4E2D\u592E OSD \u53CD\u9988 ---------- */\n.sp-osd {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 20;\n padding: 10px 18px;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n opacity: 0;\n transition: opacity 0.15s ease;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-osd.sp-visible {\n opacity: 1;\n}\n\n/* ---------- \u5E95\u90E8\u63A7\u5236\u533A ---------- */\n.sp-bottom {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 24px 12px 6px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);\n transition: opacity 0.25s ease;\n z-index: 10;\n}\n\n.sweet-player.sp-controls-hidden .sp-bottom,\n.sweet-player.sp-controls-hidden .sp-top {\n opacity: 0;\n pointer-events: none;\n}\n\n.sweet-player.sp-controls-hidden {\n cursor: none;\n}\n\n/* ---------- \u8FDB\u5EA6\u6761 ---------- */\n.sp-progress {\n position: relative;\n height: 14px;\n display: flex;\n align-items: center;\n cursor: pointer;\n margin-bottom: 4px;\n}\n\n.sp-progress-track {\n position: relative;\n width: 100%;\n height: var(--sp-bar-height);\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n overflow: visible;\n transition: height 0.1s ease;\n}\n\n.sp-progress:hover .sp-progress-track {\n height: var(--sp-bar-hover-height);\n}\n\n.sp-progress-buffered,\n.sp-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n border-radius: 2px;\n}\n\n.sp-progress-buffered {\n background: rgba(255, 255, 255, 0.35);\n}\n\n.sp-progress-played {\n background: var(--sp-accent);\n}\n\n.sp-progress-thumb {\n position: absolute;\n top: 50%;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--sp-accent);\n transform: translate(-50%, -50%) scale(0);\n transition: transform 0.1s ease;\n pointer-events: none;\n}\n\n.sp-progress:hover .sp-progress-thumb,\n.sp-progress.sp-dragging .sp-progress-thumb {\n transform: translate(-50%, -50%) scale(1);\n}\n\n.sp-progress-tooltip {\n position: absolute;\n bottom: 18px;\n transform: translateX(-50%);\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 3px;\n font-size: 12px;\n display: none;\n pointer-events: none;\n white-space: nowrap;\n}\n\n.sp-progress:hover .sp-progress-tooltip {\n display: block;\n}\n\n/* ---------- \u63A7\u5236\u6309\u94AE\u884C ---------- */\n.sp-controls {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.sp-controls-spacer {\n flex: 1;\n}\n\n.sp-btn {\n width: var(--sp-control-size);\n height: var(--sp-control-size);\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n padding: 0;\n opacity: 0.9;\n transition: opacity 0.1s ease, background 0.1s ease;\n}\n\n.sp-btn:hover {\n opacity: 1;\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-btn svg {\n width: 22px;\n height: 22px;\n}\n\n.sp-btn:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n}\n\n.sp-time {\n padding: 0 8px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n opacity: 0.95;\n}\n\n/* ---------- \u5F39\u51FA\u83DC\u5355 ---------- */\n.sp-menu {\n position: relative;\n}\n\n.sp-menu-btn {\n min-width: var(--sp-control-size);\n width: auto;\n padding: 0 8px;\n font-size: 13px;\n font-weight: 500;\n}\n\n.sp-menu-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n min-width: 80px;\n max-height: 220px;\n overflow-y: auto;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-menu.sp-open .sp-menu-panel {\n display: block;\n}\n\n.sp-menu-item {\n display: block;\n width: 100%;\n padding: 7px 16px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n text-align: center;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n}\n\n.sp-menu-item:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-menu-item.sp-active {\n color: var(--sp-accent);\n font-weight: 600;\n}\n\n.sp-menu-empty {\n padding: 7px 16px;\n opacity: 0.5;\n white-space: nowrap;\n}\n\n/* ---------- \u72B6\u6001\u8499\u5C42\uFF1Aloading / \u9519\u8BEF / \u7ED3\u675F ---------- */\n.sp-state {\n position: absolute;\n inset: 0;\n z-index: 15;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n}\n\n.sp-spinner {\n opacity: 0;\n transition: opacity 0.2s ease 0.3s; /* \u5EF6\u8FDF\u51FA\u73B0\uFF0C\u907F\u514D\u77ED\u6682 seek \u95EA\u70C1 */\n}\n\n.sp-state.sp-loading .sp-spinner {\n opacity: 1;\n}\n\n.sp-spinner-ring {\n width: 44px;\n height: 44px;\n border: 4px solid rgba(255, 255, 255, 0.25);\n border-top-color: var(--sp-accent);\n border-radius: 50%;\n animation: sp-spin 0.8s linear infinite;\n}\n\n@keyframes sp-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n.sp-state-panel {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 14px;\n background: rgba(0, 0, 0, 0.55);\n pointer-events: auto;\n}\n\n.sp-state-message {\n font-size: 15px;\n opacity: 0.95;\n}\n\n.sp-state-actions {\n display: flex;\n gap: 12px;\n}\n\n.sp-state-btn {\n padding: 8px 22px;\n border: 1px solid rgba(255, 255, 255, 0.5);\n border-radius: 18px;\n background: transparent;\n color: var(--sp-text);\n font-size: 14px;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.sp-state-btn:hover {\n background: rgba(255, 255, 255, 0.15);\n}\n\n.sp-state-btn-primary {\n background: var(--sp-accent);\n border-color: var(--sp-accent);\n}\n\n.sp-state-btn-primary:hover {\n background: var(--sp-accent);\n filter: brightness(1.1);\n}\n\n.sp-state-countdown {\n font-size: 13px;\n opacity: 0.85;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.sp-state-cancel {\n border: none;\n background: transparent;\n color: var(--sp-accent);\n cursor: pointer;\n font-size: 13px;\n padding: 2px 6px;\n}\n\n/* ---------- \u8BBE\u7F6E\u9762\u677F ---------- */\n.sp-settings-panel {\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n width: 180px;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 6px;\n padding: 4px 0;\n display: none;\n z-index: 25;\n}\n\n.sp-settings.sp-open .sp-settings-panel {\n display: block;\n}\n\n.sp-settings-row {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 7px 12px;\n border: none;\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n gap: 4px;\n}\n\n.sp-settings-row:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n\n.sp-settings-label {\n flex: 1;\n text-align: left;\n}\n\n.sp-settings-value {\n opacity: 0.6;\n font-size: 12px;\n}\n\n.sp-settings-arrow {\n display: inline-flex;\n align-items: center;\n opacity: 0.5;\n}\n\n.sp-settings-arrow svg {\n width: 16px;\n height: 16px;\n}\n\n.sp-settings-back {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 7px 12px;\n border: none;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n background: transparent;\n color: var(--sp-text);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n gap: 2px;\n margin-bottom: 2px;\n}\n\n.sp-settings-back:hover {\n background: rgba(255, 255, 255, 0.08);\n}\n\n.sp-settings-back-arrow {\n display: inline-flex;\n align-items: center;\n opacity: 0.7;\n}\n\n.sp-settings-back-arrow svg {\n width: 18px;\n height: 18px;\n}\n\n.sp-settings-sub .sp-menu-item {\n text-align: left;\n padding: 7px 16px;\n}\n\n/* ---------- \u884C\u5185\u5F00\u5173 ---------- */\n.sp-settings-toggle-row {\n cursor: pointer;\n}\n\n.sp-toggle {\n position: relative;\n width: 30px;\n height: 17px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 9px;\n flex-shrink: 0;\n transition: background 0.15s ease;\n outline: none;\n}\n\n.sp-toggle:focus-visible {\n box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.4);\n}\n\n.sp-toggle.sp-on {\n background: var(--sp-accent);\n}\n\n.sp-toggle-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 13px;\n height: 13px;\n background: #fff;\n border-radius: 50%;\n transition: transform 0.15s ease;\n}\n\n.sp-toggle.sp-on .sp-toggle-thumb {\n transform: translateX(13px);\n}\n\n/* ---------- \u97F3\u91CF ---------- */\n.sp-volume {\n display: flex;\n align-items: center;\n}\n\n.sp-volume-slider {\n width: 0;\n overflow: hidden;\n transition: width 0.15s ease;\n display: flex;\n align-items: center;\n}\n\n.sp-volume:hover .sp-volume-slider,\n.sp-volume.sp-dragging .sp-volume-slider {\n width: 64px;\n}\n\n.sp-volume-track {\n position: relative;\n width: 56px;\n height: var(--sp-bar-height);\n margin: 0 4px;\n background: rgba(255, 255, 255, 0.25);\n border-radius: 2px;\n cursor: pointer;\n}\n\n.sp-volume-fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: var(--sp-text);\n border-radius: 2px;\n}\n\n.sp-volume-thumb {\n position: absolute;\n top: 50%;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--sp-text);\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n";
|
|
1076
1413
|
|
|
1077
1414
|
// src/player.ts
|
|
1078
1415
|
var DEFAULT_RATES = [0.5, 1, 1.5, 2];
|
|
@@ -1081,19 +1418,17 @@ var DEFAULT_LONG_SEEK_STEPS = [10, 30, 60];
|
|
|
1081
1418
|
var DEFAULT_STEP_UP_INTERVAL = 2e3;
|
|
1082
1419
|
var DEFAULT_AUTO_NEXT_SECONDS = 5;
|
|
1083
1420
|
var CONTROLS_HIDE_DELAY = 3e3;
|
|
1084
|
-
var TITLE_CLICK_COUNT = 10;
|
|
1085
|
-
var TITLE_CLICK_WINDOW = 1500;
|
|
1086
1421
|
var SINGLE_CLICK_DELAY = 250;
|
|
1422
|
+
var NPM_URL = "https://www.npmjs.com/package/@sweet-player/core";
|
|
1087
1423
|
var PROGRESS_SAVE_INTERVAL = 5e3;
|
|
1088
1424
|
var PROGRESS_END_GUARD = 10;
|
|
1089
1425
|
var SweetPlayer = class {
|
|
1090
1426
|
constructor(options) {
|
|
1091
1427
|
this.emitter = new EventEmitter();
|
|
1428
|
+
this.contextMenu = null;
|
|
1092
1429
|
this.hideTimer = null;
|
|
1093
1430
|
this.clickTimer = null;
|
|
1094
1431
|
this.progressTimer = null;
|
|
1095
|
-
this.titleClicks = 0;
|
|
1096
|
-
this.lastTitleClick = 0;
|
|
1097
1432
|
this.currentRatio = "original";
|
|
1098
1433
|
/** 画质/音轨菜单当前是否由 hls.js 自动接管(业务 setQualities 会关闭) */
|
|
1099
1434
|
this.hlsManagedQuality = false;
|
|
@@ -1126,9 +1461,27 @@ var SweetPlayer = class {
|
|
|
1126
1461
|
onLevels: autoQuality && !options.qualities?.length ? (levels) => this.applyHlsLevels(levels) : void 0,
|
|
1127
1462
|
onAudioTracks: autoQuality && !options.audioTracks?.length ? (tracks) => this.applyHlsAudioTracks(tracks) : void 0
|
|
1128
1463
|
});
|
|
1464
|
+
const hidden = new Set(options.hiddenControls ?? []);
|
|
1129
1465
|
this.osd = createOsd();
|
|
1130
1466
|
this.stats = createStatsOverlay(this.container, this.video, this.media, this.i18n);
|
|
1467
|
+
this.shortcutsPanel = createShortcutsOverlay(this.container, this.i18n, options.seekStep ?? 10);
|
|
1131
1468
|
this.state = createStateOverlay(this.i18n);
|
|
1469
|
+
if (!hidden.has("contextMenu")) {
|
|
1470
|
+
this.contextMenu = createContextMenu(this.container, [
|
|
1471
|
+
{
|
|
1472
|
+
label: `${this.i18n.t("changelog")}: v${VERSION}`,
|
|
1473
|
+
onClick: () => window.open(NPM_URL, "_blank", "noopener")
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
label: this.i18n.t("videoInfo"),
|
|
1477
|
+
onClick: () => this.stats.toggle()
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
label: this.i18n.t("shortcuts"),
|
|
1481
|
+
onClick: () => this.shortcutsPanel.toggle()
|
|
1482
|
+
}
|
|
1483
|
+
]);
|
|
1484
|
+
}
|
|
1132
1485
|
this.controls = createControls({
|
|
1133
1486
|
video: this.video,
|
|
1134
1487
|
title: options.title ?? "",
|
|
@@ -1136,6 +1489,7 @@ var SweetPlayer = class {
|
|
|
1136
1489
|
playbackRates: options.playbackRates ?? DEFAULT_RATES,
|
|
1137
1490
|
aspectRatios: options.aspectRatios ?? DEFAULT_RATIOS,
|
|
1138
1491
|
seekStep: options.seekStep ?? 10,
|
|
1492
|
+
hidden,
|
|
1139
1493
|
actions: {
|
|
1140
1494
|
togglePlay: () => this.toggle(),
|
|
1141
1495
|
seekBy: (d) => this.seekBy(d),
|
|
@@ -1148,8 +1502,7 @@ var SweetPlayer = class {
|
|
|
1148
1502
|
selectQuality: (q) => this.handleQualitySelect(q),
|
|
1149
1503
|
selectAudioTrack: (t) => this.handleAudioTrackSelect(t),
|
|
1150
1504
|
onPrev: options.onPrev,
|
|
1151
|
-
onNext: options.onNext
|
|
1152
|
-
onTitleClick: () => this.handleTitleClick()
|
|
1505
|
+
onNext: options.onNext
|
|
1153
1506
|
}
|
|
1154
1507
|
});
|
|
1155
1508
|
this.container.appendChild(this.controls.topEl);
|
|
@@ -1205,6 +1558,7 @@ var SweetPlayer = class {
|
|
|
1205
1558
|
this.bindActivityTracking();
|
|
1206
1559
|
this.disposers.push(
|
|
1207
1560
|
onFullscreenChange(this.container, (fs) => {
|
|
1561
|
+
log("\u64AD\u653E\u5668", fs ? "\u8FDB\u5165\u5168\u5C4F" : "\u9000\u51FA\u5168\u5C4F");
|
|
1208
1562
|
this.controls.updateFullscreen(fs);
|
|
1209
1563
|
this.emitter.emit("fullscreenchange", fs);
|
|
1210
1564
|
})
|
|
@@ -1240,6 +1594,7 @@ var SweetPlayer = class {
|
|
|
1240
1594
|
if (showOsd) this.osd.flash(`${delta > 0 ? "+" : ""}${delta} ${this.i18n.t("seconds")}`);
|
|
1241
1595
|
}
|
|
1242
1596
|
setRate(rate) {
|
|
1597
|
+
log("\u64AD\u653E\u5668", `\u500D\u901F\u5207\u6362: ${rate}x`);
|
|
1243
1598
|
this.video.playbackRate = rate;
|
|
1244
1599
|
}
|
|
1245
1600
|
/** 0-100 */
|
|
@@ -1326,7 +1681,9 @@ var SweetPlayer = class {
|
|
|
1326
1681
|
this.keyboard.destroy();
|
|
1327
1682
|
this.gestures.destroy();
|
|
1328
1683
|
this.stats.destroy();
|
|
1684
|
+
this.shortcutsPanel.destroy();
|
|
1329
1685
|
this.state.destroy();
|
|
1686
|
+
this.contextMenu?.destroy();
|
|
1330
1687
|
this.controls.destroy();
|
|
1331
1688
|
this.disposers.forEach((d) => d());
|
|
1332
1689
|
if (this.hideTimer) clearTimeout(this.hideTimer);
|
|
@@ -1349,6 +1706,7 @@ var SweetPlayer = class {
|
|
|
1349
1706
|
this.hlsManagedAudio = true;
|
|
1350
1707
|
}
|
|
1351
1708
|
handleQualitySelect(quality) {
|
|
1709
|
+
log("\u64AD\u653E\u5668", `\u5207\u6362\u753B\u8D28: ${quality.label}`);
|
|
1352
1710
|
this.controls.qualityMenu.setActive(quality);
|
|
1353
1711
|
if (this.hlsManagedQuality && typeof quality.value === "number") {
|
|
1354
1712
|
this.media.setLevel(quality.value);
|
|
@@ -1361,6 +1719,7 @@ var SweetPlayer = class {
|
|
|
1361
1719
|
this.emitter.emit("qualitychange", quality);
|
|
1362
1720
|
}
|
|
1363
1721
|
handleAudioTrackSelect(track) {
|
|
1722
|
+
log("\u64AD\u653E\u5668", `\u5207\u6362\u97F3\u8F68: ${track.label}`);
|
|
1364
1723
|
this.controls.audioMenu.setActive(track);
|
|
1365
1724
|
if (this.hlsManagedAudio && typeof track.value === "number") {
|
|
1366
1725
|
this.media.setAudioTrack(track.value);
|
|
@@ -1401,15 +1760,6 @@ var SweetPlayer = class {
|
|
|
1401
1760
|
}
|
|
1402
1761
|
}
|
|
1403
1762
|
// ---------- 内部:交互 ----------
|
|
1404
|
-
handleTitleClick() {
|
|
1405
|
-
const now = Date.now();
|
|
1406
|
-
this.titleClicks = now - this.lastTitleClick <= TITLE_CLICK_WINDOW ? this.titleClicks + 1 : 1;
|
|
1407
|
-
this.lastTitleClick = now;
|
|
1408
|
-
if (this.titleClicks >= TITLE_CLICK_COUNT) {
|
|
1409
|
-
this.titleClicks = 0;
|
|
1410
|
-
this.stats.toggle();
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
1763
|
bindMediaEvents() {
|
|
1414
1764
|
const v = this.video;
|
|
1415
1765
|
const listen = (event, fn) => {
|
|
@@ -1417,6 +1767,7 @@ var SweetPlayer = class {
|
|
|
1417
1767
|
this.disposers.push(() => v.removeEventListener(event, fn));
|
|
1418
1768
|
};
|
|
1419
1769
|
listen("loadedmetadata", () => {
|
|
1770
|
+
log("\u539F\u751F\u4E8B\u4EF6", `\u83B7\u53D6\u5143\u6570\u636E (${v.videoWidth}x${v.videoHeight})`);
|
|
1420
1771
|
this.controls.updateTime();
|
|
1421
1772
|
this.emitter.emit("ready", void 0);
|
|
1422
1773
|
});
|
|
@@ -1432,6 +1783,7 @@ var SweetPlayer = class {
|
|
|
1432
1783
|
this.emitter.emit("pause", void 0);
|
|
1433
1784
|
});
|
|
1434
1785
|
listen("ended", () => {
|
|
1786
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u64AD\u653E\u7ED3\u675F");
|
|
1435
1787
|
this.saveProgressNow();
|
|
1436
1788
|
this.showControls();
|
|
1437
1789
|
const onNext = this.options.onNext;
|
|
@@ -1461,23 +1813,49 @@ var SweetPlayer = class {
|
|
|
1461
1813
|
this.controls.volume.update(volume, v.muted);
|
|
1462
1814
|
this.emitter.emit("volumechange", { volume, muted: v.muted });
|
|
1463
1815
|
});
|
|
1464
|
-
listen("waiting", () =>
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1816
|
+
listen("waiting", () => {
|
|
1817
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u7B49\u5F85\u7F13\u51B2\u4E2D");
|
|
1818
|
+
this.state.showLoading();
|
|
1819
|
+
});
|
|
1820
|
+
listen("stalled", () => {
|
|
1821
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u6570\u636E\u52A0\u8F7D\u505C\u6EDE");
|
|
1822
|
+
this.state.showLoading();
|
|
1823
|
+
});
|
|
1824
|
+
listen("seeking", () => {
|
|
1825
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u5F00\u59CB\u8DF3\u8F6C");
|
|
1826
|
+
this.state.showLoading();
|
|
1827
|
+
});
|
|
1828
|
+
listen("canplay", () => {
|
|
1829
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u51C6\u5907\u597D\u5F00\u59CB\u64AD\u653E");
|
|
1830
|
+
this.state.hideLoading();
|
|
1831
|
+
});
|
|
1468
1832
|
listen("playing", () => {
|
|
1469
1833
|
this.state.hideLoading();
|
|
1470
1834
|
this.state.hideError();
|
|
1471
1835
|
});
|
|
1472
|
-
listen("seeked", () =>
|
|
1473
|
-
|
|
1836
|
+
listen("seeked", () => {
|
|
1837
|
+
log("\u539F\u751F\u4E8B\u4EF6", "\u8DF3\u8F6C\u5B8C\u6210");
|
|
1838
|
+
this.state.hideLoading();
|
|
1839
|
+
});
|
|
1840
|
+
listen("error", () => {
|
|
1841
|
+
log("\u539F\u751F\u4E8B\u4EF6", `\u64AD\u653E\u9519\u8BEF: ${v.error?.message ?? "\u672A\u77E5\u9519\u8BEF"}`);
|
|
1842
|
+
this.showErrorState({ type: "media", detail: v.error });
|
|
1843
|
+
});
|
|
1474
1844
|
this.disposers.push(
|
|
1475
1845
|
this.emitter.on("error", (payload) => {
|
|
1476
1846
|
if (payload.type.startsWith("hls-")) this.showErrorStateUi();
|
|
1477
1847
|
})
|
|
1478
1848
|
);
|
|
1479
|
-
listen("enterpictureinpicture", () =>
|
|
1480
|
-
|
|
1849
|
+
listen("enterpictureinpicture", () => {
|
|
1850
|
+
log("\u64AD\u653E\u5668", "\u8FDB\u5165\u753B\u4E2D\u753B");
|
|
1851
|
+
this.controls.updatePip(true);
|
|
1852
|
+
this.emitter.emit("pipchange", true);
|
|
1853
|
+
});
|
|
1854
|
+
listen("leavepictureinpicture", () => {
|
|
1855
|
+
log("\u64AD\u653E\u5668", "\u9000\u51FA\u753B\u4E2D\u753B");
|
|
1856
|
+
this.controls.updatePip(false);
|
|
1857
|
+
this.emitter.emit("pipchange", false);
|
|
1858
|
+
});
|
|
1481
1859
|
const onVideoClick = (e) => {
|
|
1482
1860
|
if (e.target !== this.video || e.pointerType === "touch") return;
|
|
1483
1861
|
if (this.clickTimer) return;
|
|
@@ -1544,8 +1922,10 @@ var SweetPlayer = class {
|
|
|
1544
1922
|
});
|
|
1545
1923
|
}
|
|
1546
1924
|
};
|
|
1925
|
+
SweetPlayer.version = VERSION;
|
|
1547
1926
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1548
1927
|
0 && (module.exports = {
|
|
1549
1928
|
SweetPlayer,
|
|
1929
|
+
VERSION,
|
|
1550
1930
|
registerLocale
|
|
1551
1931
|
});
|