Sync subtitles: Difference between revisions

→‎Less brutal: use grammar actions and add encapsulations
m (→‎{{header|Phix}}: and a floor(milliseconds) issue.)
(→‎Less brutal: use grammar actions and add encapsulations)
 
(16 intermediate revisions by 7 users not shown)
Line 70:
</pre>
 
;References
<br><br>
 
:* Wikipedia: [[wp:SubRip|SubRip]]
 
=={{header|Amazing Hopper}}==
Line 155 ⟶ 157:
12
00:02:16,503 --> 00:02:20,048
With our advanced technology, I shrank it down.
 
</pre>
 
=={{header|BASIC}}==
==={{header|BASIC256}}===
{{trans|FreeBASIC}}
<syntaxhighlight lang="vbnet">f = freefile
print "After fast-forwarding 9 seconds:" + chr(10)
call syncSubtitles("movie.srt", "movie_corrected.srt", 9)
open f, "movie_corrected.srt"
while not eof(f)
linea = readline (f)
print linea;
end while
close
 
print chr(10) + chr(10)
print "After rolling-back 9 seconds:" + chr(10)
call syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
open f, "movie_corrected2.srt"
while not eof(f)
linea = readline (f)
print linea;
end while
close
end
 
function addSeconds (timestring, secs)
hh = int(mid(timestring, 1, 2))
mm = int(mid(timestring, 4, 2))
ss = int(mid(timestring, 7, 2)) + secs
ttt = int(mid(timestring, 10, 3))
 
while ss < 0
ss = ss + 60
mm = mm - 1
end while
while mm < 0
mm = mm + 60
hh = hh - 1
end while
while hh < 0
hh = hh + 24
end while
 
mm = mm + ss \ 60
hh = hh + mm \ 60
ss = ss mod 60
mm = mm mod 60
hh = hh mod 24
return right("0" + string(hh), 2) + ":" + right("0" + string(mm), 2) + ":" + right("0" + string(ss), 2) + "," + right("000" + string(ttt), 3)
end function
 
subroutine syncSubtitles (fileIn, fileOut, secs)
fmt = "hh:MM:ss,ttt"
f1 = freefile
open f1, fileOut
f2 = freefile
open f2, fileIn
while not eof(f2)
linea = readline (f2)
if instr(linea, "-->") > 0 then
pio = mid(linea, 1, 12)
pio = addSeconds(pio, secs)
fin = mid(linea, 18, 12)
fin = addSeconds(fin, secs)
write f1, pio; " --> "; fin + chr(10)
else
write f1, linea
end if
end while
close
end subroutine</syntaxhighlight>
 
==={{header|Chipmunk Basic}}===
{{trans|FreeBASIC}}
{{works with|Chipmunk Basic|3.6.4}}
<syntaxhighlight lang="vbnet">100 cls
110 print "After fast-forwarding 9 seconds:";chr$(10)
120 syncsubtitles("movie.srt","movie_corrected.srt",9)
130 open "movie_corrected.srt" for input as #1
140 while not eof(1)
150 line input #1,linea$
160 print linea$
170 wend
180 close #1
190 print
200 print "After rolling-back 9 seconds:";chr$(10)
210 syncsubtitles("movie.srt","movie_corrected2.srt",-9)
220 open "movie_corrected2.srt" for input as #1
230 while not eof(1)
240 line input #1,linea$
250 print linea$
260 wend
270 close #1
280 end
290 sub addseconds$(timestr$,secs)
300 hh = val(mid$(timestr$,1,2))
310 mm = val(mid$(timestr$,4,2))
320 ss = val(mid$(timestr$,7,2))+secs
330 ttt = val(mid$(timestr$,10,3))
340 while ss < 0
350 ss = ss+60
360 mm = mm-1
370 wend
380 while mm < 0
390 mm = mm+60
400 hh = hh-1
410 wend
420 while hh < 0
430 hh = hh+24
440 wend
450 mm = mm+int(ss/60)
460 hh = hh+int(mm/60)
470 ss = ss mod 60
480 mm = mm mod 60
490 hh = hh mod 24
500 addseconds$ = right$("0"+str$(hh),2)+":"+right$("0"+str$(mm),2)+":"+right$("0"+str$(ss),2)+","+right$("000"+str$(ttt),3)
510 end sub
520 sub syncsubtitles(filein$,fileout$,secs)
530 fmt$ = "hh:MM:ss,ttt"
540 open fileout$ for output as #1
550 open filein$ for input as #2
560 while not eof(2)
570 line input #2,linea$
580 if instr(linea$,"-->") > 0 then
590 pio$ = mid$(linea$,1,12)
600 pio$ = addseconds$(pio$,secs)
610 fin$ = mid$(linea$,18,12)
620 fin$ = addseconds$(fin$,secs)
630 print #1,pio$;" --> ";fin$
640 else
650 print #1,linea$
660 endif
670 wend
680 close #2
690 close #1
700 end sub</syntaxhighlight>
 
==={{header|FreeBASIC}}===
{{trans|Wren}}
<syntaxhighlight lang="vbnet">Function addSeconds(timeStr As String, secs As Integer) As String
Dim As Integer hh, mm, ss, ttt
hh = Val(Mid(timeStr, 1, 2))
mm = Val(Mid(timeStr, 4, 2))
ss = Val(Mid(timeStr, 7, 2)) + secs
ttt = Val(Mid(timeStr, 10, 3))
While ss < 0
ss += 60
mm -= 1
Wend
While mm < 0
mm += 60
hh -= 1
Wend
While hh < 0
hh += 24
Wend
mm += ss \ 60
hh += mm \ 60
ss Mod= 60
mm Mod= 60
hh Mod= 24
Return Right("0" & Str(hh), 2) & ":" & Right("0" & Str(mm), 2) & ":" & _
Right("0" & Str(ss), 2) & "," & Right("000" & Str(ttt), 3)
End Function
 
