feat(ui): add 59.94 DF timecode utility module

This commit is contained in:
Zac Gaetano 2026-05-18 19:58:34 -04:00
parent 7b8d8d4818
commit 7d8ccc95e9

View file

@ -0,0 +1,77 @@
// services/web-ui/public/js/timecode.js
// 59.94 fps drop-frame timecode utilities.
// Exposes: window.TC
(function (global) {
'use strict';
// 59.94 = 60000/1001
const FPS_EXACT = 60000 / 1001;
const NOM = 60; // nominal integer fps
const DROP = 4; // frames dropped per minute (except every 10th)
const FRAMES_PER_MIN = NOM * 60 - DROP; // 3596
const FRAMES_PER_10MIN = FRAMES_PER_MIN * 10 + DROP; // 35964
const FRAMES_PER_HOUR = FRAMES_PER_10MIN * 6; // 215784
function pad2(n) { return String(Math.floor(n)).padStart(2, '0'); }
/**
* Convert a frame count to a 59.94 DF timecode string: HH:MM:SS;FF
*/
function framesToTC(totalFrames) {
const fc = Math.max(0, Math.round(totalFrames));
const h = Math.floor(fc / FRAMES_PER_HOUR);
let rem = fc % FRAMES_PER_HOUR;
const tm = Math.floor(rem / FRAMES_PER_10MIN); // tens of minutes (05)
rem = rem % FRAMES_PER_10MIN;
let m = 0;
if (rem >= DROP) {
m = Math.floor((rem - DROP) / FRAMES_PER_MIN) + 1;
rem = (rem - DROP) % FRAMES_PER_MIN;
}
const M = tm * 10 + m;
const s = Math.floor(rem / NOM);
const ff = rem % NOM;
return `${pad2(h)}:${pad2(M)}:${pad2(s)};${pad2(ff)}`;
}
/**
* Convert a 59.94 DF timecode string (HH:MM:SS;FF or HH:MM:SS:FF) to frame count.
*/
function tcToFrames(tc) {
if (!tc) return 0;
const clean = String(tc).replace(';', ':');
const parts = clean.split(':').map(Number);
if (parts.length !== 4) return 0;
const [h, m, s, f] = parts;
const totalMinutes = h * 60 + m;
return (NOM * 3600 * h)
+ (NOM * 60 * m)
+ (NOM * s)
+ f
- DROP * (totalMinutes - Math.floor(totalMinutes / 10));
}
/**
* Convert seconds (float) frame count at 59.94.
*/
function secondsToFrames(seconds) {
return Math.round(seconds * FPS_EXACT);
}
/**
* Convert frame count seconds (float) at 59.94.
*/
function framesToSeconds(frames) {
return frames / FPS_EXACT;
}
global.TC = {
framesToTC,
tcToFrames,
secondsToFrames,
framesToSeconds,
FPS: FPS_EXACT,
};
})(window);