Metadata-Version: 2.4
Name: cuewright
Version: 0.1.0
Summary: Transcribe speech, translate it, and produce verified subtitles.
Project-URL: Homepage, https://github.com/habibkaratas/cuewright
Project-URL: Issues, https://github.com/habibkaratas/cuewright/issues
License: MIT License
        
        Copyright (c) 2026 cuewright contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: ffmpeg,srt,subtitles,transcription,translation,whisper
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.10
Requires-Dist: faster-whisper>=1.1
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# cuewright

[![CI](https://github.com/habibkaratas/cuewright/actions/workflows/ci.yml/badge.svg)](https://github.com/habibkaratas/cuewright/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/cuewright)](https://pypi.org/project/cuewright/)
[![Python](https://img.shields.io/pypi/pyversions/cuewright)](https://pypi.org/project/cuewright/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

Transcribe speech, translate it, and produce subtitles you can actually trust.

Built around one idea: **timing and wording live in separate files.** Transcription
establishes timestamps once. Every language track then supplies only text, keyed by
segment id. Editing a translation costs nothing to re-render, and all your subtitle
tracks stay frame-accurate with each other because none of them invents its own timing.

The other half of the project is verification. Machine transcription fails quietly —
a dropped sentence or a misheard word looks exactly like a correct one until someone
watches the whole video. `cuewright` ships the checks that narrow down where to look.

## Install

```bash
pip install cuewright
```

Requires Python 3.10+ and [ffmpeg](https://ffmpeg.org/) on `PATH`.

```
Windows : winget install Gyan.FFmpeg
macOS   : brew install ffmpeg
Debian  : sudo apt install ffmpeg
```

If ffmpeg lives somewhere unusual, point at it with `CUEWRIGHT_FFMPEG` (and
`CUEWRIGHT_FFPROBE`). A static build unpacked into `tools/` is also found automatically.

## Quick start

Turning an Arabic-language video into Turkish subtitles:

```bash
# 1. Transcribe. Writes video.ar.srt and video.ar.segments.json
cuewright transcribe video.mp4 --language ar

# 2. Emit a translation skeleton, one "[id] text" line per segment
cuewright template video.ar.segments.json -o video.tr.txt --with-source

# 3. Translate the text in video.tr.txt however you like, keeping the [id] markers

# 4. Build the subtitle file — timings come from the segments, text from your file
cuewright build video.ar.segments.json video.tr.txt video.tr.srt

# 5. Optionally render it into the picture
cuewright burn video.mp4 video.tr.srt video.tr.mp4
```

Step 4 refuses to write anything if the ids do not line up exactly. That is
deliberate: one skipped line would slide every later cue onto the wrong shot,
which is far harder to spot than an error at build time.

For subtitles in the source language, skip steps 2–4:

```bash
cuewright export video.ar.segments.json video.ar.vtt
```

## Verifying the result

Machine transcription goes wrong in two distinct ways, and they need different checks.

**Dropped speech.** Voice-activity detection sometimes mistakes quiet speech for
silence. `verify` finds every stretch the segments do not cover and measures its
audio level — a gap carrying speech-level sound is worth listening to.

```bash
cuewright verify video.ar.segments.json video.mp4
```

```
49 segments | speech 242s / 251s (96.5%)

coverage gaps (2)
  reference speech level: -17.8 dB
     0.00s -    5.87s ( 5.87s) | mean -22.3 dB, peak -7.2 dB | before segment 1  <-- speech-level audio, check this
   247.76s -  250.63s ( 2.87s) | mean -32.0 dB, peak -19.2 dB | end of media
```

**Misheard words.** Decode the same audio a second time with different settings,
then compare. Where the passes agree the transcript is dependable; where they
diverge the model was unsure.

```bash
cuewright transcribe video.mp4 --language ar --no-vad --tag check
cuewright compare video.ar.segments.json video.check.segments.json
```

```
word-level agreement: 99.76%

  [replace] … توفر رمال … المحلية عالية الجودة …
      A: السلك
      B: السيليكا
```

That single disagreement was a real transcription error, found without anyone
listening to the audio.

**Be clear about the limit:** both passes usually share a model, so a mistake the
model makes *consistently* survives this check. It finds uncertainty, not truth.
Proper nouns and numbers still deserve a human ear.

Finally, validate the subtitle file itself — numbering, overlaps, line lengths:

```bash
cuewright check video.tr.srt
```

## Subtitle formatting

Cues are wrapped to two lines of ~42 characters. Segments longer than that are split
into several cues with time apportioned by length, preferring sentence and clause
boundaries. When no punctuation is available the splitter scores candidate positions
so it will not separate a number from its unit (`400 | metre`) or strand a
conjunction at the end of a block.

## Using it as a library

```python
from cuewright import SegmentFile, transcribe, verify, write_srt
from cuewright.transcribe import TranscribeOptions

result = transcribe("video.mp4", TranscribeOptions(model="large-v3", language="ar"))
result.save("video.segments.json")
write_srt(result.segments, "video.ar.srt")

report = verify(result, "video.mp4")
print(f"coverage {report.coverage:.1%}, {len(report.anomalies)} anomalies")
```

## Commands

| Command | Purpose |
| --- | --- |
| `transcribe` | Speech to timestamped segments and an SRT |
| `template` | Translation skeleton (`[id] text` per segment) |
| `build` | Segments plus a translation to SRT or VTT |
| `export` | Segments to SRT or VTT in the source language |
| `burn` | Render subtitles into the picture |
| `verify` | Find dropped speech and implausible segments |
| `compare` | Cross-check two transcription passes |
| `check` | Validate SRT structure |

Run `cuewright <command> --help` for the full set of options.

## Notes on quality

- Transcription runs with `condition_on_previous_text=False`. Feeding previous text
  back in makes the model prone to repetition loops; losing a little cross-segment
  context is the cheaper trade.
- `burn` copies the audio stream untouched and re-encodes only the video, because
  drawing text necessarily changes the picture.
- Subtitles are resolution-independent. If you later obtain a higher-resolution
  master, re-run `burn` with a proportionally larger `--font-size` — there is no
  need to transcribe again.

## Development

```bash
git clone https://github.com/habibkaratas/cuewright
cd cuewright
pip install -e ".[dev]"
pytest
ruff check .
```

The test suite runs without ffmpeg or model weights: the structural half of every
check is unit-testable on its own.

## License

MIT
