初始化奇妙小屏幕控制器项目
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
export const CURRENT_DISPLAY_SPEEDS = Object.freeze([0.5, 1, 1.5, 2]);
|
||||
|
||||
export function formatPlaybackTime(valueMs) {
|
||||
const milliseconds = Math.max(0, Math.floor(Number(valueMs) || 0));
|
||||
const minutes = Math.floor(milliseconds / 60000);
|
||||
const seconds = Math.floor((milliseconds % 60000) / 1000);
|
||||
const hundredths = Math.floor((milliseconds % 1000) / 10);
|
||||
return `${minutes}:${String(seconds).padStart(2, "0")}.${String(hundredths).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
export function projectPlaybackPosition(playback, elapsedMs = 0) {
|
||||
if (!playback?.active || !Number.isFinite(Number(playback.total_duration_ms))) return null;
|
||||
const total = Math.max(1, Number(playback.total_duration_ms));
|
||||
const base = Math.max(0, Number(playback.position_ms) || 0);
|
||||
if (playback.paused) return Math.min(total - 1, base);
|
||||
const speed = Number(playback.speed) || 1;
|
||||
const projected = base + Math.max(0, Number(elapsedMs) || 0) * speed;
|
||||
return Math.min(total - 1, projected % total);
|
||||
}
|
||||
|
||||
export class LatestSeekDispatcher {
|
||||
constructor(send, {
|
||||
intervalMs = 100,
|
||||
now = () => performance.now(),
|
||||
setTimer = (callback, delay) => window.setTimeout(callback, delay),
|
||||
clearTimer = (timer) => window.clearTimeout(timer),
|
||||
onError = () => {},
|
||||
} = {}) {
|
||||
this.send = send;
|
||||
this.intervalMs = intervalMs;
|
||||
this.now = now;
|
||||
this.setTimer = setTimer;
|
||||
this.clearTimer = clearTimer;
|
||||
this.onError = onError;
|
||||
this.inFlight = false;
|
||||
this.lastStartedAt = Number.NEGATIVE_INFINITY;
|
||||
this.queued = null;
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
enqueue(positionMs, { flush = false } = {}) {
|
||||
this.queued = { positionMs, flush: flush || Boolean(this.queued?.flush) };
|
||||
this.#drain();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.queued = null;
|
||||
if (this.timer !== null) this.clearTimer(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
#drain() {
|
||||
if (this.inFlight || this.queued === null) return;
|
||||
const waitMs = this.intervalMs - (this.now() - this.lastStartedAt);
|
||||
if (!this.queued.flush && waitMs > 0) {
|
||||
if (this.timer === null) {
|
||||
this.timer = this.setTimer(() => {
|
||||
this.timer = null;
|
||||
this.#drain();
|
||||
}, waitMs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.timer !== null) this.clearTimer(this.timer);
|
||||
this.timer = null;
|
||||
const item = this.queued;
|
||||
this.queued = null;
|
||||
this.inFlight = true;
|
||||
this.lastStartedAt = this.now();
|
||||
Promise.resolve()
|
||||
.then(() => this.send(item.positionMs))
|
||||
.catch((error) => this.onError(error, item.positionMs))
|
||||
.finally(() => {
|
||||
this.inFlight = false;
|
||||
this.#drain();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user