Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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!

share|improve this question
 
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
add comment

2 Answers

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
share|improve this answer
add comment

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 '%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;
    }
}
share|improve this answer
 
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 at 20:07
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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