Files
catena/crates/cuenorm/README.md
T

4.1 KiB
Raw Blame History

cuenorm

Any subtitle format in, one normalised cue stream out.

Part of catena.

What it does

Reads a subtitle file — SubRip, WebVTT or SubStation Alpha — and writes one cue record per line: a start, an end, and what was said.

The format is worked out from the content, not from the name, because a subtitle file is named for the format somebody expected it to be in.

When you would use it

  • At the front of anything that reads recordings. Everything downstream works on cues and never has to know which of the three formats a recording arrived with.
  • Cleaning up automatic captioning. It repeats each line across overlapping windows, so the same sentence arrives three or four times over; indexed as they come, every one is a separate hit for the same words at almost the same timestamp, which buries everything else in a result list.
  • Getting markup and sound cues out of the way. <v Speaker> tags, styling, [Music], [Applause] — none of it is speech, and a search that matches [Music] matches every recording with a theme tune.

Install

cargo install --git https://git.pumpin.online/funkpower/catena cuenorm

Example

A WebVTT file with a speaker tag, a sound cue, and a repeated line:

$ cat episode.vtt
WEBVTT

00:00:01.000 --> 00:00:04.500
<v Host>The mill stood at the northern end of the valley,

00:00:04.500 --> 00:00:08.000
where the fall of the river was steepest and the ice came latest.

00:00:08.000 --> 00:00:11.000
[Music]

00:00:11.000 --> 00:00:15.500
Its wheel was undershot, which was wasteful,

00:00:15.000 --> 00:00:19.000
Its wheel was undershot, which was wasteful,

00:00:19.000 --> 00:00:24.000
and nobody replaced it for two hundred years.
$ cuenorm episode.vtt
{"v":1,"start":1.0,"end":4.5,"text":"The mill stood at the northern end of the valley,"}
{"v":1,"start":4.5,"end":8.0,"text":"where the fall of the river was steepest and the ice came latest."}
{"v":1,"start":11.0,"end":19.0,"text":"Its wheel was undershot, which was wasteful,"}
{"v":1,"start":19.0,"end":24.0,"text":"and nobody replaced it for two hundred years."}

The speaker tag is gone, [Music] left no cue at all, and the repeated line became one cue spanning both — 11.0 to 19.0, the earliest start and the latest end.

In a pipeline:

cuenorm episode.vtt | cuepack --seconds 75 > passages.ndjson
cuenorm episode.srt | cueseg --duration 7245 > chapters.ndjson

Options

Option Effect
--keep-repeats Keep consecutive repeats instead of merging them
--man Write this page as a manual page, in roff

Exit codes

Code Meaning
0 Done
64 The command line was wrong
74 The file could not be read

Bytes that are not UTF-8 are replaced rather than refused. A subtitle file downloaded from anywhere is as likely to be Latin-1, and a cue with one wrong character still has its timing and most of its words.

The formats

Format Recognised by
WebVTT The WEBVTT line at the front
SubStation Alpha A [Script Info] or [Events] section
SubRip Neither of the above

SubStation Alpha events are rows under a Format: line that names the columns, so the column order is read rather than assumed. Override blocks ({\an8}), line breaks (\N) and drawing commands are handled: a drawing command is a shape, not speech, and reads as a string of coordinates if it is kept.

A WebVTT cue line carries positioning settings after the end time, which are ignored — that is also what lets an old SubRip file with screen coordinates after its timestamps read at all.

As a library

let cues = cuekit::merge_repeats(&cuekit::parse(&content));
for cue in &cues {
    println!("{:.1}–{:.1} {}", cue.start, cue.end, cue.text);
}

See cuekit for the parsers one at a time.

See also

  • cuepack — cues into passages worth embedding.
  • cueseg — cues into topic chapters.
  • cuekit — the library this is a command over.