Apply a global ms time shift to an .srt file
#!/bin/sh
##
## takes three arguments - the .srt filename and a
## ms shift value with possibly negative sign.
##
## if a third argument is given of the form
## 2/1000
## then it will be taken as a PROGRESSIVE time
## adjustment applied at the rate of, for the
## example, 2 milliseconds later per second,
## effectively speeding up or slowing down the
## subtitle track.
## A progressive option of 0/1000 is considered
## a no-op and can be used as a placeholder if a
## RANGE of lines is specified as a 4th option.
##
## A subrange of entries can be operated on by
## specifying a line range as the fourth option.
## For example:
##
## srtshifter my-subs.srt -5000 0/1000 312-400
##
## Would cause ONLY lines 312-400 (inclusive) to
## be displayed 5 seconds earlier.
##
## all arguments must use integer values!
##
## I am told that there are people who edit the
## headers of container files to change the frame
## rate, and they are the folks who need to apply
## a speedup or slowdown to account for the slightly
## faster/slower video produced by that heinous
## act.
##
## Note that there is something called "extended srt"
## that looks like this:
##
## 00:00:14,948 --> 00:00:18,247 X1:201 X2:516 Y1:397 Y2:423
## <font color="#fbff1c">Whose side is time on?</font>
##
## Phil Ehrens <phil@slug.org>
## The next line tells sh to execute the script using tclsh \
exec tclsh "$0" ${1+"$@"}
set file [ lindex $argv 0 ]
set shift [ lindex $argv 1 ]
set progressive [ lindex $argv 2 ]
set range [ lindex $argv 3 ]
if { ! [ string length $progressive ] } {
set progressive 0/1000
}
set rstart 1
set rend 10000
regexp {(\d+)-(\d+)} $range -> rstart rend
set fid [ open $file r ]
set data [ read $fid [ file size $file ] ]
close $fid
set fid [ open $file.out w ]
set time_rx {^(\S+)\s+-->\s+(\S+)$}
proc timestamp { msecs } {
set timeatoms [ list ]
if { [ catch {
foreach div { 3600000 60000 1000 1 } \
mod { 24 60 60 1000 } {
set n [ expr {$msecs / $div} ]
if { $mod > 0 } { set n [ expr {$n % $mod} ] }
if { [ string equal 0 $n ] } { set n 00 }
if { [ string length $n ] == 1 } { set n 0$n }
lappend timeatoms $n
}
set stamp [ join [ lrange $timeatoms 0 2 ] : ]
set ms [ lindex $timeatoms 3 ]
if { [ string length $ms ] == 2 } { set ms 0$ms }
set stamp $stamp,$ms
} err ] } {
return -code error "timestamp: $err"
}
return $stamp
}
proc millify { h m s ms } {
foreach t [ list h m s ms ] f [ list 3600000 60000 1000 1 ] {
if { [ set $t ] } {
set $t [ expr [ string trimleft [ set $t ] 0 ] * $f ]
}
}
set result [ expr { $h + $m + $s + $ms } ]
return $result
}
proc timeshift { shift start end progressive } {
foreach [ list num den ] [ split $progressive / ] { break }
foreach [ list h m s ms ] [ split $start ":," ] { break }
set start [ millify $h $m $s $ms ]
set spshift [ expr { $num * ($start / $den) } ]
set start [ expr {$start + $shift + $spshift} ]
foreach [ list h m s ms ] [ split $end ":," ] { break }
set end [ millify $h $m $s $ms ]
set epshift [ expr { $num * ($end / $den) } ]
set end [ expr {$end + $shift + $epshift} ]
set start [ timestamp $start ]
set end [ timestamp $end ]
set line "$start --> $end"
return $line
}
foreach line [ split $data "\n" ] {
if { [ regexp {^\d+$} $line ] } {
set index $line
}
if { $index >= $rstart && \
$index <= $rend && \
[ regexp -- $time_rx $line -> start end ] } {
set line [ timeshift $shift $start $end $progressive ]
}
puts $fid $line
}
exit
See also
ssa2srt
See also srtinterpolate