Created
February 4, 2020 17:48
-
-
Save rwaldron/6e8f91b7a92a14ef0f1e12e7c8ded356 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const tsToSeconds = (timestamp, framerate) => { | |
/* | |
Parse a timestamp into seconds | |
Acceptable formats are: | |
- HH:MM:SS.MMM | |
- MM:SS.MMM | |
- SS.MMM | |
- HH:MM:SS;FF | |
- MM:SS;FF | |
- SS;FF | |
Hours and minutes are optional. Default to 0 | |
Seconds MUST be specified | |
Seconds can be followed by milliseconds OR by the frame number | |
*/ | |
const RADIX = 10; | |
const validformat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/; | |
let first; | |
let last; | |
let lastIndex; | |
let pairs; | |
if (typeof timestamp === "number") { | |
return timestamp; | |
} | |
if (typeof timestamp === "string" && | |
!validformat.test(timestamp)) { | |
throw new Error(` | |
Invalid time format. | |
'${timestamp}' must be formatted as any one of: | |
- HH:MM:SS.MMM | |
- MM:SS.MMM | |
- SS.MMM | |
- HH:MM:SS;FF | |
- MM:SS;FF | |
- SS;FF | |
`.trim()); | |
} | |
pairs = timestamp.split(":"); | |
lastIndex = pairs.length - 1; | |
last = pairs[lastIndex]; | |
// Fix last element: | |
if (last.includes(";")) { | |
if (!framerate || typeof framerate !== "number") { | |
throw new Error("Missing or invalid 'framerate' argument."); | |
} | |
let [ss, ff] = last.split(";"); | |
let SS = parseInt(ss, RADIX); | |
let FF = parseInt(ff, RADIX); | |
let frameAsTime = 0; | |
if (FF > framerate) { | |
throw new Error(` | |
Frame number is greater than framerate: | |
- Frame: ${FF} | |
- Framerate: ${framerate} | |
`.trim()); | |
} | |
pairs[lastIndex] = SS + (FF / framerate); | |
} | |
first = pairs[0]; | |
return [ | |
/* intentionally empty */, | |
parseFloat(first, RADIX), | |
(parseInt(first, RADIX) * 60) + | |
parseFloat(pairs[1], RADIX), | |
(parseInt(first, RADIX) * 3600) + | |
(parseInt(pairs[1], RADIX) * 60) + | |
parseFloat(pairs[2], RADIX) | |
][pairs.length || 1]; | |
}; | |
// ------------- TESTING --------------- | |
// | |
const tolerance = 0.0001; | |
const framerate = 24; | |
const tests = [ | |
[0.333, 0.333], | |
[5.333, 5.333], | |
[6, 6.000], | |
[6.004, 6.004], | |
[9.11111111, 9.11111111], | |
// *SS.MMM | |
["4.003", 4.003], | |
["01.234", 1.234], | |
["6.78", 6.780], | |
["8.090", 8.090], | |
// SS;FF | |
["10;4", 10.1666], | |
["10;17", 10.7083], | |
["12;1", 12.0416], | |
["13;2", 13.0833], | |
["20;11", 20.4583], | |
["23;17", 23.7083], | |
["27;7", 27.2916], | |
["27;22", 27.9166], | |
// HH:MM:SS;FF | |
["12:04;12", 724.5], | |
["22:59;23", 1379.9583], | |
["1:48:27;9", 6507.375], | |
["3:23:15;1", 12195.0416], | |
["12:56;7", 776.2916], | |
["12:56;7", 776.2916], | |
["2:02:42;8", 7362.3333], | |
]; | |
const fuzzyEquivalent = (a, b, tolerance = 0.0001) => { | |
return (a < (b + tolerance)) && (a > (b - tolerance)); | |
}; | |
const evaluate = (a, b, t) => fuzzyEquivalent(a, b, t) ? "PASSED" : "FAILED"; | |
const message = (input, expect, output) => | |
`Conversion of '${input}' should be ${expect} ±${tolerance}. (Received: ${output})`; | |
for (let [input, expect] of tests) { | |
const output = tsToSeconds(input, framerate); | |
const result = evaluate(output, expect, tolerance); | |
console.log(result, message(input, expect, output)); | |
} | |
const trythese = [ | |
["Frame number is greater than framerate", () => tsToSeconds("10;25", framerate)], | |
["Invalid time format", () => tsToSeconds("")], | |
["Missing or invalid 'framerate' argument.", () => tsToSeconds("00:00;1")], | |
]; | |
for (let [expect, tryable] of trythese) { | |
try { | |
tryable(); | |
} catch (error) { | |
let result = 'FAILED'; | |
if (error.message.includes(expect)) { | |
result = 'PASSED'; | |
} | |
console.log(`${result} ${error}`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment