Last active
November 21, 2016 19:10
-
-
Save mirkonasato/98412f65df28be90efd056346c38752f to your computer and use it in GitHub Desktop.
Concatenates multiple srt files together, adjusting the times
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
#!/usr/bin/env node | |
// | |
// Concatenates multiple srt files together, adjusting the times | |
// | |
// Requires ffprobe and an mp4 file along with each srt file to calculate time offsets | |
// | |
// Usage: cat-srt.js video1.srt video2.srt video3.srt output.srt | |
// | |
const fs = require('fs'); | |
const path = require('path'); | |
const execSync = require('child_process').execSync; | |
const BOM = '\uFEFF'; | |
const infiles = process.argv.slice(2, -1); | |
const outfile = process.argv[process.argv.length - 1]; | |
const TIME_PATTERN = /^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/; | |
function getStreamDuration(file) { | |
const seconds = execSync( | |
`ffprobe -v error -of default=noprint_wrappers=1:nokey=1 -show_entries format=duration ${file}` | |
); | |
return Math.floor(parseFloat(seconds.toString()) * 1000); | |
} | |
function toMillis(timeStr) { | |
const groups = TIME_PATTERN.exec(timeStr); | |
const hours = parseInt(groups[1]); | |
const minutes = parseInt(groups[2]); | |
const seconds = parseInt(groups[3]); | |
const milliseconds = parseInt(groups[4]); | |
return milliseconds + seconds * 1000 + minutes * 60 * 1000 + hours * 60 * 60 * 1000; | |
} | |
function zeroPad(n, digits) { | |
return ('0'.repeat(digits) + n).slice(-digits); | |
} | |
function toTimeStr(millis) { | |
const milliseconds = zeroPad(millis % 1000, 3); | |
const seconds = zeroPad(Math.floor(millis / 1000) % 60, 2); | |
const minutes = zeroPad(Math.floor(millis / (60 * 1000)) % 60, 2) | |
const hours = zeroPad(Math.floor(millis / (60 * 60 * 1000)), 2); | |
return `${hours}:${minutes}:${seconds},${milliseconds}`; | |
} | |
function shiftTimes(line, offset) { | |
return line.split(' --> ') | |
.map(s => toMillis(s.trim())) | |
.map(t => t + offset) | |
.map(toTimeStr) | |
.join(' --> '); | |
} | |
const output = []; | |
let slotId = 1; | |
let offset = 0; | |
for (file of infiles) { | |
const duration = getStreamDuration(path.basename(file, '.srt') + '.mp4'); | |
let input = fs.readFileSync(file, { encoding: 'utf8' }); | |
if (input.charAt(0) === BOM) { | |
input = input.substring(1); | |
} | |
const lines = input.split('\n'); | |
for (let i = 0; i < lines.length; i += 4) { | |
if (/^\d+$/.test(lines[i])) { | |
output.push(slotId++); | |
output.push(shiftTimes(lines[i + 1], offset)); | |
output.push(lines[i + 2] || '…'); // replace empty text to avoid issues | |
output.push(lines[i + 3]); | |
} | |
} | |
offset += duration; | |
} | |
fs.writeFileSync(outfile, BOM + output.join('\n') + '\n', { encoding: 'utf8' }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment