Metadata-Version: 2.5
Name: chunkscribe
Version: 0.1.0
Summary: Silence-aware chunking and OpenAI transcription for long audio.
Project-URL: Homepage, https://github.com/aug2uag/chunkscribe
Project-URL: Issues, https://github.com/aug2uag/chunkscribe/issues
Author-email: Ray Fatahi <aug2uag@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: audio,openai,speech-to-text,transcription,whisper
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
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 :: Sound/Audio :: Speech
Classifier: Topic :: Text Processing :: Linguistic
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Requires-Dist: openai>=1.40.0
Requires-Dist: python-dotenv>=1.0.0
Description-Content-Type: text/markdown

# chunkscribe

Split long audio at natural pauses and transcribe it with the OpenAI API.

Transcription endpoints cap uploads at 25 MB, which forces you to chunk anything
longer than roughly ten minutes. Cutting on a fixed clock clips words mid-syllable
at every boundary. `chunkscribe` cuts at silences instead: you set a target
duration, and it extends to the next qualifying pause.

## Install

```bash
pip install chunkscribe
```

Set your key in a `.env` file beside the audio, or in the environment:

```
OPENAI_API_KEY=sk-...
```

WAV input needs nothing else. Other formats require `pip install chunkscribe[mp3]`
and an `ffmpeg` binary on `PATH`.

## Use

```bash
chunkscribe SOURCE [OUTPUT] [options]
```

Inspect the split before spending anything:

```bash
chunkscribe lecture.wav --dry-run
```

```
14 chunks, 176.3 min of speech, ~$1.06
  part_000.wav      0.0 →    9.7 min ( 9.7 min, 18.6 MB)
  part_001.wav      9.8 →   19.2 min ( 9.4 min, 18.0 MB)
  ...
```

Then run it:

```bash
chunkscribe lecture.wav transcript.txt --language en --timestamps
```

Omit `OUTPUT` to write alongside the input as `.txt`.

## Options

| Flag | Default | |
|---|---|---|
| `--dry-run` | off | split and report, no API calls |
| `-f`, `--overwrite` | off | replace an existing output file |
| `-q`, `--quiet` | off | suppress the per-chunk listing |
| `--workdir DIR` | `.chunkscribe/` | cache location |
| `--min-dur SEC` | 420 | earliest legal cut point |
| `--max-dur SEC` | 600 | hard ceiling; keeps chunks under 25 MB |
| `--max-silence SEC` | 0.6 | pause length that qualifies as a cut |
| `--energy N` | 50 | speech/silence threshold |
| `--model NAME` | `gpt-4o-transcribe` | |
| `--language ISO` | autodetect | e.g. `en` |
| `--prompt TERMS` | — | seed names, jargon, expected spellings |
| `--timestamps` | off | prefix each chunk with its source offset |

## Tuning the split

Run `--dry-run` and read the chunk list. Two failure modes:

**One region, or very few.** `--energy` is too low, so room tone counts as speech
and no pause ever qualifies. Raise it to 55–60.

**Most chunks land on exactly `--max-dur`.** No qualifying pause is being found,
so every cut is a blind ceiling cut. Raise `--max-silence` to 0.4, or lower
`--energy`. A warning fires automatically when this happens to more than half
the chunks.

A three-hour lecture typically settles at 18–25 regions.

## Caching and resume

Chunk audio and per-chunk transcripts are cached under `--workdir`. Interrupt a
run and re-issue the same command: completed chunks are reused and cost nothing.

The cache directory name includes a fingerprint of the split settings, so
changing `--energy` or `--min-dur` writes to a fresh directory rather than
pairing new boundaries with stale transcripts. Old directories are never
cleaned up automatically — `rm -rf .chunkscribe/` when you're done.

## Accuracy notes

`--prompt` seeds the first chunk's vocabulary. After that each chunk is primed
with the tail of the previous transcript, which keeps names and terminology
consistent across boundaries. Put rare terms in `--prompt` even if they appear
late; the carry-over window is only 800 characters.

Because transcription is sequential to preserve that carry-over, a long file
takes a while. Parallelising would be faster and less consistent.

Silences longer than `--max-silence` are dropped between regions, so chunk
durations won't sum to the full runtime. That's expected. Timestamps come from
each region's true offset in the source, not from accumulated durations.

## Python API

```python
from pathlib import Path
from chunkscribe import transcribe, SplitConfig

text = transcribe(
    "lecture.wav",
    split_cfg=SplitConfig(min_dur=300, max_dur=540, energy_threshold=55),
    timestamps=True,
)
Path("transcript.txt").write_text(text, encoding="utf-8")
```

Lower-level pieces, if you want the chunk boundaries themselves:

```python
from chunkscribe import SplitConfig, TranscribeConfig, split, transcribe_chunks, render, workdir_for

cfg = SplitConfig()
chunks = split(Path("lecture.wav"), workdir_for(Path("lecture.wav"), cfg), cfg)
for c in chunks:
    print(c.index, c.start, c.end, c.size)

results = transcribe_chunks(chunks, TranscribeConfig(language="en"))
print(render(results, timestamps=True))
```

## Exit codes

`0` success · `1` split failure · `2` bad arguments · `130` interrupted

## Cost

The `--dry-run` estimate uses a hardcoded per-minute rate. Verify it against
current OpenAI pricing before relying on it.

## License

MIT
