diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c19c29..693d0ab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ the frozen-backend fallback mirror it for their toolchains. - Handle missing Electron signing credentials and retry packaging fixes without moving release tags (#2157) +### Fixed + +- Parse pasted WebVTT cues without treating mid-sentence timestamps as subtitle records (#2077) — thanks @kevin9327! + ## [0.5.3] — 2026-09-17 **Highlights** @@ -70,7 +74,6 @@ the frozen-backend fallback mirror it for their toolchains. - Video previews show their thumbnail before playback, including the source video in Dub (#2129) - Linux and Windows workspace headers consistently expand and collapse the sidebar, with the app logo at the top of the collapsed rail (#2129) - Stopping a process on macOS no longer fails with "Operation not permitted" when it was already exiting (#2032) -- A WebVTT file loaded into Paste translation maps its cues, instead of its timing lines, cue labels or notes (#2077) - A YouTube link blocked by its "not a bot" check now says how to attach signed-in cookies in Dub, instead of quoting yt-dlp's command-line flags (#2036, #2034) - An engine that fails to start now says whether it timed out, crashed (with its exit code and last output) or answered wrongly, instead of "did not signal ready: None" (#2037, #2026) - Transcribing an M4A file with PyTorch Whisper works, instead of failing with "Format not recognised" (#2042, #2039) diff --git a/docs/dubbing/translation-engines.md b/docs/dubbing/translation-engines.md index f1654ca5..6e50aedd 100644 --- a/docs/dubbing/translation-engines.md +++ b/docs/dubbing/translation-engines.md @@ -234,3 +234,5 @@ panels instead so neither editor becomes unusably small. from-source checkout. - **Installed it but still "needs install"** — restart the backend so Python picks up the newly-installed module. + +Paste translation accepts WebVTT files with hourless timestamps. Only timing records at line starts activate timestamp matching; timestamp-like text inside a sentence remains dialogue. diff --git a/frontend/src/test/dubPasteTranslation.test.jsx b/frontend/src/test/dubPasteTranslation.test.jsx index 99d872d8..42d9159e 100644 --- a/frontend/src/test/dubPasteTranslation.test.jsx +++ b/frontend/src/test/dubPasteTranslation.test.jsx @@ -426,3 +426,8 @@ describe('DubPasteTranslationDialog', () => { expect(screen.getByRole('button', { name: /Apply/i })).toBeDisabled(); }); }); + + +it('keeps an hourless timestamp embedded in prose as plain text', () => { + expect(detectPasteMode('Continue at 01:30.000 --> the finale')).toBe('plain'); +}); diff --git a/frontend/src/utils/pasteTranslations.js b/frontend/src/utils/pasteTranslations.js index 209130ff..0ab2116b 100644 --- a/frontend/src/utils/pasteTranslations.js +++ b/frontend/src/utils/pasteTranslations.js @@ -26,7 +26,7 @@ // A timing line, e.g. `00:00:01,000 --> 00:00:04,500` (`.` ms separator, // missing leading zeros and WebVTT's hourless `00:01.000` allowed — mirrors // backend/services/srt_parser.py). -const TIMING_RE = /(?:\d{1,2}:)?[0-5]?\d:[0-5]?\d[,.]\d{1,3}\s*-->/; +const TIMING_RE = /^[^\S\n]*(?:\d{1,2}:)?[0-5]?\d:[0-5]?\d[,.]\d{1,3}[^\S\n]*-->/m; // `1. text` / `2) text` / `[3] text` / `4 - text` / `5: text`. const NUMBERED_RE = /^\s*(?:\[\s*(\d{1,5})\s*\]|\(\s*(\d{1,5})\s*\)|(\d{1,5}))\s*[.):\-—]?\s+(.*)$/;