Skip to content

Instantly share code, notes, and snippets.

@duzun
Last active February 7, 2020 10:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duzun/bbfc015ecc44618e134b9b0e8f731375 to your computer and use it in GitHub Desktop.
Save duzun/bbfc015ecc44618e134b9b0e8f731375 to your computer and use it in GitHub Desktop.
Shift time in .srt subtitles file
#!/bin/node
/*jshint esversion: 9*/
/**
* Time-shift video subtitles in a .srt file.
*
* Usage:
* shiftsrt.js seconds input_file|- [output_file|-]
*
* Examples:
* shiftsrt.js 13 movie.srt movie-shifted.srt
* shiftsrt.js -13 movie.srt # -> movie_(-13).srt
* shiftsrt.js -13 movie.srt - | grep -P '^\d+:\d+:\d+' # output only shifted time intervals
* grep -P '^\d+:\d+:\d+' movie.srt | shiftsrt.js 13 - > movie-13.srt # input only time intervals
*
* @gist https://gist.github.com/duzun/bbfc015ecc44618e134b9b0e8f731375
* @author Dumitru Uzun (https://DUzun.Me)
* @version 1.0.0
*/
const fs = require('fs');
const path = require('path');
const timeReg = /^([0-9]{2,2})\:([0-9]{2,2})\:([0-9]{2,2}),([0-9]{0,3})/;
const timeShift = parseInt(process.argv[2]);
const fn = process.argv[3];
let destFn = process.argv[4];
if(!destFn) {
if(fn === '-') {
destFn = '-';
}
else {
destFn = path.parse(fn);
destFn = path.join(destFn.dir, `${destFn.name}_(${timeShift})${destFn.ext}`);
}
}
log(`${fn} -> ${destFn}`);
const _in = fn === '-' ? process.stdin : fs.createReadStream(fn, 'utf8');
const out = destFn === '-' ? process.stdout : fs.createWriteStream(destFn);
let buf = '';
let counter = 0;
_in.on('data', (data) => {
let ln = (buf + data).split('\n');
buf = ln.pop();
out.write(ln.map(mapLine).join('\n') + '\n');
});
_in.on('end', () => {
if(buf.length); {
out.write(mapLine(buf));
}
if(!out._isStdio) {
out.close();
}
log(`${counter} rows transformed`);
});
function log(...args) {
if(destFn !== '-') {
console.log(...args);
}
}
function mapLine(ln) {
if(!ln.trim() || !timeReg.test(ln)) return ln;
++counter;
return ln.split(' ').map((str) => {
let n = readTime(str);
if(!n) return str;
return writeTime(n + timeShift);
}).join(' ');
}
function readTime(str) {
let m = str.match(timeReg);
if(!m) return;
return m[1] * 60 * 60 + m[2] * 60 + +m[3] + (m[4]||0) / 1000;
}
function writeTime(num) {
let msec = (num * 1000 % 1000) | 0;
let sec = (num % 60) | 0;
num = (num / 60) | 0;
let min = num % 60;
num = (num / 60) | 0;
let hours = num;
return `${pad(hours)}:${pad(min)}:${pad(sec)},${pad(msec,3)}`;
}
function pad(n, len=2) {
return String(n).padStart(len,'0');
}
/* movie.srt = <<EOF
1
00:01:38,240 --> 00:01:39,850
We're screwed.
2
00:01:39,950 --> 00:01:41,590
No more free Wi-Fi.
3
00:01:43,020 --> 00:01:44,020
Hey, Ki-jung!
EOF
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment