Skip to content

VTT Reader: handle WebVTT files with bad signatures - #536

Merged
palemieux merged 6 commits into
masterfrom
issues/handle-vtt-bad-signature
Aug 18, 2026
Merged

VTT Reader: handle WebVTT files with bad signatures#536
palemieux merged 6 commits into
masterfrom
issues/handle-vtt-bad-signature

Conversation

@palemieux

Copy link
Copy Markdown
Contributor

No description provided.

@eeshsaxena eeshsaxena left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nicer fix than my guard, the spec-based signature check is the right call and it covers the empty-file case cleanly.

One thing worth handling: to_model now returns None when the first line is not a valid signature, but the CLI caller assumes a document. In tt.py around line 356 it does model = vtt_reader.to_model(f, None, progress_callback_read), and from there model is used directly (model.set_lang(...) and the filter/writer path). So converting a .vtt whose first line is not WEBVTT would now raise AttributeError: 'NoneType' object has no attribute ... at the call site instead of failing in the reader. Might be worth erroring out cleanly there when to_model returns None, similar to the unsupported-input branch just below.

@palemieux

Copy link
Copy Markdown
Contributor Author

@eeshsaxena addressed at 944c601

@palemieux
palemieux requested a review from eeshsaxena August 17, 2026 16:34

@eeshsaxena eeshsaxena left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on, @palemieux. The signature handling and the extra coverage (test_bad_signature_1..5, the empty-file cases, and excluding invalid/ from test_valid_samples) look solid, and returning None on a bad signature is a clean contract.

Two things I noticed in the file-parsing preprocessing in reader.py:

1. The NUL replacement doesn't actually target NUL.

data_file.read().replace("\^@", "\uFFFD")

"\^@" isn't a NUL byte. \^ is an invalid escape sequence, so the literal is the three characters \, ^, @ (and on Python 3.12+ it raises a SyntaxWarning that is slated to become an error). A real U+0000 in the input is therefore left in place:

>>> ("WEBVTT\x00x").replace("\^@", "\uFFFD")
'WEBVTT\x00x'    # NUL still present
>>> ("WEBVTT\x00x").replace("\x00", "\uFFFD")
'WEBVTT\ufffdx'  # replaced

The spec step is "Replace each U+0000 NULL character ... with a U+FFFD", so this looks like it wants .replace("\x00", "\uFFFD") (or "\u0000").

2. Leftover chained comparison.

if len(subtitle_lines) > 0 is not None:

The is not None is left over from the old subtitle_text is not None check. It parses as (len(subtitle_lines) > 0) and (0 is not None), so it still happens to mean len(subtitle_lines) > 0, but it is easy to misread. if subtitle_lines: would be clearer.

Overall this is the better approach and handles more cases than #534 did, so I am happy to close mine in favor of it. Thanks!

@palemieux

Copy link
Copy Markdown
Contributor Author
  1. The NUL replacement doesn't actually target NUL.
    data_file.read().replace("^@", "\uFFFD")

Where do you see this in the source code? I see:

lines = data_file.read().replace("\u0000", "\uFFFD") \

if len(subtitle_lines) > 0 is not None:

Fixed at d781f31

@palemieux
palemieux requested a review from eeshsaxena August 17, 2026 17:37
@palemieux

Copy link
Copy Markdown
Contributor Author

@eeshsaxena I plan on merging this soon

@palemieux
palemieux merged commit bcf311a into master Aug 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants