#!/usr/bin/env perl use strict; use warnings; use lib 'lib'; use utf8; use Getopt::Long; use Subtitle::SSA; sub usage { my ($msg) = @_; print $msg, "\n" if $msg; print <<USAGE; Usage: ssa-retime <mode> [<options>] -i <file> [ -o <file>] Modes are: * framerate Retime whole file from one fps to another. * points Retime file specifying point(s) & time shift for it. * shift Shift whole file or it's part for given time. Common options: -h This help. -i <file> Input file. (mandatory). -o <file> Output file. Default: write to stdout. -v Be verbose. Specific options for 'shift' mode: -S <string> Retime only events with specified style. This option may be given more than once. -s <time> Start time of period, that will be changed. Default: first event. -e <time> End time of period, that will be changed. Default: the latest time found in file. -l <time> Duration of period above. This option require '-s' You may select either '-o' or '-e' at the same time. -t <time> Change time for this value. Specific options for 'framerate' mode: -f <float> Source framerate. Default: 25.0 fps. -F <float> Target framerate. Specific options for 'points' mode: -p <time>/<time> Point of fixup & time shift in it. Option can be specified more than once. (see man for details) Both args must be in form of [-][[h:]m:]s[.ms]. USAGE exit 1; } ########### init ################### $| = 1; my $mode = shift @ARGV; my @points; my %opts = ( loglevel => 0, inrate => 25.0 ); #GetOptions("qvhi:o:S:f:F:p:t:s:e:l:", \%opts) GetOptions( 'h|help' => \&usage, 'v|verbose+' => \$opts{loglevel}, 'i|infile=s' => \$opts{infile}, 'o|outfile=s' => \$opts{outfile}, 'S|style=s' => \$opts{style}, 's|shiftstart=s'=> \$opts{shiftstart}, 'e|shiftend=s' => \$opts{shiftend}, 'l|shiftlen=f' => \$opts{shiftlen}, 't|shifttime=f' => \$opts{shifttime}, 'f|inrate=f' => \$opts{inrate}, 'F|outrate=f' => \$opts{outrate}, 'p|point' => \@points, ); ($opts{infile} and -f $opts{infile}) or usage("Input file not exists"); if ($mode eq 'framerate') { $opts{outrate} or usage("You should set at least '-F' option in this mode"); ($opts{inrate} != 0.0 and $opts{outrate} != 0.0) or usage("Framerate can't be zero"); } elsif ($mode eq 'shift') { $opts{shifttime} or usage("You should set '-t' option in this mode"); (not $opts{shiftstart} or $opts{shiftstart} > 0.0) or usage("Shift start must be positive time"); (not $opts{shiftend} or $opts{shiftend} > 0.0) or usage("Shift end must be positive time"); (not $opts{shiftlen} or $opts{shiftlen} > 0.0) or usage("Shift length must be positive time"); } elsif ($mode eq 'points') { scalar(@points) or usage("You should specify at least one shift point"); foreach my $point (@points) { ... } } else { usage("Incorrect mode: $mode (see help below)"); } my $ssa = Subtitle::SSA->new(debug => !!$opts{loglevel}); if ($ssa->from_file($opts{infile}) < 0) { warn "Can't parse input file: $opts{infile}\n"; exit 1; } if ($mode eq 'framerate') { my $mod = $opts{inrate} / $opts{outrate}; foreach my $event (@{ $ssa->{events} }) { $event->{start} *= $mod; $event->{end} *= $mod; } } elsif ($mode eq 'shift') { my $style = $opts{style}; my $start = $opts{shiftstart} // 0.0; my $end = $opts{shiftend} // (($opts{shiftlen}) ? $start + $opts{shiftend} : undef); my $mod = $opts{shifttime}; foreach my $event (@{ $ssa->{events} }) { next if ($style and $event->{style} ne $style); next if ($start and $event->{start} < $start); next if ($end and $event->{end} > $end); $event->{start} += $mod; $event->{end} += $mod; } } elsif ($mode eq 'points') { ...; } exit 0;