|
|
|
#!/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;
|