Skip to content

v0.1.0 — Initial release

Choose a tag to compare

@vitormf vitormf released this 28 May 13:46
· 499 commits to main since this release

`submatch` verifies that a subtitle file actually matches the audio content of a video — catching the case where subtitle tools like subliminal or Bazarr return correctly-timed but wrong-content subtitles.

Install

```bash
pip install submatch
```

System dependencies: `ffmpeg` (`brew install ffmpeg`) and `ffsubsync` (`pip install ffsubsync`).

What's in this release

Core

  • Transcribes short audio segments with Whisper and scores against subtitle text using token F1
  • Dialogue-density segment sampling — picks the 30s windows with the most subtitle words per zone, skipping intros/credits
  • Timing drift detection via ffsubsync, flagging offsets > 2s
  • Three language signals: Whisper audio language, langdetect on subtitle text, filename convention + ffprobe metadata
  • 4-state result system: PASS, DRIFT (content matches but timing drift detected), FAIL (wrong content), UNSURE (insufficient transcription data)
  • --resync: auto-correct drift in place on DRIFT; --pass-unsure: exit 0 for UNSURE results
  • --keep-synced: save the timing-corrected subtitle to disk; --delete-failures: remove subtitle files that fail the match check

Cross-language matching

When subtitle and audio languages differ (e.g. English audio + Portuguese subtitles), scoring automatically switches from token F1 to multilingual semantic similarity via paraphrase-multilingual-MiniLM-L12-v2. Use --cross-threshold to tune the cutoff independently.

Batch mode

  • Pair a directory of videos with their same-stem subtitles, or score one video against a subtitle directory
  • --recursive / -r for Plex/Kodi/Jellyfin nested library layouts
  • --sub-lang CODE to filter by language tag (e.g. pt, en, pt-BR)
  • --filter GLOB to filter by filename pattern
  • --workers for parallel processing; --device to target CPU, MPS (Apple Silicon), or CUDA
  • Live progress with ETA, in-place result lines, and --compact one-line-per-pair summary

Subtitle formats

SRT, WebVTT, and ASS/SSA — via pysubs2.

Output

Human-readable with ANSI colour, or --json for machine-readable output. Transcription results are cached per video so re-runs against a different subtitle skip re-transcription.

States and exit codes

State Meaning Exit code
PASS Content matches, no timing drift 0
DRIFT Content matches, but timing drift detected 1 (use --resync to fix in place)
FAIL Content does not match 1
UNSURE Not enough transcription data to decide 1 (use --pass-unsure to exit 0)
Error (missing dependency, unreadable file, no audio track) 2