Sub syncSubtitles(fileIn As String, fileOut As String, secs As Integer)
Dim As String linea, pio, fin, fmt
Dim As Ubyte f1, f2
fmt = "hh:MM:ss,ttt"
f1 = Freefile
Open fileOut For Output As #f1
f2 = Freefile
Open fileIn For Input As #f2
While Not Eof(f2)
Line Input #f2, linea
If Instr(linea, "-->") > 0 Then
pio = Mid(linea, 1, 12)
pio = addSeconds(pio, secs)
fin = Mid(linea, 18, 12)
fin = addSeconds(fin, secs)
Print #f1, pio; " --> "; fin
Else
Print #f1, linea
End If
Wend
Close #f2, #f1
End Sub
 
Dim As String linea
Dim As Ubyte f = Freefile
 
Print !"After fast-forwarding 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected.srt", 9)
Open "movie_corrected.srt" For Input As #f
While Not Eof(f)
Line Input #f, linea
Print linea
Wend
Close #f
 
Print !"\n\nAfter rolling-back 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
Open "movie_corrected2.srt" For Input As #f
While Not Eof(f)
Line Input #f, linea
Print linea
Wend
Close #f
 
Sleep</syntaxhighlight>
{{out}}
<pre>Same as Wren entry.</pre>
 
==={{header|Gambas}}===
{{trans|FreeBASIC}}
<syntaxhighlight lang="vbnet">Function addSeconds(timeStr As String, secs As Integer) As String
 
Dim hh As Integer, mm As Integer, ss As Integer, ttt As Integer
hh = Val(Mid(timeStr, 1, 2))
mm = Val(Mid(timeStr, 4, 2))
ss = Val(Mid(timeStr, 7, 2)) + secs
ttt = Val(Mid(timeStr, 10, 3))
While ss < 0
ss += 60
mm -= 1
Wend
While mm < 0
mm += 60
hh -= 1
Wend
While hh < 0
hh += 24
Wend
mm += ss \ 60
hh += mm \ 60
ss = ss Mod 60
mm = mm Mod 60
hh = hh Mod 24
Return Right("0" & Str(hh), 2) & ":" & Right("0" & Str(mm), 2) & ":" & Right("0" & Str(ss), 2) & "," & Right("000" & Str(ttt), 3)
 
End Function
 
Sub syncSubtitles(fileIn As String, fileOut As String, secs As Integer)
 
Dim linea As String, pio As String, fin As String, fmt As String
Dim f1 As File, f2 As File
fmt = "hh:MM:ss,ttt"
f1 = Open fileOut For Output
f2 = Open fileIn For Input
While Not Eof(f2)
Line Input #f2, linea
If InStr(linea, "-->") > 0 Then
pio = Mid(linea, 1, 12)
pio = addSeconds(pio, secs)
fin = Mid(linea, 18, 12)
fin = addSeconds(fin, secs)
Print #f1, pio; " --> "; fin
Else
Print #f1, linea
End If
Wend
Close #f1
Close #f2
 
End Sub
 
Public Sub Main()
Dim linea As String
Dim f As File
Print "After fast-forwarding 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected.srt", 9)
f = Open "movie_corrected.srt" For Input
While Not Eof(f)
Line Input #f, linea
Print linea
Wend
Close #f
Print "\n\nAfter rolling-back 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
f = Open "movie_corrected2.srt" For Input
While Not Eof(f)
Line Input #f, linea
Print linea
Wend
Close #f
End </syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
==={{header|PureBasic}}===
{{trans|FreeBASIC}}
<syntaxhighlight lang="purebasic">Procedure.s addSeconds(timeStr.s, secs)
Protected hours, minutes, seconds, milliseconds
Protected.s hh, mm, ss, ttt
hh = Mid(timeStr, 1, 2)
hours = Val(hh)
mm = Mid(timeStr, 4, 2)
minutes = Val(mm)
ss = Mid(timeStr, 7, 2)
seconds = Val(ss)
seconds + secs
ttt = Mid(timeStr, 10, 3)
If seconds < 0
seconds + 60
minutes - 1
EndIf
If minutes < 0
minutes + 60
hours - 1
EndIf
If hours < 0
hours + 24
EndIf
ProcedureReturn Str(hours) + ":" + Str(minutes) + ":" + Str(seconds) + "," + ttt
EndProcedure
 
Procedure syncSubtitles(fileIn.s, fileOut.s, secs)
Protected line.s, pio.s, fin.s
OpenFile(0, fileIn)
CreateFile(1, fileOut)
While Not Eof(0)
line = ReadString(0)
If FindString(line, "-->", 1)
pio = Mid(line, 1, 12)
fin = Mid(line, 18, 12)
pio = addSeconds(pio, secs)
fin = addSeconds(fin, secs)
WriteStringN(1, pio + " --> " + fin)
Else
WriteStringN(1, line)
EndIf
Wend
CloseFile(0)
CloseFile(1)
EndProcedure
 
