Maybe somebody can help me. In bash I don't know how to do that. I need to do a bash-script. At stdin I have .srt file of subtitles in this format:

num
HH:MM:SS,SSS --> HH:MM:SS,SSS
text line 1
text line 2
...

HH:MM:SS,SSS start and finish of title for text.

Script must shift seconds. (it can be + or -)

Example:

$cat bmt.srt
5
00:01:02,323 --> 00:01:05,572
Hello, my frieds!
6
....

$./shifter.sh +3<mbt.srt
5
00:01:05,323 --> 00:01:08,572
Hello, my frieds!
6

I think, I need to grab all HH:MM:SS and convert them to seconds firstly. If somebody can do this without sed, I will applaude. Thank you!

  • mplayer has some subtitle delay options, only I don't know if it can store it as new subtitle; still, I suspect there's an existing solution for this out there somewhere... – frostschutz Oct 17 '13 at 20:15

Unless the subtitle file spans more than 24 hours, you can use date for this:

#!/usr/bin/env bash

set -o errexit -o noclobber -o nounset -o pipefail

date_offset="$1"

shift_date() {
    date --date="$1 $date_offset" +%T,%N | cut -c 1-12
}

while read -r line
do
    if [[ $line =~ ^[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9]\ --\>\ [0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9]$ ]]
    then
        read -r start_date separator end_date <<<"$line"
        new_start_date="$(shift_date "$start_date")"
        new_end_date="$(shift_date "$end_date")"
        printf "%s %s %s\n" "$new_start_date" "$separator" "$new_end_date"
        echo "New date"
    else
        printf "%s\n" "$line"
    fi
done

For some reason you need to use decimal numbers with this, but it works:

$ ./shifter.sh "+3.0 seconds" < bmt.srt
5
00:01:05,323 --> 00:01:08,572
New date
Hello, my frieds!
6

Perl solution. I did not use any classical time handling module, as miliseconds handling is generally poorly supported.

#!/usr/bin/perl
use warnings;
use strict;

use constant FACTORS => (60 * 60 * 1000,
                              60 * 1000,
                                   1000,
                                      1);

sub time2ms {
    my $time = shift;
    my ($ms, $i) = (0, 0);
    $ms += (FACTORS)[$i++] * $_ for split /[^0-9]/, $time;
    return $ms;
}


sub ms2time {
    my $ms = shift;
    my $str = q();
    for my $i (0 .. 3) {
                $str .= sprintf +($i == 3 ? '%03d' : '%02d')
                                    . (':', ':', ',', q())[$i],
                                $ms / (FACTORS)[$i];
        $ms = $ms % (FACTORS)[$i];
    }
    return $str;
}


my $diff   = 1000 * shift;
my $TIME_R = qr/[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}/;
while (<>) {
    if (my ($from, $to) = /($TIME_R) --> ($TIME_R)/) {
        my $i = 0;
        for my $time ($from, $to) {
            $time = time2ms($time) + $diff;
            print ms2time($time), (' --> ', "\n")[$i++];
        }
    } else {
        print;
    }
}
  • Thanks for the script! Let me add a small improvement though: In the ms2time loop, if $i==3, sprintf should be '%03d' instead of '%02d'. – Vangelis Tasoulas Jan 6 '14 at 20:07
  • @VangelisTasoulas: Updated. – choroba Feb 12 '17 at 17:11

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.