The chapters payload now carries resume_rewind_seconds (server-side per-user setting); the player applies it at two events, each rewinding once from where playback currently sits — never stacked: - open-resume: restorePosition starts the rewind early from the saved spot (percentage and chapter+offset branches, clamped at the book start). Deep links (?loc=) stay exempt — an explicit jump is not a resume. - unpause: a genuine pause→play steps back again (the re-orientation gap). Programmatic plays — post-restore autoplay and track-switch autoplays — set a suppress flag: their play event is the session start the open-rewind already positioned, not an unpause. Math lives in audio-rewind.ts (pure, unit-tested) after the audio-scrubber pattern; payload values gate through the 0/5/10 allow-list so stale or foreign servers degrade to exact resume.
22 lines
1.1 KiB
TypeScript
22 lines
1.1 KiB
TypeScript
// Resume rewind (per-user server setting): how many seconds before the
|
|
// saved position the audiobook/podcast player starts. Applied on
|
|
// open-resume AND again on every genuine unpause — each event rewinds
|
|
// once from where playback currently sits, never stacked. Pure math;
|
|
// the Alpine component owns when to call it (the play-event suppress
|
|
// flag keeps restore-autoplay and track switches from double-firing).
|
|
|
|
// applyResumeRewind maps a position back by the rewind amount, clamped
|
|
// to the start of the book.
|
|
export function applyResumeRewind(positionSeconds: number, rewindSeconds: number): number {
|
|
if (!Number.isFinite(positionSeconds)) return 0;
|
|
if (!Number.isFinite(rewindSeconds) || rewindSeconds <= 0) return Math.max(0, positionSeconds);
|
|
return Math.max(0, positionSeconds - rewindSeconds);
|
|
}
|
|
|
|
// normalizeResumeRewind gates the server payload value to the setting's
|
|
// allow-list (0/5/10) — anything else means off.
|
|
export function normalizeResumeRewind(value: unknown): number {
|
|
const n = Number(value);
|
|
return n === 5 || n === 10 ? n : 0;
|
|
}
|