mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 13:05:09 +00:00
fix(subtitle): keep WebVTT-only cue markup out of converted subtitles
Strip cue classes, <v>/<lang>/ruby and karaoke timestamps before conversion, decode entities for SRT/SSA/ASS, load pysubs2 as utf-8-sig, and route sdh_method: filter-subs through convert_before_strip instead of raising.
This commit is contained in:
@@ -55,6 +55,9 @@ format-appropriate clean-up to make a single, valid file:
|
||||
that confuse downstream parsers, and merges multi-line cues that a service split into
|
||||
separate overlapping cues. By default unshackle keeps the original styling and layout
|
||||
(see [`preserve_formatting`](#subtitle-configuration)).
|
||||
- **Cue markup normalisation**: unshackle removes the WebVTT-only parts of a cue
|
||||
payload, so no player draws them as dialogue. See
|
||||
[Cue markup](#cue-markup-and-html-entities).
|
||||
- **TTML for muxing**: Matroska cannot carry TTML. If you do not request a specific
|
||||
output format, unshackle automatically converts any plain TTML track to WebVTT before
|
||||
muxing (the closest lossless equivalent). Boxed `fTTML` is likewise unwrapped to TTML
|
||||
@@ -188,6 +191,20 @@ itself. The default (`auto`) prefers the `subby` cleaner for SRT, falls back to
|
||||
SubtitleEdit when you install it, and otherwise uses the built-in `filter-subs`
|
||||
approach.
|
||||
|
||||
Every engine removes the same three things: bracketed sound effects (`[MUSIC PLAYING]`,
|
||||
`(door creaks)`), speaker labels (`MAN:`), and font tags. Every engine keeps plain
|
||||
dialogue and inline `<i>` styling. They differ on music and lyric lines:
|
||||
|
||||
| `sdh_method` | Engine that runs | Lyric lines (`♪ ... ♪`) |
|
||||
| --- | --- | --- |
|
||||
| `auto` | `subby` for an SRT track. SubtitleEdit for any other format when you install it, otherwise `filter-subs`. | Kept, unless the fallback reaches `filter-subs`. |
|
||||
| `subby` | `subby`, but only for a track that is already SRT. Any other format falls through to `filter-subs`. | Kept for SRT, removed otherwise. |
|
||||
| `subtitleedit` | SubtitleEdit when you install it, otherwise `filter-subs`. | Kept with SubtitleEdit, removed without it. |
|
||||
| `filter-subs` | `filter-subs` always. | Removed. |
|
||||
|
||||
Pick `filter-subs` when you want lyrics gone as well. Pick `subby` or install
|
||||
SubtitleEdit when you want to read along with the songs.
|
||||
|
||||
!!! note "Why `convert_before_strip` exists"
|
||||
`subby`'s SDH stripper only operates on SRT. Handed any other codec it returns
|
||||
silently without stripping anything, not an error but a no-op that leaves the SDH
|
||||
@@ -288,6 +305,28 @@ Reading the table:
|
||||
Install [SubtitleEdit / `seconv`](#subtitle-configuration) if you need colour or the
|
||||
highest → SRT fidelity.
|
||||
|
||||
### Cue markup and HTML entities
|
||||
|
||||
Styling is not the only thing a conversion has to handle. WebVTT carries markup that no
|
||||
other format understands, and a player that does not know a tag draws it as dialogue.
|
||||
unshackle removes that markup before any backend runs, so the result is the same for
|
||||
every `conversion_method`:
|
||||
|
||||
| WebVTT markup | Result |
|
||||
| --- | --- |
|
||||
| `<c.bg_transparent>text</c>` | Tags removed, text kept. A class only selects a CSS `::cue` rule that no player ever receives, so it carries no styling. |
|
||||
| `<i.loud>`, `<b.x>`, `<u.y>` | Class list removed, the styling tag kept. |
|
||||
| `<v Bob>`, `<lang ja>`, `<ruby>`, `<rt>` | Tags removed, text kept. |
|
||||
| `<00:00:02.000>` | Removed. These are karaoke timings that no subtitle player renders. |
|
||||
| `&`, ` `, `'` | Decoded to real characters for SRT, SSA, and ASS output. Kept escaped for WebVTT and TTML, which need them. |
|
||||
|
||||
!!! warning "A class list breaks more than you expect"
|
||||
FFmpeg reads SubRip markup as a tag only while the tag name stays inside
|
||||
`[0-9a-zA-Z_/]`. The dot in `<i.loud>` fails that test, so FFmpeg prints the opening
|
||||
tag on screen and then closes an italic run that never opened. The FFmpeg WebVTT
|
||||
reader matches `i`, `b`, and `u` exactly, so there the same class silently loses the
|
||||
styling. mpv, VLC, Plex, and Jellyfin all use one of those two readers.
|
||||
|
||||
!!! note "Config wins over a service's preference"
|
||||
A service may set a `preferred_conversion_method` on its own tracks (for example when
|
||||
it ships subtitles that a particular backend handles best). An explicit
|
||||
@@ -381,6 +420,32 @@ subtitle:
|
||||
| `group_by` | `type` | `type`, `language` | Whether all tracks of one type stay together, or each language stays next to its own variants. |
|
||||
| `language_priority` | *(unset)* | a list of languages | Languages to sort to the top. The rest keep the default order. Nothing is removed. |
|
||||
|
||||
### Which settings to change
|
||||
|
||||
The defaults give a clean, player-safe file, so most people change nothing. Start here
|
||||
only when you want something the defaults do not do:
|
||||
|
||||
| If you want | Set |
|
||||
| --- | --- |
|
||||
| Every original style kept, with no re-encode at all | `--sub-format original`, and `sidecar_format: original` for sidecars |
|
||||
| The highest conversion fidelity, including colour | Install SubtitleEdit. `conversion_method` stays `auto` |
|
||||
| Music and lyric lines stripped along with the sound effects | `sdh_method: filter-subs` |
|
||||
| Lyric lines kept | `sdh_method: subby`, or install SubtitleEdit |
|
||||
| No stripped copies made at all | `strip_sdh: false` |
|
||||
| One backend for every conversion, whatever you install | `conversion_method: <backend>` |
|
||||
|
||||
Two keys read as if they control markup, and do not:
|
||||
|
||||
- **`preserve_formatting`** decides whether unshackle re-encodes a WebVTT file after
|
||||
the repair pipeline. Left `true`, the cue text passes through as written. Set to
|
||||
`false`, unshackle re-encodes the file through pycaption, which merges identical cues
|
||||
and drops empty ones but also flattens styling. Neither setting leaves WebVTT-only
|
||||
markup in the file: [cue markup normalisation](#cue-markup-and-html-entities) runs
|
||||
either way.
|
||||
- **`convert_before_strip`** applies to SDH stripping, not to your output format. It
|
||||
converts the working copy to SRT so a non-SubtitleEdit engine can read it. Turning it
|
||||
off makes `subby` and `filter-subs` no-ops on anything that is not already SRT.
|
||||
|
||||
!!! tip "SubtitleEdit is optional but recommended"
|
||||
Several high-fidelity paths (the best-quality conversions and the SubtitleEdit SDH
|
||||
stripper) only activate when you install the SubtitleEdit CLI and put it on your `PATH`.
|
||||
|
||||
@@ -43,7 +43,7 @@ Common config keys. This is a useful subset, and every `dl` flag works:
|
||||
| `sub_format` | str | *(unset)* | Convert subtitles to this format (`srt`, `vtt`, `original`, ...). |
|
||||
| `forced_subs` | bool | `false` | Include forced subtitle tracks. |
|
||||
| `forced_s_lang` | list | `[]` | Forced subtitle language(s); implies `forced_subs`. A `-` prefix excludes. |
|
||||
| `no_subs` / `no_audio` / `no_chapters` | bool | `false` | Skip that track type. |
|
||||
| `no_subs` / `no_audio` / `no_chapters` / `no_attachments` | bool | `false` | Skip that track type. `no_attachments` also skips subtitle font attaching. |
|
||||
| `downloads` | int | `1` | Tracks downloaded concurrently. |
|
||||
| `workers` | int | *(downloader default)* | Threads per track. |
|
||||
| `speed_limit` | str | *(unlimited)* | Total download speed cap across all downloads combined, e.g. `500k`, `5M`, `1.5G` or plain bytes/sec. Values are bytes, not bits (`5M` = 5.0 MB/s). |
|
||||
@@ -132,7 +132,7 @@ in or writes them as sidecar files. See [Subtitles](../../guide/subtitles.md) fo
|
||||
|---------|------|---------|-------------|
|
||||
| `strip_sdh` | bool | `true` | Strip SDH/CC cues (hearing-impaired annotations) into a clean track. |
|
||||
| `sdh_method` | str | `"auto"` | SDH-stripping backend to use. |
|
||||
| `preserve_formatting` | bool | `true` | Keep styling/positioning when converting or stripping. |
|
||||
| `preserve_formatting` | bool | `true` | Pass WebVTT cue text through as written. `false` re-encodes it through pycaption, which merges identical cues but flattens styling. unshackle removes WebVTT-only cue markup either way. |
|
||||
| `convert_before_strip` | bool | `true` | Convert to the working format before stripping SDH. |
|
||||
| `conversion_method` | str | `"auto"` | Subtitle conversion backend. |
|
||||
| `output_mode` | str | `"mux"` | `mux` embeds subtitles in the MKV; `sidecar` writes separate files; `both` writes sidecars and still muxes. |
|
||||
|
||||
Reference in New Issue
Block a user