|
|
@ -22,24 +22,30 @@ fn main() { |
|
|
|
let argv = |
|
|
|
let argv = |
|
|
|
clap::App::new("srtune") |
|
|
|
clap::App::new("srtune") |
|
|
|
.version(env!("CARGO_PKG_VERSION")) |
|
|
|
.version(env!("CARGO_PKG_VERSION")) |
|
|
|
.about("Modify a .srt file to match a video. Input and output can be a file or stream, \ |
|
|
|
.about("\ |
|
|
|
so you pipe multiple invocations to create more complex operations. However, a single \ |
|
|
|
'srtune' helps you edit a .srt file to match a video. Input and output can be either a file, or a stream, \ |
|
|
|
|
|
|
|
so you can pipe multiple invocations to create more complex operations. However, a single \ |
|
|
|
invocation should suffice in most cases.\n\ |
|
|
|
invocation should suffice in most cases.\n\ |
|
|
|
\n\ |
|
|
|
\n\ |
|
|
|
Times are specified with colons and always include seconds (HH:MM:SS, MM:SS, 0:SS). \ |
|
|
|
Times are specified with colons (required) and always include seconds (HH:MM:SS, MM:SS, 0:SS, :SS). \ |
|
|
|
Decimal point can be either period or comma, so times can be copied directly from the \ |
|
|
|
Decimal point in the seconds part, if needed, can be either a period or a comma; times can be copied from the \ |
|
|
|
.srt file. Numbers without colons are assumed to be subtitle indices.\n\ |
|
|
|
.srt file. Numbers without colons are assumed to be subtitle indices.\n\ |
|
|
|
\n\ |
|
|
|
\n\ |
|
|
|
The tool can be used iteratively, adjusting the invocation until the generated \ |
|
|
|
The tool can be used iteratively, adjusting the invocation until the generated \ |
|
|
|
subtitle file matches the audio track. As such, times accepted by its parameters \ |
|
|
|
subtitle file matches the audio track. You can reload the file in VLC by dragging it \ |
|
|
|
are, by default, the ones seen in the output file (after shifts and moving), while \ |
|
|
|
onto the player window. To make this work, subtitle times specified in arguments are the \ |
|
|
|
indices are those from the input file.\n\ |
|
|
|
ones seen in the output file (after shifts and moving), while entry indices are those from the input file.\n\ |
|
|
|
|
|
|
|
\n\ |
|
|
|
|
|
|
|
Using indices makes it easier to specify a subtitle to alter, but it is tied to the one .srt file. \ |
|
|
|
|
|
|
|
Times are harder to write, but the one configuration will work for any locatization or variant of the file,\ |
|
|
|
|
|
|
|
so long as it is intended for the same version of the movie. Enable debug logging with '-v' to see times \ |
|
|
|
|
|
|
|
you can use in place of indices. |
|
|
|
\n\ |
|
|
|
\n\ |
|
|
|
Indices are normally not renumbered, so the output file can be used as a reference \ |
|
|
|
Indices are normally not renumbered, so the output file can be used as a reference \ |
|
|
|
for both times and indices. The flag '--renumber' will give each output entry a new \ |
|
|
|
for both times and indices when you work out the right set of arguments. The flag '--renumber' \ |
|
|
|
sequential number. Please note that, once renumbered, the indices in the output file \ |
|
|
|
will give each output entry a new sequential number. Please note that, once renumbered, \ |
|
|
|
should no longer be used in the command invocation, as there can be a mismatch. They \ |
|
|
|
the indices in the output file should no longer be used in the command invocation, \ |
|
|
|
can be used when piped into a new process, of course. |
|
|
|
as there can be (and often will be) differences from the original file.\ |
|
|
|
") |
|
|
|
") |
|
|
|
.arg(clap::Arg::with_name("input") |
|
|
|
.arg(clap::Arg::with_name("input") |
|
|
|
.value_name("INFILE") |
|
|
|
.value_name("INFILE") |
|
|
@ -210,7 +216,6 @@ fn main() { |
|
|
|
} |
|
|
|
} |
|
|
|
None => (/* no automoves */) |
|
|
|
None => (/* no automoves */) |
|
|
|
} |
|
|
|
} |
|
|
|
debug!("Automove: {:?}", automove); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let inf = argv.value_of("input"); |
|
|
|
let inf = argv.value_of("input"); |
|
|
|
let outf = argv.value_of("output"); |
|
|
|
let outf = argv.value_of("output"); |
|
|
@ -359,11 +364,13 @@ enum AutoMoveTag { |
|
|
|
ByIndex(u32, SubInstant) |
|
|
|
ByIndex(u32, SubInstant) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug,Default,Clone,Copy)] |
|
|
|
#[derive(Debug,Default,Clone)] |
|
|
|
struct IterState { |
|
|
|
struct IterState { |
|
|
|
start_time : Option<SubInstant>, |
|
|
|
start_time : Option<SubInstant>, |
|
|
|
renumber_i : u32, |
|
|
|
renumber_i : u32, |
|
|
|
timeline_head : SubInstant, |
|
|
|
timeline_head : SubInstant, |
|
|
|
|
|
|
|
/// Queue last item for duration clipping
|
|
|
|
|
|
|
|
queued : Option<Subtitle>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn transform_subtitles<'a>(mut lines : Box<dyn SubsInput + 'a>, mut outfile : Box<dyn SubsOutput + 'a>, mut opts : TransformOpts) { |
|
|
|
fn transform_subtitles<'a>(mut lines : Box<dyn SubsInput + 'a>, mut outfile : Box<dyn SubsOutput + 'a>, mut opts : TransformOpts) { |
|
|
@ -443,11 +450,8 @@ fn transform_subtitles<'a>(mut lines : Box<dyn SubsInput + 'a>, mut outfile : Bo |
|
|
|
|
|
|
|
|
|
|
|
subtitle.dur *= opts.durscale; |
|
|
|
subtitle.dur *= opts.durscale; |
|
|
|
|
|
|
|
|
|
|
|
// TODO prevent durations overlap (will need to buffer one entry)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let would_be_shifted_start = subtitle.start + opts.shift; |
|
|
|
let would_be_shifted_start = subtitle.start + opts.shift; |
|
|
|
|
|
|
|
|
|
|
|
// TODO use drain_filter when stable
|
|
|
|
|
|
|
|
let mut to_drop = vec![]; |
|
|
|
let mut to_drop = vec![]; |
|
|
|
for (i, amove) in opts.automove.iter().enumerate() { |
|
|
|
for (i, amove) in opts.automove.iter().enumerate() { |
|
|
|
match amove { |
|
|
|
match amove { |
|
|
@ -502,7 +506,17 @@ fn transform_subtitles<'a>(mut lines : Box<dyn SubsInput + 'a>, mut outfile : Bo |
|
|
|
istate.renumber_i += 1; |
|
|
|
istate.renumber_i += 1; |
|
|
|
subtitle.num = istate.renumber_i; |
|
|
|
subtitle.num = istate.renumber_i; |
|
|
|
} |
|
|
|
} |
|
|
|
outfile.emit(subtitle); |
|
|
|
|
|
|
|
|
|
|
|
if let Some(mut q) = istate.queued.take() { |
|
|
|
|
|
|
|
if q.start + q.dur > subtitle.start { |
|
|
|
|
|
|
|
let clipped = subtitle.start - q.start; |
|
|
|
|
|
|
|
debug!("Clipping duration of #{} ({}) to avoid overlap: {} -> {}", q.num, q.start, q.dur, clipped); |
|
|
|
|
|
|
|
q.dur = clipped; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
outfile.emit(q); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
istate.queued = Some(subtitle); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Err(e) => { |
|
|
|
Err(e) => { |
|
|
@ -514,6 +528,11 @@ fn transform_subtitles<'a>(mut lines : Box<dyn SubsInput + 'a>, mut outfile : Bo |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// emit the last entry
|
|
|
|
|
|
|
|
if let Some(q) = istate.queued.take() { |
|
|
|
|
|
|
|
outfile.emit(q); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//region Time types
|
|
|
|
//region Time types
|
|
|
|