Skip to content

Instantly share code, notes, and snippets.

@divinity76
Last active March 12, 2022 18:24
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 divinity76/5ad23dbf9b3de711ec03915b50c18224 to your computer and use it in GitHub Desktop.
Save divinity76/5ad23dbf9b3de711ec03915b50c18224 to your computer and use it in GitHub Desktop.
php subtitle tools
<?php
declare(strict_types=1);
class Subtitle_tools
{
private static function convertToCygpath(string $path): string
{
if (PHP_OS !== 'CYGWIN') {
return $path;
}
$cygpath = shell_exec("cygpath " . escapeshellarg($path));
return substr($cygpath, 0, -strlen("\n"));
}
private static function convertToWinpath(string $path): string
{
if (PHP_OS !== 'CYGWIN') {
return $path;
}
$cygpath = shell_exec("cygpath -w " . escapeshellarg($path));
return substr($cygpath, 0, -strlen("\n"));
}
private static function fixDirPath(string $path): string
{
$path = self::convertToCygpath($path);
$lastChar = substr($path, -1);
if ($lastChar !== '/' && $lastChar !== '\\') {
$path .= '/';
}
return str_replace('\\', '/', $path);
}
public static function getSubtitlesFromDirectory(string $directory): array
{
$directory = self::fixDirPath($directory);
$ret = array();
$files = scandir($directory);
foreach ($files as $file) {
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (in_array($ext, array('srt', 'sub'), true)) {
$ret[] = $directory . $file;
}
}
natsort($ret);
return $ret;
}
public static function getVideoFilesFromDirectory(string $directory): array
{
$directory = self::fixDirPath($directory);
$ret = array();
$files = scandir($directory);
foreach ($files as $file) {
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
if (in_array($ext, array('avi', 'mkv', 'mp4', 'mpg', 'mpeg', 'wmv'), true)) {
$ret[] = $directory . $file;
}
}
natsort($ret);
return $ret;
}
public static function fixSubtitleNames(string $directory): void
{
$videos = self::getVideoFilesFromDirectory($directory);
$subs = self::getSubtitlesFromDirectory($directory);
if (count($videos) !== count($subs)) {
throw new \Exception('Number of videos and subtitles do not match. found ' . count($videos) . ' videos and ' . count($subs) . ' subtitles');
}
foreach ($videos as $index => $video) {
$sub = $subs[$index];
$subExtension = pathinfo($sub, PATHINFO_EXTENSION);
$newSub = $video . '.' . $subExtension;
if ($newSub !== $sub) {
echo "Renaming $sub to $newSub\n";
rename($sub, $newSub);
}
}
}
public static function adjustSubtitleTimestamps(string $dir, float $relative_adjust): void
{
$subs = self::getSubtitlesFromDirectory($dir);
foreach ($subs as $sub) {
echo "doing {$sub}\n";
$ext = strtolower(pathinfo($sub, PATHINFO_EXTENSION));
if ($ext === "srt") {
$content = file_get_contents($sub);
if ($content === false) {
throw new \RuntimeException("failed to fetch {$sub}");
}
// 00:00:24,024 --> 00:00:26,824
$adjusted = preg_replace_callback('/(?<hour>\d{2})\:(?<minute>\d{2})\:(?<second>\d{2})(?:\,(?<subseconds>\d+))?/m', function ($matches) use ($relative_adjust) {
//var_dump($matches);die();
$formatNum = function ($num, int $pad) {
return str_pad((string)$num, $pad, '0', STR_PAD_LEFT);
};
$hour = $matches['hour'];
$minute = $matches['minute'];
$second = $matches['second'];
$subseconds = (float)("0." . ((string)($matches['subseconds'] ?? 0)));
$input = $formatNum($hour, 2) . ':' . $formatNum($minute, 2) . ':' . $formatNum($second, 2) . ',' . $formatNum($subseconds * 1000, 0);
$total = ($hour * 3600) + ($minute * 60) + $second + ($subseconds);
$total += $relative_adjust;
$hour = floor($total / 3600);
$total = $total - ($hour * 3600);
$minute = floor($total / 60);
$total = $total - ($minute * 60);
$second = floor($total);
$total = $total - $second;
$subseconds = $total;
$output = $formatNum($hour, 2) . ':' . $formatNum($minute, 2) . ':' . $formatNum($second, 2) . ',' . $formatNum($subseconds * 1000, 0);
if (0) {
var_dump(["input" => $input, "output" => $output, "equal" => $input === $output]);
die();
}
return $output;
}, $content, -1, $count);
file_put_contents($sub, $adjusted, LOCK_EX);
} else {
throw new \RuntimeException("unsupported subtitle format: {$ext}");
}
}
}
}
$path = 'C:\Users\hans\Downloads\The Blacklist Season 2 Complete 720P Bluray x264 [i_c]';
if (0) {
var_dump(
Subtitle_tools::getSubtitlesFromDirectory($path),
Subtitle_tools::getVideoFilesFromDirectory($path),
);
}
var_dump(Subtitle_tools::fixSubtitleNames($path));
var_dump(Subtitle_tools::adjustSubtitleTimestamps($path, +0.5));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment