Files
hyperframes/packages/core/src/runtime/playbackRate.ts
T
Miguel Ángel afafca4b96 feat: make creator media edits render-safe (#3322)
* feat: make creator media edits render-safe

* fix: align media playback timing

* docs: add creator editing recipes

* docs: expand creator editing guidance

* fix: unify media source offsets

* fix: scale natural media duration

* fix: preserve natural media zero spans

* fix: align compiled natural media timing

* test: classify compiler media test as integration

* fix: drop inactive media windows

* fix: unify literal timing parsing

* fix: keep browser media parsing serializable

* fix: keep page timing readers strict

* fix: close remaining preview timing gaps

* fix(core): preserve Studio voice pitch at playback speed

* chore: keep creator contract source-neutral
2026-08-18 10:17:02 -04:00

52 lines
1.8 KiB
TypeScript

export function normalizePlaybackRate(raw: number): number {
return Number.isFinite(raw) && raw > 0 ? Math.max(0.1, Math.min(5, raw)) : 1;
}
/** Parse a literal numeric timing attribute without accepting trailing units or garbage. */
export function parseStrictFiniteTimingNumber(raw: string | null | undefined): number | null {
return parseNumeric(raw);
}
export function readElementPlaybackRate(el: Pick<Element, "getAttribute">): number {
const authored = Number.parseFloat(el.getAttribute("data-playback-rate") ?? "");
const raw =
Number.isFinite(authored) && authored > 0
? authored
: typeof HTMLMediaElement !== "undefined" && el instanceof HTMLMediaElement
? el.defaultPlaybackRate
: 1;
return normalizePlaybackRate(raw);
}
export function readMediaStart(el: Pick<Element, "getAttribute">): number {
const parse = (raw: string | null): number | null => {
const value = parseStrictFiniteTimingNumber(raw);
if (value == null) return null;
return Number.isFinite(value) && value >= 0 ? value : null;
};
return (
parse(el.getAttribute("data-playback-start")) ?? parse(el.getAttribute("data-media-start")) ?? 0
);
}
export function resolveNaturalMediaTimelineDuration(
el: Pick<Element, "getAttribute">,
sourceDuration: number,
): number | null {
return resolveNaturalMediaTimelineDurationFromValues(
sourceDuration,
readMediaStart(el),
readElementPlaybackRate(el),
);
}
export function resolveNaturalMediaTimelineDurationFromValues(
sourceDuration: number,
mediaStart: number,
playbackRate: number,
): number | null {
if (!Number.isFinite(sourceDuration)) return null;
const remaining = Math.max(0, sourceDuration - mediaStart);
return remaining / normalizePlaybackRate(playbackRate);
}
import { parseNumeric } from "./startExpression";