Site icon Kamil Wyremski

Przesuwanie napisów z filmów w czasie

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? 😉

Exit mobile version