user
Sync subtitles

**Sync subtitles**

Sometimes, when you download a movie, the subtitles are out of sync with the audio in this movie. Video players like VLC solve the problem, but it must be done manually.

Make a program that takes the "movie.srt" file, and synchronizes the subtitles "n" seconds.

* Try to fast-forward the subtitles by 9 seconds.
* Try rolling back the subtitles by 9 seconds.

Take the excerpt from the following subtitles file as an example:

**movie.srt**

```
1
00:01:31,550 --> 00:01:36,347
Four billion years ago,
the first marine life forms.

2
00:01:36,555 --> 00:01:42,019
First came the fish...then slowly
other life forms evolved.

3
00:01:42,144 --> 00:01:43,979
Therefore, our ancestors...

4
00:01:43,979 --> 00:01:45,898
...came from fish.

5
00:01:46,232 --> 00:01:47,608
Everyone, come and see this.

6
00:01:47,733 --> 00:01:50,361
Cretaceous Tyrannosaurus.

7
00:01:50,736 --> 00:01:52,488
Ferocious!

8
00:01:58,035 --> 00:01:58,869
Red,

9
00:01:59,203 --> 00:02:00,079
Pong!

10
00:02:02,540 --> 00:02:03,999
Isn't this a gecko?

11
00:02:04,416 --> 00:02:07,419
How else can I get a 15 ton T-Rex in here?

12
00:02:07,503 --> 00:02:11,048
With our advanced technology, I shrank it down.

```

References

:   * Wikipedia: SubRip
assistant
### Brutal

```
# 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)
   }
}

```

You may Attempt This Online!

### Less brutal

```
# 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);
}

```

You may Attempt This Online!