diff --git a/.vscode/settings.json b/.vscode/settings.json
index 52652b8..f23cea0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,7 @@
{
"cSpell.words": ["danmaku", "danmakuload", "stime"],
"conventionalCommits.scopes": ["repo", "utils", "player", "pages", "start"],
+ "prettier.printWidth": 120,
"markdownlint.config": {
"MD001": false,
"MD033": false,
diff --git a/public/index.html b/public/index.html
index 3de3504..12af695 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,10 +2,7 @@
-
+
@@ -15,11 +12,7 @@
|
-
+
|
(Last: None )
@@ -34,11 +27,7 @@
|
Time Offset: |
-
+
|
@@ -69,8 +58,7 @@
return document.getElementById(id);
}
(function () {
- const lastVideoFile =
- window.localStorage.getItem('lastVideoFile');
+ const lastVideoFile = window.localStorage.getItem('lastVideoFile');
if (lastVideoFile) {
id('last-video-file').textContent = lastVideoFile;
}
@@ -82,25 +70,14 @@
window.localStorage.setItem('lastVideoFile', video.name);
const danmaku = id('danmaku-file').files[0];
- const danmakuUrl = danmaku
- ? URL.createObjectURL(danmaku)
- : null;
+ const danmakuUrl = danmaku ? URL.createObjectURL(danmaku) : null;
- const player = new Player(
- id('player'),
- __player_metadata__,
- video.name,
- videoUrl,
- danmakuUrl,
- {
- autoPlay: id('auto-play').checked,
- fullscreen: id('fullscreen').checked,
- muted: id('muted').checked,
- danmakuTimeOffset: parseFloat(
- id('danmaku-time-offset').value
- ),
- }
- );
+ const player = new Player(id('player'), __player_metadata__, video.name, videoUrl, danmakuUrl, {
+ autoPlay: id('auto-play').checked,
+ fullscreen: id('fullscreen').checked,
+ muted: id('muted').checked,
+ danmakuTimeOffset: parseFloat(id('danmaku-time-offset').value),
+ });
window.player = player;
id('start-panel').remove();
id('player').removeAttribute('style');
diff --git a/src/player.main.ts b/src/player.main.ts
index f85bd0f..3772abb 100644
--- a/src/player.main.ts
+++ b/src/player.main.ts
@@ -53,9 +53,7 @@ class Player {
{
this.options.autoPlay ? this.play() : this.pause();
this.options.muted ? this.mute() : this.unmute();
- this.options.fullscreen
- ? this.requestFullscreen()
- : this.setContainerData('fullscreen', false);
+ this.options.fullscreen ? this.requestFullscreen() : this.setContainerData('fullscreen', false);
}
this.constructed = true;
if (this.options.autoPlay) this.toast('Autoplay');
@@ -71,15 +69,9 @@ class Player {
this.setContainerData('paused', this.video.paused);
this.overHour = this.video.duration >= 60 * 60;
});
- this.onVideoEvent('play', () =>
- this.setContainerData('paused', this.video.paused)
- );
- this.onVideoEvent('pause', () =>
- this.setContainerData('paused', this.video.paused)
- );
- this.onVideoEvent('volumechange', () =>
- this.setContainerData('muted', this.video.muted)
- );
+ this.onVideoEvent('play', () => this.setContainerData('paused', this.video.paused));
+ this.onVideoEvent('pause', () => this.setContainerData('paused', this.video.paused));
+ this.onVideoEvent('volumechange', () => this.setContainerData('muted', this.video.muted));
this.onPlayerEvent('fullscreenchange', () => {
const fullscreen = document.fullscreenElement === this.container;
this.setContainerData('fullscreen', fullscreen ? true : false);
@@ -118,9 +110,7 @@ class Player {
this.container.addEventListener(type, listener);
}
firePlayerEvent(type: string, detail?: any) {
- this.container.dispatchEvent(
- new CustomEvent(type, detail ? { detail: detail } : null)
- );
+ this.container.dispatchEvent(new CustomEvent(type, detail ? { detail: detail } : null));
}
toast(html: string) {
if (this.constructed) {
@@ -129,11 +119,7 @@ class Player {
}
seek(time: number) {
const fixedTime = clamp(time, 0, this.video.duration);
- this.toast(
- `Seek: ${fTime(fixedTime, this.overHour)} / ${fTime(
- this.video.duration
- )}`
- );
+ this.toast(`Seek: ${fTime(fixedTime, this.overHour)} / ${fTime(this.video.duration)}`);
this.video.currentTime = fixedTime;
}
seekPercent(percent: number) {
diff --git a/src/player.metadata.ts b/src/player.metadata.ts
index 990deef..f930d73 100644
--- a/src/player.metadata.ts
+++ b/src/player.metadata.ts
@@ -1,18 +1,12 @@
type MetaEventTypes = 'selfEvent' | 'playerEvent' | 'videoEvent';
-type MetaEvents =
- | EventListenerMap
- | (() => EventListenerMap);
+type MetaEvents = EventListenerMap | (() => EventListenerMap);
type ElementMetaEvents = MetaEvents<
(player: Player, element: HTMLElementTagNameMap[T]) => void
>;
type ElementVideoMetaEvents = MetaEvents<
- (
- player: Player,
- element: HTMLElementTagNameMap[T],
- video: HTMLVideoElement
- ) => void
+ (player: Player, element: HTMLElementTagNameMap[T], video: HTMLVideoElement) => void
>;
function bindMetadataEvents( // TODO improve
@@ -112,10 +106,7 @@ class EDC {
{
selfEvent: [element, () => [player, element]],
playerEvent: [player.container, () => [player, element]],
- videoEvent: [
- player.video,
- () => [player, element, player.video],
- ],
+ videoEvent: [player.video, () => [player, element, player.video]],
}
);
for (const childBulder of this._childrenBuilders) {
diff --git a/src/player.player.ts b/src/player.player.ts
index d418ecf..a2c5c3a 100644
--- a/src/player.player.ts
+++ b/src/player.player.ts
@@ -17,10 +17,7 @@ const __player_metadata__: PlayerMetadata = {
E.innerHTML = T.detail.content;
opacityVisible(E);
clearTimeout(P._dyn.toastTimer);
- P._dyn.toastTimer = setTimeout(
- () => opacityInvisible(E),
- 800
- );
+ P._dyn.toastTimer = setTimeout(() => opacityInvisible(E), 800);
},
}),
new EDC('div')
@@ -43,9 +40,7 @@ const __player_metadata__: PlayerMetadata = {
.children(
new EDC('button', 'playToggle') //
.attr({ class: 'play-toggle' })
- .css((s) =>
- toggleByPlayerData('paused', s.attributes.class)
- )
+ .css((s) => toggleByPlayerData('paused', s.attributes.class))
.selfEvents({ click: (P) => P.togglePlay() })
.children(...newSpans('⏵', '⏸')),
new EDC('div') //
@@ -53,18 +48,11 @@ const __player_metadata__: PlayerMetadata = {
.children(
new EDC('button', 'muteToggle')
.attr({ class: 'mute-toggle' })
- .css((s) =>
- toggleByPlayerData(
- 'muted',
- s.attributes.class
- )
- )
+ .css((s) => toggleByPlayerData('muted', s.attributes.class))
.selfEvents({
click: (P) => P.toggleMute(),
})
- .children(
- ...newSpans('Mute', 'Mute')
- ),
+ .children(...newSpans('Mute', 'Mute')),
new EDC('input', 'volume')
.attr({
class: 'volume',
@@ -75,18 +63,14 @@ const __player_metadata__: PlayerMetadata = {
value: '100',
})
.selfEvents({
- input: (P, E) =>
- P.setVolume(E.valueAsNumber / 100),
+ 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
- )),
+ volumechange: (_, E, V) => (E.valueAsNumber = Math.round(V.volume * 100)),
})
),
new EDC('div')
@@ -106,36 +90,23 @@ const __player_metadata__: PlayerMetadata = {
create: (_, E) => (E.valueAsNumber = 0),
change: (P, E) => {
P.seekPercent(E.valueAsNumber);
- opacityInvisible(
- P.elements.progressPopup
- );
+ 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)';
+ 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;
+ const v = V.currentTime / V.duration;
E.valueAsNumber = v ? v : 0;
}
},
@@ -148,16 +119,8 @@ const __player_metadata__: PlayerMetadata = {
new EDC('div')
.attr({ class: 'time-label' })
.selfEvents({
- mouseover: (P) =>
- toggleDisplayBi(
- P.elements.timeInput,
- P.elements.timeCurrent
- ),
- mouseleave: (P) =>
- toggleDisplayBi(
- P.elements.timeCurrent,
- P.elements.timeInput
- ),
+ mouseover: (P) => toggleDisplayBi(P.elements.timeInput, P.elements.timeCurrent),
+ mouseleave: (P) => toggleDisplayBi(P.elements.timeCurrent, P.elements.timeInput),
})
.children(
new EDC('input', 'timeInput')
@@ -171,48 +134,31 @@ const __player_metadata__: PlayerMetadata = {
if (E.validity.valid) {
P.seek(timeToSeconds(E.value));
} else {
- E.value = fTime(
- P.video.currentTime
- );
+ E.value = fTime(P.video.currentTime);
}
},
})
.videoEvents({
canplay: (P, E, V) => {
E.step = P.overHour ? '1' : '0';
- E.value = fTime(
- V.currentTime,
- P.overHour
- );
+ E.value = fTime(V.currentTime, P.overHour);
E.max = fTime(V.duration);
},
timeupdate: (P, E, V) => {
- E.value = fTime(
- V.currentTime,
- P.overHour
- );
+ E.value = fTime(V.currentTime, P.overHour);
},
}),
new EDC('span', 'timeCurrent') //
.html('--:--')
.videoEvents({
- canplay: (P, E, V) =>
- (E.textContent = fTime(
- V.currentTime,
- P.overHour
- )),
- timeupdate: (P, E, V) =>
- (E.textContent = fTime(
- V.currentTime,
- P.overHour
- )),
+ canplay: (P, E, V) => (E.textContent = fTime(V.currentTime, P.overHour)),
+ timeupdate: (P, E, V) => (E.textContent = fTime(V.currentTime, P.overHour)),
}),
new EDC('span').html(' / '),
new EDC('span')
.html('--:--') //
.videoEvents({
- canplay: (_, E, V) =>
- (E.textContent = fTime(V.duration)),
+ canplay: (_, E, V) => (E.textContent = fTime(V.duration)),
})
),
new EDC('div', 'danmaku-controls')
@@ -220,52 +166,33 @@ const __player_metadata__: PlayerMetadata = {
.attr({ class: 'danmaku-controls' })
.children(
new EDC('button', 'danmakuToggle')
- .condition((P) =>
- P.danmakuUrl ? true : false
- )
+ .condition((P) => (P.danmakuUrl ? true : false))
.attr({ class: 'danmaku-toggle' })
- .css((s) =>
- toggleByPlayerData(
- 'danmaku-on',
- s.attributes.class
- )
- )
+ .css((s) => toggleByPlayerData('danmaku-on', s.attributes.class))
.selfEvents({
click: (P) => {
if (!P._dyn.danmakuOn) {
P._dyn.danmakuOn = true;
P.commentManager.start();
- P.setContainerData(
- 'danmakuOn',
- true
- );
+ P.setContainerData('danmakuOn', true);
P.toast('Danmaku On');
} else {
P._dyn.danmakuOn = false;
P.commentManager.clear();
P.commentManager.stop();
- P.setContainerData(
- 'danmakuOn',
- false
- );
+ P.setContainerData('danmakuOn', false);
P.toast('Danmaku Off');
}
},
})
- .children(
- ...newSpans('Danmaku', 'Danmaku')
- ),
+ .children(...newSpans('Danmaku', 'Danmaku')),
new EDC('button', 'danmakuListToggle') //
.html('?') //
.selfEvents({
- click: (P) =>
- toggleDisplay(
- P.elements.danmakuList
- ),
+ click: (P) => toggleDisplay(P.elements.danmakuList),
})
.playerEvents({
- danmakuload: (P, E) =>
- (E.innerHTML = `(${P.commentManager.timeline.length})`),
+ danmakuload: (P, E) => (E.innerHTML = `(${P.commentManager.timeline.length})`),
}),
new EDC('input')
.attr({
@@ -276,26 +203,18 @@ const __player_metadata__: PlayerMetadata = {
})
.selfEvents({
create: (P, E) => {
- if (!P.options.danmakuTimeOffset)
- P.options.danmakuTimeOffset = 0;
- E.valueAsNumber =
- P.options.danmakuTimeOffset;
+ if (!P.options.danmakuTimeOffset) P.options.danmakuTimeOffset = 0;
+ E.valueAsNumber = P.options.danmakuTimeOffset;
},
input: (P, E) => {
- P.options.danmakuTimeOffset =
- E.valueAsNumber;
+ P.options.danmakuTimeOffset = E.valueAsNumber;
P.commentManager.clear();
},
})
),
new EDC('button', 'fullscreenToggle')
.attr({ class: 'fullscreen-toggle' })
- .css((s) =>
- toggleByPlayerData(
- 'fullscreen',
- s.attributes.class
- )
- )
+ .css((s) => toggleByPlayerData('fullscreen', s.attributes.class))
.selfEvents({
click: (P) => P.toggleFullscreen(),
})
@@ -310,16 +229,11 @@ const __player_metadata__: PlayerMetadata = {
.playerEvents({
danmakuload: async (P, E) => {
const timeline = P.commentManager.timeline;
- const overHour = timeline
- ? timeline[timeline.length - 1].stime >= 36e5
- : false;
+ 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
- )}` +
+ `${fTime(data.stime / 1e3, overHour)}` +
`${data.text}`;
}
E.innerHTML = html;
@@ -337,31 +251,19 @@ const __player_metadata__: PlayerMetadata = {
.condition((P) => (P.danmakuUrl ? true : false))
.selfEvents({
create: (P, E) => {
- P.commentManager = initDanmaku(
- E,
- P.danmakuUrl,
- () => P.firePlayerEvent('danmakuload')
- );
+ P.commentManager = initDanmaku(E, P.danmakuUrl, () => P.firePlayerEvent('danmakuload'));
P._dyn.danmakuOn = true;
P.setContainerData('danmakuOn', true);
- if (!P.options.danmakuTimeOffset)
- P.options.danmakuTimeOffset = 0;
+ 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 time = Math.floor(1e3 * (V.currentTime - P.options.danmakuTimeOffset));
const deltaTime = time - cm._lastPosition;
- if (
- deltaTime < 0 ||
- deltaTime > cm.options.seekTrigger
- ) {
+ if (deltaTime < 0 || deltaTime > cm.options.seekTrigger) {
cm.clear();
}
cm.time(time);
@@ -414,15 +316,13 @@ const __player_metadata__: PlayerMetadata = {
break;
case 73: // I
P.toast(
- `${new Date().toLocaleTimeString()}
${fTime(
- P.video.currentTime,
- P.overHour
- )} / ${fTime(P.video.duration)}`
+ `${new Date().toLocaleTimeString()}
${fTime(P.video.currentTime, P.overHour)} / ${fTime(
+ P.video.duration
+ )}`
);
break;
case 68: // D
- if (P.elements.danmakuToggle)
- P.elements.danmakuToggle.click();
+ if (P.elements.danmakuToggle) P.elements.danmakuToggle.click();
break;
default:
break;
diff --git a/src/utils.ts b/src/utils.ts
index 84aa902..d1a20b3 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -12,25 +12,18 @@ interface StrKV {
type AnyFunction = (...args: any[]) => any;
-type AppendArguments = F extends (
- ...arg: [...infer P]
-) => infer R
+type AppendArguments = F extends (...arg: [...infer P]) => infer R
? (...args: [...P, ...A]) => R
: never;
-type PrependArguments = F extends (
- ...arg: [...infer P]
-) => infer R
+type PrependArguments = F extends (...arg: [...infer P]) => infer R
? (...args: [...A, ...P]) => R
: never;
type ExtendedHTMLEventMap = StrAnyKV & HTMLElementEventMap;
type EventListenerMap = {
- [key in keyof ExtendedHTMLEventMap]?: AppendArguments<
- F,
- [ExtendedHTMLEventMap[key]]
- >;
+ [key in keyof ExtendedHTMLEventMap]?: AppendArguments;
};
type HTMLTagNames = keyof HTMLElementTagNameMap;
@@ -115,11 +108,7 @@ function timeToSeconds(time: string): number {
return time.split(':').reduce((acc, time) => 60 * acc + parseInt(time));
}
-function bindEvent(
- target: HTMLElement,
- entries: { [s: string]: Function },
- params: any[]
-) {
+function bindEvent(target: HTMLElement, entries: { [s: string]: Function }, params: any[]) {
for (const [type, listener] of Object.entries(entries)) {
target.addEventListener(type, (event) => listener(...params, event));
}
@@ -127,14 +116,8 @@ function bindEvent(
function initDanmaku(stage: HTMLElement, url: string, onload: () => void) {
const provider = new CommentProvider();
- provider.addStaticSource(
- CommentProvider.XMLProvider('GET', url, null, null),
- CommentProvider.SOURCE_XML
- );
- provider.addParser(
- new BilibiliFormat.XMLParser(),
- CommentProvider.SOURCE_XML
- );
+ provider.addStaticSource(CommentProvider.XMLProvider('GET', url, null, null), CommentProvider.SOURCE_XML);
+ provider.addParser(new BilibiliFormat.XMLParser(), CommentProvider.SOURCE_XML);
const commentManager = new CommentManager(stage);
// @ts-expect-error
provider.addTarget(commentManager);