OpenConsole()
PrintN("After fast-forwarding 9 seconds:" + #CRLF$)
syncSubtitles("movie.srt", "movie_corrected.srt", 9)
OpenFile(0, "movie_corrected.srt")
While Not Eof(0)
PrintN(ReadString(0))
Wend
CloseFile(0)
 
PrintN(#CRLF$ + #CRLF$ + "After rolling-back 9 seconds:" + #CRLF$)
syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
OpenFile(0, "movie_corrected2.srt")
While Not Eof(0)
PrintN(ReadString(0))
Wend
CloseFile(0)
 
PrintN(#CRLF$ + "Press ENTER to exit"): Input()
CloseConsole()</syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
==={{header|QB64}}===
<syntaxhighlight lang="vbnet">Dim As String linea
f = FreeFile
 
Print "After fast-forwarding 9 seconds:"; Chr$(10)
Call syncSubtitles("movie.srt", "movie_corrected.srt", 9)
Open "movie_corrected.srt" For Input As #f
While Not EOF(f)
Line Input #f, linea
Print linea
Wend
Close #f
 
Print Chr$(10); Chr$(10); "After rolling-back 9 seconds:"; Chr$(10)
Call syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
Open "movie_corrected2.srt" For Input As #f
While Not EOF(f)
Line Input #f, linea
Print linea
Wend
Close #f
 
Function addSeconds$ (timeStr As String, secs As Integer)
Dim As Integer hh, mm, ss, ttt
hh = Val(Mid$(timeStr, 1, 2))
mm = Val(Mid$(timeStr, 4, 2))
ss = Val(Mid$(timeStr, 7, 2)) + secs
ttt = Val(Mid$(timeStr, 10, 3))
 
While ss < 0
ss = ss + 60
mm = mm - 1
Wend
While mm < 0
mm = mm + 60
hh = hh - 1
Wend
While hh < 0
hh = hh + 24
Wend
 
mm = mm + ss \ 60
hh = hh + mm \ 60
ss = ss Mod 60
mm = mm Mod 60
hh = hh Mod 24
addSeconds$ = Right$("0" + Str$(hh), 2) + ":" + Right$("0" + Str$(mm), 2) + ":" + Right$("0" + Str$(ss), 2) + "," + Right$("000" + Str$(ttt), 3)
End Function
 
Sub syncSubtitles (fileIn As String, fileOut As String, secs As Integer)
Dim As String linea, pio, fin, fmt
Dim As Integer f1, f2
 
fmt = "hh:MM:ss,ttt"
f1 = FreeFile
Open fileOut For Output As #f1
f2 = FreeFile
Open fileIn For Input As #f2
While Not EOF(f2)
Line Input #f2, linea
If InStr(linea, "-->") > 0 Then
pio = Mid$(linea, 1, 12)
pio = addSeconds$(pio, secs)
fin = Mid$(linea, 18, 12)
fin = addSeconds$(fin, secs)
Print #f1, pio; " --> "; fin
Else
Print #f1, linea
End If
Wend
Close #f2, #f1
End Sub</syntaxhighlight>
 
==={{header|Yabasic}}===
{{trans|FreeBASIC}}
<syntaxhighlight lang="yabasic">print "After fast-forwarding 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected.srt", 9)
f = open("movie_corrected.srt")
while not eof(f)
line input #f linea$
print linea$
wend
close #f
 
print "\n\nAfter rolling-back 9 seconds:\n"
syncSubtitles("movie.srt", "movie_corrected2.srt", -9)
f = open("movie_corrected2.srt")
while not eof(f)
line input #f linea$
print linea$
wend
close #f
end
 
sub addSeconds$ (timeStr$, secs)
hh = val(mid$(timeStr$, 1, 2))
mm = val(mid$(timeStr$, 4, 2))
ss = val(mid$(timeStr$, 7, 2)) + secs
ttt = val(mid$(timeStr$, 10, 3))
 
while ss < 0
ss = ss + 60
mm = mm - 1
wend
while mm < 0
mm = mm + 60
hh = hh - 1
wend
while hh < 0
hh = hh + 24
wend
 
mm = mm + int(ss / 60)
hh = hh + int(mm / 60)
ss = mod(ss, 60)
mm = mod(mm, 60)
hh = mod(hh, 24)
return right$("0" + str$(hh), 2) + ":" + right$("0" + str$(mm), 2) + ":" + right$("0" + str$(ss), 2) + "," + right$("000" + str$(ttt), 3)
end sub
 
sub syncSubtitles (fileIn$, fileOut$, secs)
fmt$ = "hh:MM:ss,ttt"
nl$ = chr$(10)
open fileOut$ for writing as #1
open fileIn$ for reading as #2
while not eof(2)
line input #2 linea$
if (instr(linea$, "-->") > 0) then
pio$ = mid$(linea$, 1, 12)
pio$ = addSeconds$(pio$, secs)
fin$ = mid$(linea$, 18, 12)
fin$ = addSeconds$(fin$, secs)
print #1, pio$ + " --> " + fin$ + nl$;
else
print #1, linea$ + nl$;
fi
wend
close #2
close #1
end sub</syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
=={{header|C++}}==
{{trans|FreeBASIC}}
<syntaxhighlight lang="cpp">#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <ctime>
 
using namespace std;
 
string addSeconds(string timeStr, int secs) {
int hours, minutes, seconds, milliseconds;
sscanf(timeStr.c_str(), "%d:%d:%d,%d", &hours, &minutes, &seconds, &milliseconds);
int total_seconds = hours * 3600 + minutes * 60 + seconds + secs;
hours = total_seconds / 3600;
total_seconds %= 3600;
minutes = total_seconds / 60;
seconds = total_seconds % 60;
char buffer[13];
sprintf(buffer, "%02d:%02d:%02d,%03d", hours, minutes, seconds, milliseconds);
return string(buffer);
}
 
void syncSubtitles(string fileIn, string fileOut, int secs) {
ifstream fin(fileIn);
ofstream fout(fileOut);
string line;
while (getline(fin, line)) {
if (line.find("-->") != string::npos) {
string start = line.substr(0, 12);
string end = line.substr(17, 12);
start = addSeconds(start, secs);
end = addSeconds(end, secs);
fout << start << " --> " << end << "\n";
} else {
fout << line << "\n";
}
}
fin.close();
fout.close();
}
 
int main() {
cout << "After fast-forwarding 9 seconds:\n\n";
syncSubtitles("movie.srt", "movie_corrected.srt", 9);
ifstream f("movie_corrected.srt");
string line;
while (getline(f, line)) {
cout << line << "\n";
}
f.close();
cout << "\n\nAfter rolling-back 9 seconds:\n\n";
syncSubtitles("movie.srt", "movie_corrected2.srt", -9);
ifstream f2("movie_corrected2.srt");
while (getline(f2, line)) {
cout << line << "\n";
}
f2.close();
return 0;
}</syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
=={{header|COBOL}}==
<syntaxhighlight lang="cobol"> IDENTIFICATION DIVISION.
PROGRAM-ID. SubtitleSync.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INPUT-FILE ASSIGN TO "movie.srt".
SELECT OUTPUT-FILE ASSIGN TO "movie_corrected.srt".
DATA DIVISION.
FILE SECTION.
FD INPUT-FILE.
01 IN-REC PIC X(100).
FD OUTPUT-FILE.
01 OUT-REC PIC X(100).
WORKING-STORAGE SECTION.
01 WS-SECS PIC S9(4) COMP VALUE 0.
01 WS-LINE PIC X(100) VALUE SPACES.
01 WS-START-TIME PIC X(12) VALUE SPACES.
01 WS-END-TIME PIC X(12) VALUE SPACES.
01 WS-HH PIC 99 VALUE 0.
01 WS-MM PIC 99 VALUE 0.
01 WS-SS PIC 99 VALUE 0.
01 WS-TTT PIC 999 VALUE 0.
PROCEDURE DIVISION.
DISPLAY "After fast-forwarding 9 seconds:"
MOVE 9 TO WS-SECS
OPEN INPUT INPUT-FILE
OPEN OUTPUT OUTPUT-FILE
PERFORM syncSubtitles
CLOSE INPUT-FILE
CLOSE OUTPUT-FILE
DISPLAY "After rolling-back 9 seconds:"
MOVE -9 TO WS-SECS
OPEN INPUT INPUT-FILE
OPEN OUTPUT OUTPUT-FILE
PERFORM syncSubtitles
CLOSE INPUT-FILE
CLOSE OUTPUT-FILE
STOP RUN.
syncSubtitles SECTION.
READ INPUT-FILE AT END GO TO CLOSE-FILES
IF WS-LINE(1:3) = "-->" THEN
MOVE WS-LINE(1:12) TO WS-START-TIME
MOVE WS-LINE(18:12) TO WS-END-TIME
PERFORM addSeconds
MOVE WS-START-TIME TO WS-LINE(1:12)
PERFORM addSeconds
MOVE WS-END-TIME TO WS-LINE(18:12)
STRING WS-START-TIME DELIMITED BY SIZE " --> "
DELIMITED BY SIZE WS-END-TIME DELIMITED BY SIZE INTO OUT-REC
WRITE OUT-REC
ELSE
MOVE WS-LINE TO OUT-REC
WRITE OUT-REC
END-IF
END-READ
CLOSE INPUT-FILE
CLOSE OUTPUT-FILE
EXIT.
addSeconds SECTION.
MOVE WS-START-TIME(1:2) TO WS-HH
MOVE WS-START-TIME(4:2) TO WS-MM
MOVE WS-START-TIME(7:2) TO WS-SS
MOVE WS-START-TIME(10:3) TO WS-TTT
ADD WS-SECS TO WS-SS
IF WS-SS < 0
ADD 60 TO WS-SS
SUBTRACT 1 FROM WS-MM
END-IF
IF WS-MM < 0
ADD 60 TO WS-MM
SUBTRACT 1 FROM WS-HH
END-IF
IF WS-HH < 0
ADD 24 TO WS-HH
END-IF
DIVIDE WS-SS BY 60 GIVING WS-MM REMAINDER WS-SS
DIVIDE WS-MM BY 60 GIVING WS-HH REMAINDER WS-MM
MOVE WS-HH TO WS-START-TIME(1:2)
MOVE WS-MM TO WS-START-TIME(4:2)
MOVE WS-SS TO WS-START-TIME(7:2)
MOVE WS-TTT TO WS-START-TIME(10:3)
EXIT.
CLOSE-FILES SECTION.
CLOSE INPUT-FILE
CLOSE OUTPUT-FILE
EXIT.
 
</syntaxhighlight>
 
=={{header|jq}}==
'''Works with gojq, the Go implementation of jq'''
 
The following program has been written for gojq, the Go implementation of jq,
and will almost surely need adapting for use with other versions of jq.
 
'''Usage'''
 
To fast-forward the subtitles by 9 seconds:
<pre>
< movie.srt gojq -nRr --argjson seconds 9 -f sync-subtitles.jq
</pre>
where sync-subtitles.jq is the name of the file containing the jq program.
To roll-back, specify `seconds` as a negative number, e.g.:
<pre>
< movie.srt gojq -nRr --argjson seconds -9 -f sync-subtitles.jq
</pre>
<syntaxhighlight lang="jq">
def syncSubtitles($secs):
def fmt: "%H:%M:%S";
def adjust: strptime(fmt) | .[5] += $seconds | strftime(fmt);
 
if ($secs|type) != "number" then "The number of seconds must be specified as an integer" | error end
| inputs as $line
| ($line
| capture("^(?<start>[^,]*),(?<startms>[0-9]*) *--> *(?<finish>[^,]*),(?<finishms>[0-9]*)")
| "\(.start|adjust),\(.startms) --> \(.finish|adjust),\(.finishms)" )
// $line ;
 
if $seconds > 0 then
"Fast-forwarding \($seconds) seconds" | debug
| syncSubtitles($seconds)
elif $seconds == 0 then
"No resynchronization is needed" | debug
else
"Rolling-back \(-$seconds) seconds" | debug
| syncSubtitles($seconds)
end
</syntaxhighlight>
{{output}}
As shown elsewhere on this page.
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">using Dates
function syncsubtitles(intext::AbstractString, sec::Integer, msec::Integer)
outtext, fmt = "", dateformat"HH:MM:SS,sss"
deltatime = Dates.Second(sec) + Dates.Millisecond(msec)
for line in split(intext, r"\r?\n")
if !isempty(line) && length(begin times = split(line, " --> ") end) == 2
start, stop = DateTime.(times, fmt) .+ deltatime
line = join(Dates.format.((start, stop), fmt), " ==> ")
end
outtext *= line * "\n"
end
return outtext
end
 
function syncsubtitles(infile, outfile::AbstractString, sec, msec = 0)
outs = open(outfile, "w")
write(outs, syncsubtitles(read(infile, String), sec, msec))
close(outs)
end
 
println("After fast-forwarding 9 seconds:\n")
syncsubtitles("movie.srt", "movie_corrected.srt", 9)
println(read("movie_corrected.srt", String))
println("After rolling-back 9 seconds:\n")
syncsubtitles("movie.srt", "movie_corrected2.srt", -9)
println(read("movie_corrected2.srt", String))
</syntaxhighlight>{{out}}
<pre>
After fast-forwarding 9 seconds:
 
1
00:01:40,550 ==> 00:01:45,347
Four billion years ago,
the first marine life forms.
 
2
00:01:45,555 ==> 00:01:51,019
First came the fish...then slowly
other life forms evolved.
 
3
00:01:51,144 ==> 00:01:52,979
Therefore, our ancestors...
 
4
00:01:52,979 ==> 00:01:54,898
...came from fish.
 
5
00:01:55,232 ==> 00:01:56,608
Everyone, come and see this.
 
6
00:01:56,733 ==> 00:01:59,361
Cretaceous Tyrannosaurus.
 
7
00:01:59,736 ==> 00:02:01,488
Ferocious!
 
8
00:02:07,035 ==> 00:02:07,869
Red,
 
9
00:02:08,203 ==> 00:02:09,079
Pong!
 
10
00:02:11,540 ==> 00:02:12,999
Isn't this a gecko?
 
11
00:02:13,416 ==> 00:02:16,419
How else can I get a 15 ton T-Rex in here?
 
12
00:02:16,503 ==> 00:02:20,048
With our advanced technology, I shrank it down.
 
 
After rolling-back 9 seconds:
 
1
00:01:22,550 ==> 00:01:27,347
Four billion years ago,
the first marine life forms.
 
2
00:01:27,555 ==> 00:01:33,019
First came the fish...then slowly
other life forms evolved.
 
3
00:01:33,144 ==> 00:01:34,979
Therefore, our ancestors...
 
4
00:01:34,979 ==> 00:01:36,898
...came from fish.
 
5
00:01:37,232 ==> 00:01:38,608
Everyone, come and see this.
 
6
00:01:38,733 ==> 00:01:41,361
Cretaceous Tyrannosaurus.
 
7
00:01:41,736 ==> 00:01:43,488
Ferocious!
 
8
00:01:49,035 ==> 00:01:49,869
Red,
 
9
00:01:50,203 ==> 00:01:51,079
Pong!
 
10
00:01:53,540 ==> 00:01:54,999
Isn't this a gecko?
 
11
00:01:55,416 ==> 00:01:58,419
How else can I get a 15 ton T-Rex in here?
 
12
00:01:58,503 ==> 00:02:02,048
With our advanced technology, I shrank it down.
 
Line 162 ⟶ 1,012:
Needed a bugfix to adjust_timedate() for time-only handling, in that a y,m,d of 0,0,0 pre-dates the introduction of the Gregorian calendar.
There was also a floor(milliseconds) which should have been/is now round(milliseconds). Fixes can be grabbed from github if needed.
 
You could of course use sequence lines = read_lines("movie.srt") and write_lines("movie_corrected.srt",lines) for a file-based solution, but obviously that would not be compatible with JavaScript in a browser window.
<syntaxhighlight lang="phix">
requires("1.0.6") -- (a time-only handling bugfix in adjust_timedate)
Line 214 ⟶ 1,066:
00:02:13,416 --> 00:02:16,419
00:02:16,503 --> 00:02:20,048
With our advanced technology, I shrank it down.
</pre>
 
=={{header|Python}}==
{{libheader|datetime}}
{{works with|Python|3.x}}
{{trans|FreeBASIC}}
<syntaxhighlight lang="python">#! /usr/bin/env python3
 
import datetime
 
def add_seconds(time_str, secs):
time_format = "%H:%M:%S,%f"
time_obj = datetime.datetime.strptime(time_str, time_format)
delta = datetime.timedelta(seconds=secs)
new_time_obj = time_obj + delta
new_time_str = new_time_obj.strftime(time_format)[:-3] # remove the last 3 digits of microseconds
return new_time_str
 
def sync_subtitles(file_in, file_out, secs):
with open(file_in, 'r') as fin, open(file_out, 'w') as fout:
for line in fin:
if '-->' in line:
start_time, end_time = line.strip().split(' --> ')
start_time = add_seconds(start_time, secs)
end_time = add_seconds(end_time, secs)
fout.write(f"{start_time} --> {end_time}\n")
else:
fout.write(line)
 
print("After fast-forwarding 9 seconds:\n")
sync_subtitles("movie.srt", "movie_corrected.srt", 9)
with open("movie_corrected.srt", 'r') as f:
print(f.read())
 
print("\n\nAfter rolling-back 9 seconds:\n")
sync_subtitles("movie.srt", "movie_corrected2.srt", -9)
with open("movie_corrected2.srt", 'r') as f:
print(f.read())
</syntaxhighlight>
{{out}}
<pre>Same as FreeBASIC entry.</pre>
 
=={{header|Raku}}==
===Brutal===
<syntaxhighlight lang="raku" line># 20240621 Raku programming solution
 
sub MAIN() {
my @lines = $*IN.lines;
for 9, -9 -> $seconds {
say "Original subtitle adjusted by {$seconds.fmt('%+d')} seconds.";
for @lines -> $line {
if $line ~~ /(\d ** 2 ':' \d ** 2 ':' \d ** 2 ',' \d ** 3) ' --> ' (\d ** 2 ':' \d ** 2 ':' \d ** 2 ',' \d ** 3)/ {
my $start = adjust-time($0.Str, $seconds);
my $end = adjust-time($1.Str, $seconds);
my $adjusted = $line;
$adjusted ~~ s/\d ** 2 ':' \d ** 2 ':' \d ** 2 ',' \d ** 3 ' --> ' \d ** 2 ':' \d ** 2 ':' \d ** 2 ',' \d ** 3/$start ~ ' --> ' ~ $end/.Str;
say $adjusted
} else {
say $line;
}
}
say()
}
}
 
sub adjust-time($time, $seconds) {
my ($time_str, $milliseconds_str) = $time.split(',');
my (\hh, \mm, \ss) = $time_str.split(':')>>.Int;
my $milliseconds = $milliseconds_str.Int;
my $datetime = DateTime.new(:year, :month, :day,
:hour(hh), :minute(mm), :second(ss));
given $datetime .= later(:seconds($seconds)) {
return sprintf('%02d:%02d:%02d,%03d',.hour,.minute,.second,$milliseconds)
}
}</syntaxhighlight>
 
You may [https://ato.pxeger.com/run?1=lVTdbuNEFJYQV-cpDquu7JSJ49_EdtUuCKjIBQtaKnFTCbnxJDa1Z1aecUq0aiWeg5tewEvtI_AUnIlj1ykroc6FfXzm-87PN8fz519Ndts-Pv7d6vU0_vj5H6q9wR--Xr61J_gBELHe4VdVKbjCczw5Xb519h9nZmstG0wYThOcXuCJ4ispctWxaKlsh69-bMpNKbIKKawudcUxy39rleY53uzwQ09y1rW2rddf5tbkHnvfq7NDJJPnUINJZKwhC61yffA9PODMvs7x9BR9tFILP2mz3g4maOGUIlr4ItZsnLxT6ETprNGkUNfdVJc1t09c52fdsEGZydl_aFzkZD2jef9LGzQ87zo_RjxtkyBq9oLWBj1ewJkden8YyA_7vmami-PCzEQMxT3t3COvFH8m6h77rLd7OH4Txp7A3nEPYAb3SEfzHMk4jHO386vai1yXVVUeIMY1MZqafUe9r0oaSmZ1B2CI10XB8Lqu6aHUgDS0Hp1ak4sLZyl0zzlKYBjPEx6B80xzE5KA35J5ZeoQ_M5Odzyjas1Kayl00dmY5tmO4ZFwn1hpIdvGLooJI3YpWs3tujYfXRk29dL1uCm3XIyqcM6xIruxD0hlD2pOhvNquG4bgep9Uwq9pr_Y9fN0eLDXbpBbzDElMKfLzpwuCjsSoz_J7iY6XEiPHz_7xwPXTV0vDTwWRe5-yA6OOQvCBVxSaLwxkaRAI5TCbCMZ6ILjumyUxjprzP1QlWtuLpNaOYDgwxAliqJR2NBnrpfA5Z66ykiHLpIqHMchk3qt5F21A0kfzSgq8q2stjx3AAIYYnlhOA4esGSRwBUxOZFoQE31mVhxpWWjKANACGPsmByxOImBQPuy1o2su7oAop4zZ37gjzkLNndj-G7Lm50UlG8liZrRxaO46awkMWAOA3gRBCN25LJg7sE3dMjZistW4dWuyYSQKmub1lAXMCAXwXxM9VkYx3DJG7kqifkFQNxjY-YGY8nJEc8TeMdzBpD0qIT57lMxfuq6zCXtfpJiQ8E8Fw5un0WhO8aRbkkCSyUsve8QM9zw1a18QyyvZ4Us9OZj1oIcCXwv77r7aJUJXBJPE9uLUNNwXU3f8d-xFGhOzwTzYeBGR6V6HnPDGH4pddEdcL41Z5yj5qtCyEpu6L9doipIzFssNebyTjjdzP8L Attempt This Online!]
 
===Less brutal===
<syntaxhighlight lang="raku" line># 20240622 Raku programming solution
 
grammar SRT { # loc.gov/preservation/digital/formats/fdd/fdd000569.shtml
token TOP { ^ <subtitle>+ % \n $ }
token subtitle { <index> \n <timecode> \n <text> \n? }
token index { \d+ }
token timecode { <timestamp> ' --> ' <timestamp> }
token timestamp { \d ** 2 ':' \d ** 2 ':' \d ** 2 ',' \d ** 3 }
token text { <line>+ % \n }
token line { <-[\n]>+ }
}
 
class SRT::Actions { has @.subtitles;
 
method TOP($/) {
@.subtitles = $<subtitle>».made;
make @.subtitles
}
 
method subtitle($/) {
make {
index => $<index>.Str,
start => $<timecode><timestamp>[0].made,
end => $<timecode><timestamp>[1].made,
text => $<text>.made,
}
}
 
method timestamp($/) { make $/.Str }
 
method text($/) { make $<line>.join("\n") }
}
 
class SubtitleAdjuster {
 
method adjust-time($time, $seconds) {
my ($time-str, $milliseconds-str) = $time.split(',');
my (\hh, \mm, \ss) = $time-str.split(':')>>.Int;
my \mls = $milliseconds-str.Int;
my $datetime = DateTime.new( :year, :month, :day,
:hour(hh), :minute(mm), :second(ss));
given $datetime .= later(:seconds($seconds)) {
return sprintf('%02d:%02d:%02d,%03d', .hour, .minute, .second, mls)
}
}
 
method adjust-subtitles(@subtitles, Int $seconds) {
@subtitles.map({
$_<start> = self.adjust-time($_<start>, $seconds);
$_<end> = self.adjust-time($_<end>, $seconds);
$_;
});
}
 
method format-srt(@subtitles) {
@subtitles.map({
$_<index> ~ "\n"
~ $_<start> ~ " --> " ~ $_<end> ~ "\n"
~ $_<text> ~ "\n"
}).join("\n");
}
}
 
my $srt-content = $*IN.slurp;
my $parsed = SRT.parse($srt-content, :actions(SRT::Actions.new));
my @subtitles = $parsed.made;
 
my $adjuster = SubtitleAdjuster.new;
 
for 9, -9 -> \N {
my @adjusted-subtitles = $adjuster.adjust-subtitles(@subtitles.deepmap(*.clone), N);
say "Original subtitle adjusted by {N.fmt('%+d')} seconds.";
say $adjuster.format-srt(@adjusted-subtitles);
}</syntaxhighlight>
 
You may [https://ato.pxeger.com/run?1=jVbdbts2FAZ2NfApzjIHllNakfwvJ3FTbCuWXaRFG2AXdTewFm2rkUiDpJ0ahfsiu-nF9hS722P0EfYUO9Sf6SYtJsAydc75Pp4_HumPPxW7XX_8-NfazNujT9_-slAsy5iCly9u4D18D6mc-Qu5OV0prrnaMJNIcRoni8Sw9HQuVcaMPp3Hsf0FQdAfRL5emiwlAGDkLRdw8-w5Mv0G53r9xiQm5ZNHcAxTAQ3Y7a0qJZqeJyLm7ybW5NwkGZ_JmJdP_J2xq8cuMrdG2DR-5IorpCW0a21YtppAE9pte3dln6FyaU4IJyfQgea4-fCaVuvuAQU6aTdNE1GH6qit2Krbr6bi9cS6vCNkljKtbc7H4yczm2KNJkum4dKvEqPPiCXJuFnK2CbVa5y24L2V4eXYwQU09rn-528_YzE_K-0ydstdYyveucSV5oA9R1UPeBUpv5jgRkWt_JdG0b0eM6hMoa8L6CT8VfA6d8pBcBHj_cuI8B4iT3OJsG1xoN_di6vmKgIrQmqcWsc_M0SyA5uikP5bmQjvaCqOWgclK9P1JH671oYrzFLuQknGcnHbbu417J1CQ2N0Itb77EK2hULb1phGaGRJmialmRW1bEmt3terNDEeNl7rzAVPl0sK0yzDm9a1tYVWiHGzNZn4V8K4uGmWanCui_tbfw5pxMxwS47GP-Lyxnol-J0H4y1n6Pw4k8KgN-OYbZ1qfeUaL-Vaectly4ITsTbcyzL7ULjhYUT7aBfJBs_Q3gv_AlJcK6-01l6d35bbsaC4WSscMyuVCDP3msdBJx7XN3ocdOMmBd_6gn-FH7goyChgploV2_3mKstcnyrvsl5SwAw-UPS9BTbuynNcbfx-np-fCaZY83TuHzRRpXQa6ewAiydpktfyIaxVfglZPewK8UGAxaBva2Wc0P5nMOU0_wD28Ow1H5xAUZeP5aNCmofwoH3xAjhQ7VrO0Sw9R99tr6K7bQzUcKwA9vbJ1bWv07VaneXaFVOax6jAsevnD56LwAZkxSj23Llsm932IzJcHozcgq4ctvkGrJoJF_fGhGVBK0wrRBTaEWDw0-sin5a5hMbtgy0qQv8r_ebHnK9sEU78WSoFx4N0XaRFsy0cPVP46hYs3b9vq63gzRbeX_vzDKfF8aO42dpB2Sb-UY3fu-B2xH1vccdd8UlRfll8_PTNvyEJgnEQjrsh7feDvOClYEC7vSF5imcP3tgBJAXYaaKBLSQlZslhnihtcCAr-_ZMkznPW1L7BEiH1Cz9ft-h7XVoEEbkaQ6dMZwWBZNe-r6PS5wGqbxLt0Tig3JYgW9kusFiEtIlNVfY67nkXRoNI3KDSI4gnBXWeyZm-JaRSuMOhPSIa-uC-3QUjQga5W7NlcwKvwjpV5gB7XQ7LmZIB8GI_LThaot1pTCTCGX43tTcRpZgMsiA1MbDbtdB9wPaHYTkBxyDbMblWsPNVjEhpGZrtbbQIakth92BC-3Q3mhEnnIlZwkivyNkVNmOaNB1U46C0SAiL3hMCYkqq4h2gr0znXEQ0ABz91yKBZKFASnFHdrvBa4d5i2KyJUWTZNHCAwWfHYrHyMqrFA92gsHLmqIgoj8LO-Ap5pj5QVcIc4gOuzjVxh-krZf4PdLIsBWz5J1SI3tH7gahjTojciviVkWBY43tsb2M2G2FDKViy1OeNBLTOYtJAZieSf8ouf_Aw) Attempt This Online!]
 
=={{header|Rust}}==
{{libheader|chrono}}
{{libheader|lazy_static}}
{{libheader|regex}}
 
<syntaxhighlight lang="rust">
use chrono::{NaiveTime, TimeDelta};
use lazy_static::lazy_static;
use regex::Regex;
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::io::{stdout, Write};
use std::path::Path;
 
lazy_static! {
static ref RE: Regex =
Regex::new(r"(\d{2}:\d{2}:\d{2},\d{3})\s+-->\s+(\d{2}:\d{2}:\d{2},\d{3})").unwrap();
static ref FORMAT: String = String::from("%H:%M:%S,%3f");
}
 
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
 
match args.len() {
3 => {
let path = &args[1];
let secs: i64 = args[2].parse()?;
let mut lock = stdout().lock();
for line in sync_file(path, secs)? {
writeln!(lock, "{line}").unwrap()
}
}
_ => println!("usage: subrip-sync <filename> <seconds>"),
}
 
Ok(())
}
 
fn sync_file<P>(path: P, secs: i64) -> Result<impl Iterator<Item = String>, io::Error>
where
P: AsRef<Path>,
{
let file = File::open(path)?;
let reader = io::BufReader::new(file);
Ok(sync_lines(reader.lines().flatten(), secs))
}
 
fn sync_lines(lines: impl Iterator<Item = String>, secs: i64) -> impl Iterator<Item = String> {
let delta = TimeDelta::new(secs, 0).unwrap();
lines.map(move |line| {
if let Some(groups) = RE.captures(&line) {
format(&groups[1], &groups[2], &delta)
} else {
line
}
})
}
 
fn format(start: &str, stop: &str, delta: &TimeDelta) -> String {
format!(
"{} --> {}",
(NaiveTime::parse_from_str(start, &FORMAT).unwrap() + *delta).format(&FORMAT),
(NaiveTime::parse_from_str(stop, &FORMAT).unwrap() + *delta).format(&FORMAT)
)
}
</syntaxhighlight>
 
{{out}}
<pre>
$ subrip-sync movie.srt 9 > movie_plus_9.srt
$ cat movie_plus_9.srt
1
00:01:40,550 --> 00:01:45,347
Four billion years ago,
the first marine life forms.
 
2
00:01:45,555 --> 00:01:51,019
First came the fish...then slowly
other life forms evolved.
 
3
00:01:51,144 --> 00:01:52,979
Therefore, our ancestors...
 
4
00:01:52,979 --> 00:01:54,898
...came from fish.
 
5
00:01:55,232 --> 00:01:56,608
Everyone, come and see this.
 
6
00:01:56,733 --> 00:01:59,361
Cretaceous Tyrannosaurus.
 
7
00:01:59,736 --> 00:02:01,488
Ferocious!
 
8
00:02:07,035 --> 00:02:07,869
Red,
 
9
00:02:08,203 --> 00:02:09,079
Pong!
 
10
00:02:11,540 --> 00:02:12,999
Isn't this a gecko?
 
11
00:02:13,416 --> 00:02:16,419
How else can I get a 15 ton T-Rex in here?
 
12
00:02:16,503 --> 00:02:20,048
With our advanced technology, I shrank it down.
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-date}}
{{libheader|Wren-ioutil}}
<syntaxhighlight lang="wren">import "./date" for Date
import "./ioutil" for File, FileUtil
 
var syncSubtitles = Fn.new { |fileIn, fileOut, secs|
var nl = FileUtil.lineBreak
var fmt = "hh|:|MM|:|ss|,|ttt"
var f = File.create(fileOut)
for (line in FileUtil.readLines(fileIn)) {
if (line.contains("-->")) {
var start = line[0..11]
var startDate = Date.parse(start, fmt).addSeconds(secs)
start = startDate.format(fmt)
var end = line[17..28]
var endDate = Date.parse(end, fmt).addSeconds(secs)
end = endDate.format(fmt)
f.writeBytes(start + " --> " + end + nl)
} else {
f.writeBytes(line + nl)
}
}
f.close()
}
 
System.print("After fast-forwarding 9 seconds:\n")
syncSubtitles.call("movie.srt", "movie_corrected.srt", 9)
System.print(File.read("movie_corrected.srt"))
System.print("After rolling-back 9 seconds:\n")
syncSubtitles.call("movie.srt", "movie_corrected2.srt", -9)
System.print(File.read("movie_corrected2.srt"))</syntaxhighlight>
 
{{out}}
<pre>
After fast-forwarding 9 seconds:
 
1
00:01:40,550 --> 00:01:45,347
Four billion years ago,
the first marine life forms.
 
2
00:01:45,555 --> 00:01:51,019
First came the fish...then slowly
other life forms evolved.
 
3
00:01:51,144 --> 00:01:52,979
Therefore, our ancestors...
 
4
00:01:52,979 --> 00:01:54,898
...came from fish.
 
5
00:01:55,232 --> 00:01:56,608
Everyone, come and see this.
 
6
00:01:56,733 --> 00:01:59,361
Cretaceous Tyrannosaurus.
 
7
00:01:59,736 --> 00:02:01,488
Ferocious!
 
8
00:02:07,035 --> 00:02:07,869
Red,
 
9
00:02:08,203 --> 00:02:09,079
Pong!
 
10
00:02:11,540 --> 00:02:12,999
Isn't this a gecko?
 
11
00:02:13,416 --> 00:02:16,419
How else can I get a 15 ton T-Rex in here?
 
12
00:02:16,503 --> 00:02:20,048
With our advanced technology, I shrank it down.
 
 
After rolling-back 9 seconds:
 
1
00:01:22,550 --> 00:01:27,347
Four billion years ago,
the first marine life forms.
 
2
00:01:27,555 --> 00:01:33,019
First came the fish...then slowly
other life forms evolved.
 
3
00:01:33,144 --> 00:01:34,979
Therefore, our ancestors...
 
4
00:01:34,979 --> 00:01:36,898
...came from fish.
 
5
00:01:37,232 --> 00:01:38,608
Everyone, come and see this.
 
6
00:01:38,733 --> 00:01:41,361
Cretaceous Tyrannosaurus.
 
7
00:01:41,736 --> 00:01:43,488
Ferocious!
 
8
00:01:49,035 --> 00:01:49,869
Red,
 
9
00:01:50,203 --> 00:01:51,079
Pong!
 
10
00:01:53,540 --> 00:01:54,999
Isn't this a gecko?
 
11
00:01:55,416 --> 00:01:58,419
How else can I get a 15 ton T-Rex in here?
 
12
00:01:58,503 --> 00:02:02,048
With our advanced technology, I shrank it down.
</pre>
354

edits