Merge branch 'fix/review-2077' into fix/community-integration
# Conflicts: # CHANGELOG.md # docs/dubbing/translation-engines.md
This commit is contained in:
@@ -41,6 +41,9 @@ the frozen-backend fallback mirror it for their toolchains.
|
||||
|
||||
- Handle missing Electron signing credentials and retry packaging fixes without moving release tags (#2157)
|
||||
|
||||
- Electron packaging can recover without changing a release tag
|
||||
- Parse pasted WebVTT cues while separating metadata, identifiers, empty cues, and complete timing lines (#2077) — thanks @kevin9327!
|
||||
|
||||
## [0.5.3] — 2026-09-17
|
||||
|
||||
**Highlights**
|
||||
|
||||
@@ -86,8 +86,14 @@ def parse_srt(content: str) -> SrtParseResult:
|
||||
# example timestamp inside a NOTE/STYLE/REGION cannot become speech.
|
||||
blocks = []
|
||||
for block in re.split(r"\n[^\S\n]*\n", text):
|
||||
first = block.strip().split("\n", 1)[0].strip()
|
||||
if first in {"STYLE", "REGION"} or re.match(r"NOTE(?:[ \t]|$)", first):
|
||||
lines = block.strip("\n").split("\n")
|
||||
first = lines[0].strip()
|
||||
# WebVTT's block parser gives a timing line in position two
|
||||
# precedence over the identifier (including STYLE/REGION/NOTE).
|
||||
# https://www.w3.org/TR/webvtt1/#file-parsing
|
||||
identifies_cue = len(lines) > 1 and _TIMING_RE.match(lines[1])
|
||||
metadata = first in {"STYLE", "REGION"} or re.match(r"NOTE(?:[ \t]|$)", first)
|
||||
if metadata and not identifies_cue:
|
||||
continue
|
||||
blocks.append(block)
|
||||
text = "\n\n".join(blocks)
|
||||
@@ -110,7 +116,7 @@ def parse_srt(content: str) -> SrtParseResult:
|
||||
if is_webvtt:
|
||||
# The blank separator ends WebVTT dialogue; following identifiers,
|
||||
# NOTE/STYLE blocks belong outside the cue, even when numeric.
|
||||
body = re.split(r"\n[^\S\n]*\n", body.lstrip("\n"), maxsplit=1)[0]
|
||||
body = re.split(r"\n[^\S\n]*\n", body, maxsplit=1)[0]
|
||||
# An index must directly precede the next timing line. A blank line
|
||||
# AFTER a number instead marks that number as preceding dialogue.
|
||||
next_index = None
|
||||
|
||||
@@ -263,3 +263,5 @@ Mixed or malformed SRT files can make a bare number indistinguishable from spoke
|
||||
If the Argos native runtime cannot load, both desktop and browser clients show localized recovery guidance: reinstall the backend or select NLLB. The API returns the stable `argos_runtime_unavailable` error code without exposing native library paths.
|
||||
|
||||
WebVTT import excludes NOTE, STYLE, and REGION metadata blocks before parsing timestamps; timing examples in notes never become spoken segments.
|
||||
|
||||
WebVTT import separates metadata blocks from cue identifiers using the [WebVTT block-parsing rules](https://www.w3.org/TR/webvtt1/#file-parsing): a timing line immediately after an identifier makes a cue, even when that identifier is NOTE, STYLE, or REGION. Later timing examples inside metadata are ignored, and empty cues never borrow the next cue’s identifier as dialogue.
|
||||
|
||||
@@ -375,7 +375,7 @@ def test_numeric_only_cue_after_invalid_cue_is_not_discarded():
|
||||
|
||||
@pytest.mark.parametrize('header', ['NOTE', 'NOTE translator notes', 'NOTE\ttranslator notes', 'STYLE', 'REGION'])
|
||||
def test_webvtt_metadata_timestamps_never_become_dialogue(header):
|
||||
text = f'WEBVTT\n\n{header}\n00:00.000 --> 00:02.000\nMetadata only\n\ncue\n00:03.000 --> 00:04.000\nReal dialogue\n'
|
||||
text = f'WEBVTT\n\n{header}\nMetadata content\n00:00.000 --> 00:02.000\nMetadata only\n\ncue\n00:03.000 --> 00:04.000\nReal dialogue\n'
|
||||
result = parse_srt(text)
|
||||
assert [cue['text'] for cue in result.segments] == ['Real dialogue']
|
||||
assert result.segments[0]['start'] == 3
|
||||
@@ -384,3 +384,17 @@ def test_webvtt_metadata_timestamps_never_become_dialogue(header):
|
||||
def test_webvtt_note_words_inside_dialogue_are_retained():
|
||||
text = 'WEBVTT\n\n00:01.000 --> 00:02.000\nNOTE this is spoken\nSTYLE\nREGION\n'
|
||||
assert parse_srt(text).segments[0]['text'] == 'NOTE this is spoken\nSTYLE\nREGION'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('identifier', ['STYLE', 'REGION', 'NOTE', 'NOTE identifier'])
|
||||
def test_webvtt_metadata_words_can_identify_a_cue(identifier):
|
||||
text = f'WEBVTT\n\n{identifier}\n00:01.000 --> 00:02.000\nSpoken text\n'
|
||||
assert [cue['text'] for cue in parse_srt(text).segments] == ['Spoken text']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('gap', ['\n', ' \n', '\n\n'])
|
||||
def test_empty_webvtt_cue_does_not_capture_following_identifier(gap):
|
||||
text = f'WEBVTT\n\n00:01.000 --> 00:02.000\n{gap}next-id\n00:03.000 --> 00:04.000\nSpoken text\n'
|
||||
result = parse_srt(text)
|
||||
assert result.skipped_cues == 1
|
||||
assert [cue['text'] for cue in result.segments] == ['Spoken text']
|
||||
|
||||
Reference in New Issue
Block a user