Potrzeba matką wynalazków. Tak było i tym razem. Potrzebowałem prostego programiku do przesunięcia napisów z filmu w czasie (wyświetlały się kilka sekund za późno). Znalezione w sieci nie działały prawidłowo.
No i stworzyłem taki prosty program w PHP.
Po kilku miesiącach znów potrzebowałem, jednak okazało się, że tym razem napisy mają inny format. A więc stworzyłem drugi programik.
W końcu postanowiłem się z nim podzielić – może Tobie się przyda?
Skrypt jest dostępny pod adresem napisy.wyremski.pl
Natomiast kod źródłowy pod adresem https://github.com/kamilwyremski/napisy/
Poniżej krótki opis skryptu PHP:
Najpierw sprawdzam czy uploadowany plik jest prawidłowy:
if (
!isset($_FILES['plik']['error']) ||
is_array($_FILES['plik']['error'])
) {
throw new RuntimeException('Nieznany parametr');
}
switch ($_FILES['plik']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('Nie wysłano pliku');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Przekroczono limit wielkości pliku');
default:
throw new RuntimeException('Nieznane błędy');
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
if ($finfo->file($_FILES['plik']['tmp_name']) != 'text/plain'){
throw new RuntimeException('Nieprawidłowy format pliku');
}
Wykorzystałem tutaj fragment kodu ze strony https://www.php.net/manual/en/features.file-upload.php
Następnie otwieram plik i sprawdzam format napisów. Myślałem żeby sprawdzać po prostu pierwszą linijkę ale nie zawsze po pierwszej linijce jest wiadomo w jakim formacie są napisy.
Skrypt obsługuje taki format napisów:
00:00:14:Tutaj jest pierwsza linia tekstu
00:00:20:Tutaj jest druga linia tekstu
Oraz taki:
1 00:00:14,125 --> 00:00:17,916 Tutaj jest pierwsza linia tekstu 2 00:00:20,458 --> 00:00:23,208 Tutaj jest druga linia tekstu
Skrypt do przetwarzania linii z pliku z napisami i ich przesuwania wygląda tak:
$handle = fopen($_FILES['plik']['tmp_name'], "r");
while (($line = fgets($handle)) !== false) {
if(!$subtitle_type){
if(preg_match("/^\d\d:\d\d:\d\d:/",$line)) {
$subtitle_type = 'standard';
}elseif(preg_match("/^\d\d:\d\d:\d\d,\d\d\d --> \d\d:\d\d:\d\d,\d\d\d$/",$line)) {
$subtitle_type = 'arrow';
}
}
if($subtitle_type=='standard'){
$time_original = substr($line, 0, 8);
$seconds = strtotime($time_original);
$time_new = date('H:i:s',$seconds + $_POST['przesuniecie']);
$new_line = str_replace($time_original, $time_new, $line);
$output .= $new_line;
}elseif($subtitle_type=='arrow'){
$match_count = preg_match_all('/(\d\d:\d\d:\d\d),\d\d\d --> (\d\d:\d\d:\d\d),\d\d\d/i', $line, $matches);
if($match_count){
$time_original = strtotime($matches[1][0]);
$time_new = date('H:i:s',$time_original + $_POST['przesuniecie']);
$new_line = str_replace($matches[1][0], $time_new, $line);
$time_original = strtotime($matches[2][0]);
$time_new = date('H:i:s',$time_original + $_POST['przesuniecie']);
$new_line = str_replace($matches[2][0], $time_new, $new_line);
$output .= $new_line;
}else{
$output .= $line;
}
}else{
$output .= $line;
}
}
fclose($handle);
Najpierw próbuje określić format napisów. Jeśli w danej linii nie został znaleziony to jest ona kopiowana do docelowego wyniku. Jeśli został znaleziony to jest odpowiednio przekształcany. Korzystam tutaj z wyrażeń regularnych.
Na końcu wymuszam pobranie wygenerowanych nowych napisów przez przeglądarkę:
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($_FILES['plik']['name']) . "\"");
echo($output);
exit();
Sam formularz wysyłania napisów to standardowy formularz ostylowany w Bootstrap4:
<form action="" method="post" enctype="multipart/form-data" class="col-sm-4"> <div class="form-group"> <label for="plik">Plik z napisami</label> <input type="file" class="form-control-file" id="plik" name="plik" required accept=".txt,.srt"> </div><div class="form-group">
<label for="przesuniecie">Przesunięcie (w sekundach)</label>
<input type="number" class="form-control" id="przesuniecie" name="przesuniecie" required step="1">
</div>
<button type="submit" class="btn btn-primary">Wyślij</button>
</form>
Oczywiście można by były rozbudować aplikację i dodać m.in. obsługę więcej liczby formatów napisów – może chciałbyś w tym pomóc? 😉