diff --git a/src/player.player.ts b/src/player.player.ts
index 78b3c2c..2daec41 100644
--- a/src/player.player.ts
+++ b/src/player.player.ts
@@ -1,447 +1,456 @@
-const toggleDisplayByData = (dataName: string, thisClass: string) => {
- let r = '';
- for (let i = 0; i < 4; i++) {
- r += `.player[data-${dataName}='${i < 2 ? 'true' : 'false'}'] `;
- r += `.${thisClass}>span:${i % 2 === 0 ? 'first' : 'last'}-child`;
- r += `{display: ${i > 0 && i - 3 < 0 ? 'none' : 'unset'};}\n`;
+const __player_metadata__ = (function () {
+ function toggleDisplayByData(dataName: string, thisClass: string) {
+ let r = '';
+ for (let i = 0; i < 4; i++) {
+ r += `.player[data-${dataName}='${i < 2 ? 'true' : 'false'}'] `;
+ r += `.${thisClass}>span:${i % 2 === 0 ? 'first' : 'last'}-child`;
+ r += `{display: ${i > 0 && i - 3 < 0 ? 'none' : 'unset'};}\n`;
+ }
+ return r;
}
- return r;
-};
-function toggleComponent(P: Player, key: string, on: () => void, onToast: string, off: () => void, offToast: string) {
- if (!P._dyn[key]) {
- P._dyn[key] = true;
- on();
- P.setContainerData(key, true);
- P.toast(onToast);
- } else {
- P._dyn[key] = false;
- off();
- P.setContainerData(key, false);
- P.toast(offToast);
+ function toggleComponent(P: Player, key: string, on: Function, onMsg: string, off: Function, offMsg: string) {
+ if (!P._dyn[key]) {
+ P._dyn[key] = true;
+ on();
+ P.setContainerData(key, true);
+ P.toast(onMsg);
+ } else {
+ P._dyn[key] = false;
+ off();
+ P.setContainerData(key, false);
+ P.toast(offMsg);
+ }
}
-}
-const icon_danmaku_off =
- '';
-const icon_subtitle_on =
- '';
+ const icon_danmaku_off =
+ '';
+ const icon_subtitle_on =
+ '';
-const Icons = {
- play: '',
- pause: '',
- fullscreen: '',
- fullscreen_exit: '',
- volume: '',
- mute: '',
- danmaku_off: icon_danmaku_off,
- danmaku_on:
- icon_danmaku_off +
- '',
- subtitle_on: icon_subtitle_on,
- subtitle_off: icon_subtitle_on.replace('d=', 'fill="#7e7e7e" d='),
-} as const;
+ const icons = {
+ volume: '',
+ mute: '',
+ danmakuOff: icon_danmaku_off,
+ danmakuOn:
+ icon_danmaku_off +
+ '',
+ subtitleOn: icon_subtitle_on,
+ subtitleOff: icon_subtitle_on.replace('d=', 'fill="#7e7e7e" d='),
+ } as const;
-function icon(p: K) {
- return (
- ''
- );
-}
+ function icon(p: K) {
+ return (
+ ''
+ );
+ }
-const __player_metadata__: PlayerMetadata = {
- elements: [
- new EDC('div') //
- .class('toast box visibility-transition invisible')
- .playerEvents({
- toast: (P, E, T: CustomEvent) => {
- E.innerHTML = T.detail.content;
- opacityVisible(E);
- clearTimeout(P._dyn.toastTimer);
- P._dyn.toastTimer = setTimeout(() => opacityInvisible(E), 800);
- },
- }),
- new EDC('div') //
- .class('controls-wrapper')
- .selfEvents({
- mousemove: (P) => opacityVisible(P.elements.controls),
- mouseleave: (P) => {
- opacityInvisible(P.elements.controls);
- P.focus();
- },
- })
- .children(
- new EDC('div', 'controls') //
- .class('controls box visibility-transition invisible')
- .playerEvents({
- fullscreen: (_, E) => opacityInvisible(E),
- })
- .children(
- new EDC('button', 'playToggle') //
- .class('play-toggle')
- .title('Play/Pause')
- .css((s) => toggleDisplayByData('paused', s._attrs.class))
- .selfEvents({ click: (P) => P.togglePlay() })
- .children(...newSpans('⏵', '⏸')),
- new EDC('div') //
- .class('volume-wrapper')
- .children(
- new EDC('button', 'muteToggle')
- .class('mute-toggle')
- .title('Mute/Unmute')
- .css((s) => toggleDisplayByData('muted', s._attrs.class))
- .selfEvents({
- click: (P) => P.toggleMute(),
- })
- .children(...newSpans(icon('mute'), icon('volume'))),
- new EDC('input', 'volume')
- .class('volume')
- .title('Volume')
- .attrs({
- type: 'number',
- min: '0',
- max: '100',
- step: '5',
- value: '100',
- })
- .selfEvents({
- input: (P, E) => P.setVolume(E.valueAsNumber / 100),
- })
- .playerEvents({
- mute: (_, E) => (E.disabled = true),
- unmute: (_, E) => (E.disabled = false),
- })
- .videoEvents({
- volumechange: (_, E, V) => (E.valueAsNumber = Math.round(V.volume * 100)),
- })
- ),
- new EDC('div') //
- .class('progress-wrapper')
- .children(
- new EDC('input', 'progress')
- .class('progress')
- .attrs({
- type: 'range',
- min: '0',
- max: '1',
- step: '0.0001',
- 'default-value': '0',
- 'data-seeking': 'false',
- })
- .selfEvents({
- create: (_, E) => (E.valueAsNumber = 0),
- change: (P, E) => {
- P.seekPercent(E.valueAsNumber);
- opacityInvisible(P.elements.progressPopup);
- P._dyn.progressInputting = false;
- },
- input: (P, E) => {
- P._dyn.progressInputting = true;
- const value = E.valueAsNumber;
- const popup = P.elements.progressPopup;
- popup.textContent = fTime(P.video.duration * value);
- popup.style.left = `calc(${value * 100}% + (${8 - value * 100 * 0.15}px))`;
- popup.style.transform = 'translateX(' + -popup.offsetWidth / 2 + 'px)';
- opacityVisible(popup);
- },
- })
- .videoEvents({
- timeupdate: (P, E, V) => {
- if (!P._dyn.progressInputting) {
- const v = V.currentTime / V.duration;
- E.valueAsNumber = v ? v : 0;
- }
- },
- }),
- new EDC('div', 'progressPopup') //
- .class('progress-popup box visibility-transition invisible')
- ),
- new EDC('div') //
- .class('time-label')
- .selfEvents({
- mouseleave: (P) => toggleDisplayBi(P.elements.timeCurrent, P.elements.timeInput),
- })
- .children(
- new EDC('input', 'timeInput')
- .class('time-input hide')
- .attrs({
- type: 'time',
- step: '1',
- })
- .selfEvents({
- change: (P, E) => {
- if (E.validity.valid) {
- P.seek(timeToSeconds(E.value));
- } else {
- E.value = fTime(P.video.currentTime, true);
- }
- },
- })
- .videoEvents({
- canplay: (P, E, V) => {
- E.step = '1';
- E.value = P.fCurrentTime(true);
- E.max = fTime(V.duration, true);
- },
- timeupdate: (P, E, V) => {
- E.value = P.fCurrentTime(true);
- },
- }),
- new EDC('span', 'timeCurrent') //
- .html('--:--')
- .videoEvents({
- canplay: (P, E, V) => (E.textContent = P.fCurrentTime()),
- timeupdate: (P, E, V) => (E.textContent = P.fCurrentTime()),
- }),
- new EDC('span').html(' / '),
- new EDC('span')
- .html('--:--')
- .selfEvents({
- click: (P) => toggleDisplay(P.elements.timeInput, P.elements.timeCurrent),
- })
- .videoEvents({
- canplay: (_, E, V) => (E.textContent = fTime(V.duration)),
- })
- ),
- new EDC('button', 'subtitleToggle')
- .condition(hasSubtitle)
- .class('subtitle-toggle')
- .title('Subtitle')
- .css((s) => toggleDisplayByData('subtitle-on', s._attrs.class))
- .selfEvents({
- click: (P) =>
- toggleComponent(
- P,
- 'subtitleOn',
- () => P.subtitleManager.show(),
- 'Subtitle On',
- () => P.subtitleManager.hide(),
- 'Subtitle Off'
- ),
- })
- .children(...newSpans(icon('subtitle_on'), icon('subtitle_off'))),
- new EDC('div')
- .condition(hasDanmaku)
- .class('danmaku-controls')
- .children(
- new EDC('button', 'danmakuToggle')
- .class('danmaku-toggle')
- .title('Danmaku')
- .css((s) => toggleDisplayByData('danmaku-on', s._attrs.class))
- .selfEvents({
- click: (P) =>
- toggleComponent(
- P,
- 'danmakuOn',
- () => P.commentManager.start(),
- 'Danmaku On',
- () => {
- P.commentManager.clear();
- P.commentManager.stop();
- },
- 'Danmaku Off'
- ),
- })
- .children(...newSpans(icon('danmaku_on'), icon('danmaku_off'))),
- new EDC('button', 'danmakuListToggle') //
- .html('?')
- .title('Danmaku list')
- .selfEvents({
- click: (P) => toggleDisplay(P.elements.danmakuList),
- })
- .playerEvents({
- danmakuload: (P, E) => (E.innerHTML = `(${danmakuCount(P)})`),
- }),
- new EDC('input')
- .class('danmaku-time-offset')
- .title('Danmaku time offset')
- .attrs({
- type: 'number',
- step: '1',
- value: '0',
- })
- .selfEvents({
- create: (P, E) => {
- if (!P.options.danmakuTimeOffset) P.options.danmakuTimeOffset = 0;
- E.valueAsNumber = P.options.danmakuTimeOffset;
- },
- input: (P, E) => {
- P.options.danmakuTimeOffset = E.valueAsNumber;
- P.commentManager.clear();
- },
- }),
- new EDC('input')
- .class('danmaku-size-offset')
- .title('Danmaku size offset')
- .attrs({
- type: 'number',
- step: '1',
- value: '0',
- })
- .selfEvents({
- create: (P, E) => {
- if (!P.options.danmakuSizeOffset) P.options.danmakuSizeOffset = 0;
- E.valueAsNumber = P.options.danmakuSizeOffset;
- P._dyn.danmakuSizeFlag = randomStr();
- },
- input: (P, E) => {
- P.options.danmakuSizeOffset = E.valueAsNumber;
- P._dyn.danmakuSizeFlag = randomStr();
- P.commentManager.clear();
- },
- })
- ),
- new EDC('button', 'fullscreenToggle')
- .class('fullscreen-toggle')
- .title('Fullscreen')
- .css((s) => toggleDisplayByData('fullscreen', s._attrs.class))
- .selfEvents({
- click: (P) => P.toggleFullscreen(),
- })
- .children(...newSpans('🡷', '🡵'))
- )
- ),
- new EDC('div', 'danmakuList')
- .condition(hasDanmaku)
- .class('danmaku-list box hide')
- .children(
- new EDC('ul') //
- .playerEvents({
- danmakuload: async (P, E) => {
- const timeline = P.commentManager.timeline;
- const overHour = timeline ? timeline[timeline.length - 1].stime >= 36e5 : false;
- let html = '';
- for (const data of timeline) {
- html += // for performance, do not use document.createElement
- `${fTime(data.stime / 1e3, overHour)}` +
- `${data.text}`;
- }
- E.innerHTML = html;
- },
- })
- ),
- new EDC('div')
- .class('overlays abp')
- .selfEvents({
- click: (P) => P.togglePlay(),
- })
- .children(
- new EDC('div', 'subtitleStage')
- .class('subtitle-stage container')
- .condition(hasSubtitle)
- .selfEvents({
- create: (P, E) => {
- P.subtitleManager = initSubtitle(E, P.video, P.subtitleUrl);
- P.firePlayerEvent('subtitleload');
- P._dyn.subtitleOn = true;
- P.setContainerData('subtitleOn', true);
- },
- })
- .videoEvents({
- resize: (P) => P.subtitleManager.resize(),
- }),
- new EDC('div', 'danmakuStage')
- .class('danmaku-stage container')
- .condition(hasDanmaku)
- .selfEvents({
- create: (P, E) => {
- P.commentManager = initDanmaku(E, P.danmakuUrl, () => P.firePlayerEvent('danmakuload'));
- if (P.options.danmakuSizeOffset) {
- P.commentManager.filter.addModifier(function (commentData: StrAnyKV) {
- const override = commentData;
- const size = commentData['size'];
- let sizeBak = commentData['sizeBackup'];
- if (size && override['sizeFlag'] != P._dyn.danmakuSizeFlag) {
- if (!sizeBak) {
- override['sizeBackup'] = size;
- sizeBak = size;
- }
- override['size'] = sizeBak + P.options.danmakuSizeOffset;
- override['sizeFlag'] = P._dyn.danmakuSizeFlag;
- }
- return override;
- });
- }
- P._dyn.danmakuOn = true;
- P.setContainerData('danmakuOn', true);
- if (!P.options.danmakuTimeOffset) P.options.danmakuTimeOffset = 0;
- },
- })
- .videoEvents({
- timeupdate: (P, _, V) => {
- if (!P._dyn.danmakuOn) return;
- const cm = P.commentManager;
- const time = Math.floor(1e3 * (V.currentTime - P.options.danmakuTimeOffset));
- const deltaTime = time - cm._lastPosition;
- if (deltaTime < 0 || deltaTime > cm.options.seekTrigger) {
- cm.clear();
+ // ====================================================================== //
+
+ const toastBox = new EDC('div') //
+ .class('toast box visibility-transition invisible')
+ .playerEvents({
+ toast: (P, E, T: CustomEvent) => {
+ E.innerHTML = T.detail.content;
+ opacityVisible(E);
+ clearTimeout(P._dyn.toastTimer);
+ P._dyn.toastTimer = setTimeout(() => opacityInvisible(E), 800);
+ },
+ });
+
+ const playToggle = new EDC('button', 'playToggle') //
+ .class('play-toggle')
+ .title('Play/Pause')
+ .css((s) => toggleDisplayByData('paused', s._attrs.class))
+ .selfEvents({
+ click: (P) => P.togglePlay(),
+ })
+ .children(...newSpans('⏵', '⏸'));
+
+ const muteToggle = new EDC('button', 'muteToggle')
+ .class('mute-toggle')
+ .title('Mute/Unmute')
+ .css((s) => toggleDisplayByData('muted', s._attrs.class))
+ .selfEvents({
+ click: (P) => P.toggleMute(),
+ })
+ .children(...newSpans(icon('mute'), icon('volume')));
+
+ const volumeInput = new EDC('input', 'volume')
+ .class('volume')
+ .title('Volume')
+ .attrs({ type: 'number', min: '0', max: '100', step: '5', value: '100' })
+ .selfEvents({
+ input: (P, E) => P.setVolume(E.valueAsNumber / 100),
+ })
+ .playerEvents({
+ mute: (_, E) => (E.disabled = true),
+ unmute: (_, E) => (E.disabled = false),
+ })
+ .videoEvents({
+ volumechange: (_, E, V) => (E.valueAsNumber = Math.round(V.volume * 100)),
+ });
+
+ const progressBar = new EDC('input', 'progress')
+ .class('progress')
+ .attrs({
+ type: 'range',
+ min: '0',
+ max: '1',
+ step: '0.0001',
+ 'default-value': '0',
+ 'data-seeking': 'false',
+ })
+ .selfEvents({
+ create: (_, E) => (E.valueAsNumber = 0),
+ change: (P, E) => {
+ P.seekPercent(E.valueAsNumber);
+ opacityInvisible(P.elements.progressPopup);
+ P._dyn.progressInputting = false;
+ },
+ input: (P, E) => {
+ P._dyn.progressInputting = true;
+ const value = E.valueAsNumber;
+ const popup = P.elements.progressPopup;
+ popup.textContent = fTime(P.video.duration * value);
+ popup.style.left = `calc(${value * 100}% + (${8 - value * 100 * 0.15}px))`;
+ popup.style.transform = 'translateX(' + -popup.offsetWidth / 2 + 'px)';
+ opacityVisible(popup);
+ },
+ })
+ .videoEvents({
+ timeupdate: (P, E, V) => {
+ if (!P._dyn.progressInputting) {
+ const v = V.currentTime / V.duration;
+ E.valueAsNumber = v ? v : 0;
+ }
+ },
+ });
+
+ const progressPopup = new EDC('div', 'progressPopup') //
+ .class('progress-popup box visibility-transition invisible');
+
+ const timeInput = new EDC('input', 'timeInput')
+ .class('time-input hide')
+ .attrs({ type: 'time', step: '1' })
+ .selfEvents({
+ change: (P, E) => {
+ if (E.validity.valid) {
+ P.seek(timeToSeconds(E.value));
+ } else {
+ E.value = fTime(P.video.currentTime, true);
+ }
+ },
+ })
+ .videoEvents({
+ canplay: (P, E, V) => {
+ E.value = P.fCurrentTime(true);
+ E.max = fTime(V.duration, true);
+ },
+ timeupdate: (P, E, V) => {
+ E.value = P.fCurrentTime(true);
+ },
+ });
+
+ const timeCurrent = new EDC('span', 'timeCurrent') //
+ .html('--:--')
+ .videoEvents({
+ canplay: (P, E, V) => (E.textContent = P.fCurrentTime()),
+ timeupdate: (P, E, V) => (E.textContent = P.fCurrentTime()),
+ });
+
+ const timeTotal = new EDC('span')
+ .html('--:--')
+ .selfEvents({
+ click: (P) => toggleDisplay(P.elements.timeInput, P.elements.timeCurrent),
+ })
+ .videoEvents({
+ canplay: (_, E, V) => (E.textContent = fTime(V.duration)),
+ });
+
+ const subtitleToggle = new EDC('button', 'subtitleToggle')
+ .condition(hasSubtitle)
+ .class('subtitle-toggle')
+ .title('Subtitle')
+ .css((s) => toggleDisplayByData('subtitle-on', s._attrs.class))
+ .selfEvents({
+ click: (P) =>
+ toggleComponent(
+ P,
+ 'subtitleOn',
+ () => P.subtitleManager.show(),
+ 'Subtitle On',
+ () => P.subtitleManager.hide(),
+ 'Subtitle Off'
+ ),
+ })
+ .children(...newSpans(icon('subtitleOn'), icon('subtitleOff')));
+
+ const danmakuToggle = new EDC('button', 'danmakuToggle')
+ .class('danmaku-toggle')
+ .title('Danmaku')
+ .css((s) => toggleDisplayByData('danmaku-on', s._attrs.class))
+ .selfEvents({
+ click: (P) =>
+ toggleComponent(
+ P,
+ 'danmakuOn',
+ () => P.commentManager.start(),
+ 'Danmaku On',
+ () => {
+ P.commentManager.clear();
+ P.commentManager.stop();
+ },
+ 'Danmaku Off'
+ ),
+ })
+ .children(...newSpans(icon('danmakuOn'), icon('danmakuOff')));
+
+ const danmakuListToggle = new EDC('button', 'danmakuListToggle') //
+ .html('?')
+ .title('Danmaku list')
+ .selfEvents({
+ click: (P) => toggleDisplay(P.elements.danmakuList),
+ })
+ .playerEvents({
+ danmakuload: (P, E) => (E.innerHTML = `(${danmakuCount(P)})`),
+ });
+
+ const danmakuTimeOffset = new EDC('input')
+ .class('danmaku-time-offset')
+ .title('Danmaku time offset')
+ .attrs({ type: 'number', step: '1', value: '0' })
+ .selfEvents({
+ create: (P, E) => {
+ if (!P.options.danmakuTimeOffset) P.options.danmakuTimeOffset = 0;
+ E.valueAsNumber = P.options.danmakuTimeOffset;
+ },
+ input: (P, E) => {
+ P.options.danmakuTimeOffset = E.valueAsNumber;
+ P.commentManager.clear();
+ },
+ });
+
+ const danmakuSizeOffset = new EDC('input')
+ .class('danmaku-size-offset')
+ .title('Danmaku size offset')
+ .attrs({ type: 'number', step: '1', value: '0' })
+ .selfEvents({
+ create: (P, E) => {
+ if (!P.options.danmakuSizeOffset) P.options.danmakuSizeOffset = 0;
+ E.valueAsNumber = P.options.danmakuSizeOffset;
+ P._dyn.danmakuSizeFlag = randomStr();
+ },
+ input: (P, E) => {
+ P.options.danmakuSizeOffset = E.valueAsNumber;
+ P._dyn.danmakuSizeFlag = randomStr();
+ P.commentManager.clear();
+ },
+ });
+
+ const fullscreenToggle = new EDC('button', 'fullscreenToggle')
+ .class('fullscreen-toggle')
+ .title('Fullscreen')
+ .css((s) => toggleDisplayByData('fullscreen', s._attrs.class))
+ .selfEvents({
+ click: (P) => P.toggleFullscreen(),
+ })
+ .children(...newSpans('🡷', '🡵'));
+
+ const danmakuList = new EDC('div', 'danmakuList')
+ .condition(hasDanmaku)
+ .class('danmaku-list box hide')
+ .children(
+ new EDC('ul') //
+ .playerEvents({
+ danmakuload: async (P, E) => {
+ const timeline = P.commentManager.timeline;
+ const overHour = timeline ? timeline[timeline.length - 1].stime >= 36e5 : false;
+ let html = '';
+ for (const data of timeline) {
+ html += // for performance, do not use document.createElement
+ `${fTime(data.stime / 1e3, overHour)}` +
+ `${data.text}`;
+ }
+ E.innerHTML = html;
+ },
+ })
+ );
+
+ const subtitleStage = new EDC('div', 'subtitleStage')
+ .class('subtitle-stage container')
+ .condition(hasSubtitle)
+ .selfEvents({
+ create: (P, E) => {
+ P.subtitleManager = initSubtitle(E, P.video, P.subtitleUrl);
+ P.firePlayerEvent('subtitleload');
+ P._dyn.subtitleOn = true;
+ P.setContainerData('subtitleOn', true);
+ },
+ })
+ .videoEvents({
+ resize: (P) => P.subtitleManager.resize(),
+ });
+
+ const danmakuStage = new EDC('div', 'danmakuStage')
+ .class('danmaku-stage container')
+ .condition(hasDanmaku)
+ .selfEvents({
+ create: (P, E) => {
+ P.commentManager = initDanmaku(E, P.danmakuUrl, () => P.firePlayerEvent('danmakuload'));
+ if (P.options.danmakuSizeOffset) {
+ P.commentManager.filter.addModifier(function (commentData: StrAnyKV) {
+ const override = commentData;
+ const size = commentData['size'];
+ let sizeBak = commentData['sizeBackup'];
+ if (size && override['sizeFlag'] != P._dyn.danmakuSizeFlag) {
+ if (!sizeBak) {
+ override['sizeBackup'] = size;
+ sizeBak = size;
}
- cm.time(time);
- },
- play: (P) => P.commentManager.start(),
- pause: (P) => P.commentManager.stop(),
- resize: (P, _, V) => {
- const cm = P.commentManager;
- cm.clear();
- cm.setBounds();
- cm.options.scroll.scale = V.offsetWidth / 680 / 1;
- },
- })
- ),
- ],
- playerEvent: {
- mousemove: (P) => {
- clearTimeout(P._dyn.mouseTimer);
- P.setContainerData('mouseIdle', false);
- P._dyn.mouseTimer = setTimeout(() => {
- P.setContainerData('mouseIdle', true);
- }, 1e3);
- },
- keydown: (P, T) => {
- if (T.target === P.container) {
- switch (T.keyCode) {
- case 32: // Space
- P.togglePlay();
- break;
- case 77: // M
- P.toggleMute();
- break;
- case 70: // F
- P.toggleFullscreen();
- break;
- case 38: // Up
- P.adjustVolume(0.05);
- break;
- case 40: // Down
- P.adjustVolume(-0.05);
- break;
- case 37: // Left
- P.skip(T.ctrlKey ? -10 : T.shiftKey ? -1 : -5);
- break;
- case 39: // Right
- P.skip(T.ctrlKey ? 10 : T.shiftKey ? 1 : 5);
- break;
- case 73: // I
- P.toast(
- [
- ['LocalTime', new Date().toLocaleString()],
- ['File', `${P.title} @ ${P.video.videoWidth}x${P.video.videoHeight}`],
- ['Time', `${P.fCurrentTime()} / ${fTime(P.video.duration)}`],
- ]
- .map(([l, t]) => `${l}: ${t}`)
- .join('
')
- );
- break;
- case 68: // D
- if (P.elements.danmakuToggle) P.elements.danmakuToggle.click();
- break;
- default:
- break;
+ override['size'] = sizeBak + P.options.danmakuSizeOffset;
+ override['sizeFlag'] = P._dyn.danmakuSizeFlag;
+ }
+ return override;
+ });
+ }
+ P._dyn.danmakuOn = true;
+ P.setContainerData('danmakuOn', true);
+ if (!P.options.danmakuTimeOffset) P.options.danmakuTimeOffset = 0;
+ },
+ })
+ .videoEvents({
+ timeupdate: (P, _, V) => {
+ if (!P._dyn.danmakuOn) return;
+ const cm = P.commentManager;
+ const time = Math.floor(1e3 * (V.currentTime - P.options.danmakuTimeOffset));
+ const deltaTime = time - cm._lastPosition;
+ if (deltaTime < 0 || deltaTime > cm.options.seekTrigger) {
+ cm.clear();
}
+ cm.time(time);
+ },
+ play: (P) => P.commentManager.start(),
+ pause: (P) => P.commentManager.stop(),
+ resize: (P, _, V) => {
+ const cm = P.commentManager;
+ cm.clear();
+ cm.setBounds();
+ cm.options.scroll.scale = V.offsetWidth / 680 / 1;
+ },
+ });
+
+ // ====================================================================== //
+
+ const mouseIdle = (P: Player) => {
+ clearTimeout(P._dyn.mouseTimer);
+ P.setContainerData('mouseIdle', false);
+ P._dyn.mouseTimer = setTimeout(() => {
+ P.setContainerData('mouseIdle', true);
+ }, 1e3);
+ };
+
+ const hotkeys = (P: Player, T: KeyboardEvent) => {
+ if (T.target === P.container) {
+ switch (T.keyCode) {
+ case 32: // Space
+ P.togglePlay();
+ break;
+ case 77: // M
+ P.toggleMute();
+ break;
+ case 70: // F
+ P.toggleFullscreen();
+ break;
+ case 38: // Up
+ P.adjustVolume(0.05);
+ break;
+ case 40: // Down
+ P.adjustVolume(-0.05);
+ break;
+ case 37: // Left
+ P.skip(T.ctrlKey ? -10 : T.shiftKey ? -1 : -5);
+ break;
+ case 39: // Right
+ P.skip(T.ctrlKey ? 10 : T.shiftKey ? 1 : 5);
+ break;
+ case 73: // I
+ P.toast(
+ [
+ ['LocalTime', new Date().toLocaleString()],
+ ['File', `${P.title} @ ${P.video.videoWidth}x${P.video.videoHeight}`],
+ ['Time', `${P.fCurrentTime()} / ${fTime(P.video.duration)}`],
+ ]
+ .map(([l, t]) => `${l}: ${t}`)
+ .join('
')
+ );
+ break;
+ case 68: // D
+ if (P.elements.danmakuToggle) P.elements.danmakuToggle.click();
+ break;
+ default:
+ break;
}
+ }
+ };
+
+ // ====================================================================== //
+
+ return {
+ elements: [
+ toastBox,
+ new EDC('div') //
+ .class('controls-wrapper')
+ .selfEvents({
+ mousemove: (P) => opacityVisible(P.elements.controls),
+ mouseleave: (P) => {
+ opacityInvisible(P.elements.controls);
+ P.focus();
+ },
+ })
+ .children(
+ new EDC('div', 'controls') //
+ .class('controls box visibility-transition invisible')
+ .playerEvents({
+ fullscreen: (_, E) => opacityInvisible(E),
+ })
+ .children(
+ playToggle,
+ new EDC('div') //
+ .class('volume-wrapper')
+ .children(muteToggle, volumeInput),
+ new EDC('div') //
+ .class('progress-wrapper')
+ .children(progressBar, progressPopup),
+ new EDC('div') //
+ .class('time-label')
+ .selfEvents({
+ mouseleave: (P) => toggleDisplayBi(P.elements.timeCurrent, P.elements.timeInput),
+ })
+ .children(timeInput, timeCurrent, new EDC('span').html(' / '), timeTotal),
+ subtitleToggle,
+ new EDC('div')
+ .condition(hasDanmaku)
+ .class('danmaku-controls')
+ .children(danmakuToggle, danmakuListToggle, danmakuTimeOffset, danmakuSizeOffset),
+ fullscreenToggle
+ )
+ ),
+ danmakuList,
+ new EDC('div')
+ .class('overlays abp')
+ .selfEvents({
+ click: (P) => P.togglePlay(),
+ })
+ .children(subtitleStage, danmakuStage),
+ ],
+ playerEvent: {
+ mousemove: mouseIdle,
+ keydown: hotkeys,
},
- },
-};
+ } as PlayerMetadata;
+})();
Object.defineProperty(window, '__player_metadata__', __player_metadata__);