diff --git a/services/web-ui/public/js/timecode.js b/services/web-ui/public/js/timecode.js new file mode 100644 index 0000000..e7bebe4 --- /dev/null +++ b/services/web-ui/public/js/timecode.js @@ -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 (0–5) + 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);