mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-22 18:57:14 +00:00
105 lines
3.3 KiB
Markdown
105 lines
3.3 KiB
Markdown
# cuekit
|
|
|
|
The cue model, and the three subtitle formats it is read from.
|
|
|
|
Part of [catena](../../README.md).
|
|
|
|
## What it is
|
|
|
|
A library. Three formats go in — SubRip, WebVTT and SubStation Alpha — and
|
|
one [`cue`](../../docs/records.md#cue--one-line-of-a-transcript) stream comes out.
|
|
Everything downstream works on cues and never has to know which of the three a
|
|
recording arrived with.
|
|
|
|
[`cuenorm`](../cuenorm/README.md) is a command over this.
|
|
|
|
## When you would use it
|
|
|
|
- **Reading subtitles in your own program**, without shelling out.
|
|
- **Adding a fourth format**, or handling one of these three differently.
|
|
- **Anywhere timed text is the input** and the format is whatever somebody
|
|
happened to have.
|
|
|
|
## Reading a file
|
|
|
|
```rust
|
|
let cues = cuekit::parse(&content);
|
|
```
|
|
|
|
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 it
|
|
is already known:
|
|
|
|
```rust
|
|
use cuekit::Format;
|
|
|
|
let cues = cuekit::parse_as(&content, Format::WebVtt);
|
|
assert_eq!(cuekit::detect(&content), Format::WebVtt);
|
|
```
|
|
|
|
The parsers are also available directly — `cuekit::srt::parse` (which reads
|
|
WebVTT too) and `cuekit::ass::parse`.
|
|
|
|
## The cue
|
|
|
|
```rust
|
|
use cuekit::Cue;
|
|
|
|
let cue = Cue::new(12.5, 15.0, "the mill stood at the northern end");
|
|
assert_eq!(cue.seconds(), 2.5);
|
|
```
|
|
|
|
Deliberately smaller than a [chunk](../../docs/records.md#chunk--a-retrievable-passage).
|
|
A cue is what a subtitle file holds — a few seconds, often half a sentence —
|
|
and turning cues into passages worth retrieving is a later stage's job.
|
|
|
|
`seconds()` is negative if the file says a cue ends before it starts, which
|
|
some do.
|
|
|
|
## Merging the repeats
|
|
|
|
```rust
|
|
let merged = cuekit::merge_repeats(&cues);
|
|
```
|
|
|
|
Consecutive cues with identical text become one, keeping the earliest start
|
|
and the latest end.
|
|
|
|
Automatic captioning emits a rolling window: the same line appears in cue
|
|
after cue as the window slides, so a ten-minute stretch of speech can arrive
|
|
as three times as many cues as there are sentences. Indexed as they come,
|
|
every one of those is a separate hit for the same words at almost the same
|
|
timestamp.
|
|
|
|
## Cleaning a cue's text
|
|
|
|
```rust
|
|
assert_eq!(cuekit::text::clean("<v Host>[Music] The mill stood"), "The mill stood");
|
|
```
|
|
|
|
Styling and speaker tags go; bracketed sound cues — `[Music]`, `[Applause]`,
|
|
`[inaudible]` — go with them, because they are not speech and a search that
|
|
matches them matches every recording with a theme tune.
|
|
|
|
## Timestamps
|
|
|
|
```rust
|
|
assert_eq!(cuekit::srt::timestamp("00:01:23,456"), 83.456);
|
|
assert_eq!(cuekit::ass::timestamp("0:01:23.45"), 83.45);
|
|
```
|
|
|
|
Both return `NaN` rather than an error for something that is not a timestamp,
|
|
which is what lets a malformed block be dropped whole instead of guessed at.
|
|
|
|
`cuekit::jsnum` holds the number parsing these use. It reproduces the two
|
|
JavaScript conversions the original parser was written against, which disagree
|
|
about almost everything an ill-formed timestamp can contain — an empty string
|
|
is zero to one of them and not a number to the other. Reproducing both is what
|
|
makes a malformed file drop the same cues here as there.
|
|
|
|
## See also
|
|
|
|
- [`cuenorm`](../cuenorm/README.md) — the command over this.
|
|
- [`cuepack`](../cuepack/README.md) and [`cueseg`](../cueseg/README.md) — what reads cues.
|
|
- [The records](../../docs/records.md) — the cue shape on the wire.
|