初始化奇妙小屏幕控制器项目

This commit is contained in:
2026-09-08 22:56:52 +08:00
commit 8d368de3b5
491 changed files with 67678 additions and 0 deletions
@@ -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();
});
}
}