// An editing timecode: to the tenth of a second, the precision a cut is marked at (the editor's // segment fields, the clip panel's In / Out). Hours appear only when the item reaches an hour, so a // short's "0:12.4" stays short and a film reads "1:02:03.4". `formatDuration` is the whole-second // clock for a card or a player bar; this one is for a field someone types a cut into. /** `s` as `m:ss.d`, or `h:mm:ss.d` when `hours` (pass `duration >= 3600` so one item's fields agree). */ export function formatTimecode(s: number, hours = false): string { if (!isFinite(s) || s < 0) s = 0; const tenths = Math.round(s * 10); const h = Math.floor(tenths / 36000); const m = Math.floor((tenths % 36000) / 600); const sec = ((tenths % 600) / 10).toFixed(1).padStart(4, "0"); return hours || h > 0 ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`; } /** A typed timecode back to seconds: `h:mm:ss.d`, `m:ss.d` or bare seconds, its decimal a point or * a comma (how a Hungarian hand writes one). Null when it is not one. */ export function parseTimecode(v: string): number | null { const parts = v .trim() .replace(/,(\d*)$/, ".$1") .split(":"); if (parts.length > 3 || parts.some((p) => p.trim() === "")) return null; let total = 0; for (const p of parts) { const n = Number(p); if (!isFinite(n) || n < 0) return null; total = total * 60 + n; } return total; }