Skip to content

Instantly share code, notes, and snippets.

@robbertkl
Created February 22, 2020 16:42
Show Gist options
  • Save robbertkl/bdf0da32c9a06abfe0a935e77020f034 to your computer and use it in GitHub Desktop.
Save robbertkl/bdf0da32c9a06abfe0a935e77020f034 to your computer and use it in GitHub Desktop.
Script to manipulate subtitles in .srt files or SRT embedded in .mkv
<?php
try {
$usageException = new \RuntimeException("Usage: php {$argv[0]} file [file [...]]");
if ($argc < 2) throw $usageException;
array_shift($argv);
$filenames = $argv;
foreach ($filenames as $filename) {
if (!file_exists($filename)) throw new \RuntimeException('ERROR: file not found');
if (preg_match('/\.srt$/', $filename)) {
fixSrtFile($filename);
echo 'Processed ' . basename($filename) . PHP_EOL;
} else if (preg_match('/\.mkv$/', $filename)) {
fixMkvFile($filename);
echo 'Written new ' . basename($filename) . PHP_EOL;
} else {
throw new \RuntimeException("ERROR: file '{$filename}' is not an SRT or MKV file");
}
}
} catch (\Exception $exception) {
fprintf(STDERR, $exception->getMessage() . PHP_EOL);
exit(1);
}
function fixSrtFile($filename) {
$srt = file_get_contents($filename);
$srt = fixSrt($srt);
file_put_contents($filename, $srt);
}
function fixMkvFile($filename) {
$cmd = 'mkvmerge -i -F verbose-text ' . escapeshellarg($filename);
unset($trackOutput);
exec($cmd, $trackOutput, $status);
if ($status != 0) throw new \RuntimeException('ERROR: could not inspect MKV tracks');
$tracks = [];
$tmpFilename = null;
try {
foreach ($trackOutput as $line) {
if (!preg_match('/^Track ID (\d+): subtitles \(SubRip\/SRT\) \[.*language:(\S+).*\]$/', $line, $matches)) continue;
$track = [
'number' => $matches[1],
'language' => $matches[2],
'srtFile' => tempnam('/tmp', 'srt'),
];
$tracks[] = $track;
$cmd = 'mkvextract tracks ' . escapeshellarg($filename) . ' ' . $track['number'] . ':' . escapeshellarg($track['srtFile']);
exec($cmd, $output, $status);
if ($status != 0) throw new \RuntimeException('ERROR: could not extract SRT track from MKV');
fixSrtFile($track['srtFile']);
echo 'Processed track ' . $track['number'] . ' (' . $track['language'] . ') of ' . basename($filename) . PHP_EOL;
}
$tmpFilename = $filename . '~';
$cmd = 'mkvmerge -o ' . escapeshellarg($tmpFilename) . ' -S ' . escapeshellarg($filename);
foreach ($tracks as $track) {
$cmd .= " --language 0:{$track['language']} {$track['srtFile']}";
}
exec($cmd, $output, $status);
foreach ($tracks as $track) @unlink($track['srtFile']);
rename($tmpFilename, $filename);
} catch (\Exception $exception) {
foreach ($tracks as $track) @unlink($track['srtFile']);
if (!is_null($tmpFilename)) @unlink($tmpFilename);
throw $exception;
}
}
function fixSrt($srt) {
$subs = parseSrt($srt);
$n = count($subs);
$lastUnique = 0;
for ($i = 1; $i < $n; $i++) {
list($time, $text) = $subs[$i];
if ($time == $subs[$lastUnique][0]) {
$subs[$lastUnique][1] .= PHP_EOL . $text;
unset($subs[$i]);
} else {
$lastUnique = $i;
}
}
$subs = array_values($subs);
return getSrt($subs);
}
function parseSrt($srt) {
$subs = [];
$blocks = preg_split('/\n\n/', trim($srt));
foreach ($blocks as $block) {
$lines = explode(PHP_EOL, trim($block));
if ($lines < 3) throw new \RuntimeException('Invalid SRT');
$id = array_shift($lines);
$time = array_shift($lines);
$text = implode(PHP_EOL, $lines);
$subs[] = [$time, $text];
}
return $subs;
}
function getSrt($subs) {
$srt = '';
$i = 1;
foreach ($subs as $sub) {
list($time, $text) = $sub;
$srt .= $i . PHP_EOL;
$srt .= $time . PHP_EOL;
$srt .= $text . PHP_EOL . PHP_EOL;
$i++;
}
return $srt;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment