Closes#52. Users who already have correct, pre-synced subtitles can now
skip ASR entirely — they upload a video as normal and then hit "Import
.srt" instead of "Upload & Transcribe". The .srt cues populate the dub
segment list directly, so the rest of the pipeline (translate, dub,
export) just works.
Backend
- services/srt_parser.py: lenient SubRip parser. Tolerates BOM, CRLF,
missing index numbers, dot-vs-comma ms separator, and overlap (shifts
the later cue's start to the earlier's end rather than dropping). Skips
cues with non-positive duration or empty bodies; reports counts so the
UI can warn.
- dub_core.py: new POST /dub/import-srt/{job_id} accepts the .srt file,
parses it, clamps cues that run past the source media's duration, and
replaces job["segments"]. Tries UTF-8 with BOM first, falls back to
latin-1 for legacy Windows subs.
Frontend
- api/dub.ts: dubImportSrt helper with a typed response.
- hooks/useDubWorkflow.js: handleDubImportSrt — sets segments, flips
dubStep to 'editing', shows a toast with per-bucket counts (imported /
skipped / overlap-shifted / clamped) so the user sees what happened.
- pages/DubTab.jsx: "Import .srt" button next to "Upload & Transcribe"
once a job exists, plus a smaller "Import .srt instead" affordance in
the transcription-failure banner — the exact recovery path the
reporter asked for.
Tests
- tests/test_srt_parser.py: 12 cases covering well-formed input,
multi-line cues, dot-as-separator, BOM, CRLF, malformed cues, empty
bodies, overlap shift, overlap-becomes-zero-drop, missing indices,
empty input, and segment shape (sequential ids, speaker filler).
pytest is now 226 passed (was 214); vitest unchanged at 11.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>