Metadata-Version: 2.4
Name: aria-transcribe
Version: 0.1.0
Summary: Drop-in CLI transcription. Groq by default, local Whisper opt-in.
Project-URL: Homepage, https://github.com/Nyavix/aria-transcribe
Project-URL: Repository, https://github.com/Nyavix/aria-transcribe
Author: Nvikelo Nyathi
License: MIT
License-File: LICENSE
Keywords: audio,cli,groq,speech-to-text,transcription,whisper
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: groq>=0.11
Requires-Dist: rich>=13
Requires-Dist: tomli-w>=1.0
Requires-Dist: typer>=0.12
Provides-Extra: all
Requires-Dist: faster-whisper>=1.0; extra == 'all'
Requires-Dist: keyring>=24; extra == 'all'
Requires-Dist: pyannote-audio>=3.1; extra == 'all'
Requires-Dist: torch>=2.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Provides-Extra: diarize
Requires-Dist: pyannote-audio>=3.1; extra == 'diarize'
Requires-Dist: torch>=2.0; extra == 'diarize'
Provides-Extra: keyring
Requires-Dist: keyring>=24; extra == 'keyring'
Provides-Extra: local
Requires-Dist: faster-whisper>=1.0; extra == 'local'
Description-Content-Type: text/markdown

# transcribe

Drop-in CLI transcription. Point it at an audio/video file (or a folder), get a transcript next to it. **Groq by default**, local Whisper opt-in (planned).

```bash
transcribe podcast.mp3                              # -> podcast.txt
transcribe ./voice-notes/                           # -> .txt next to every clip
transcribe meeting.mp4 -t srt                       # -> meeting.srt
transcribe lecture.m4a -t md --timestamps           # -> lecture.md with [mm:ss]
transcribe ./media/ -r -t json -o ./transcripts/    # recursive, JSON, one output dir
transcribe interview.mp3 --diarize                  # label speakers: "Speaker 1: ..."
```

---

## Install

```bash
pipx install aria-transcribe
# or
uv tool install aria-transcribe
```

From source (dev): `pipx install -e .` · `uv tool install -e .`

Optional extras:
- `[local]` — adds `faster-whisper` for offline transcription (Phase 3, not built yet)
- `[keyring]` — opt-in system keyring for API key storage
- `[diarize]` — adds `pyannote.audio` + `torch` for speaker labels (`--diarize`); ~2 GB, see [Speaker diarization](#speaker-diarization)
- `[all]` — everything

## Prerequisites

- **Python 3.11+**
- **ffmpeg + ffprobe** on `PATH`
  - Windows: `winget install Gyan.FFmpeg` · `scoop install ffmpeg` · `choco install ffmpeg`
  - macOS: `brew install ffmpeg`
  - Linux: `pacman -S ffmpeg` · `apt install ffmpeg` · `dnf install ffmpeg`
- **GROQ_API_KEY** for cloud transcription — grab one from [console.groq.com/keys](https://console.groq.com/keys)

## Setup

Set the API key one of three ways (precedence: CLI > env > config):

```bash
# 1. env var (recommended — survives across sessions)
export GROQ_API_KEY="gsk_..."                   # bash/zsh
$env:GROQ_API_KEY = "gsk_..."                   # PowerShell

# 2. one-off
transcribe clip.mp3 --api-key gsk_...

# 3. config file (see below) — handy on machines you fully control
```

## Config file

First run writes a commented template to:
- `$XDG_CONFIG_HOME/transcribe/config.toml` if set
- otherwise `~/.config/transcribe/config.toml` (works on Windows too)

Edit the values you want; everything is optional. CLI flags and env vars always win.

```toml
[defaults]
backend = "groq"
file_type = "txt"
timestamps = false
# output_dir = "~/transcripts"
# prompt_file = "~/path/to/custom-glossary.txt"

[groq]
# api_key = "gsk_..."          # prefer GROQ_API_KEY env var
model = "whisper-large-v3-turbo"
upload_limit_mb = 24

[diarize]
enabled = false                # default for --diarize
engine = "pyannote"
model = "pyannote/speaker-diarization-3.1"
device = "auto"                # auto | cpu | cuda
# hf_token = "hf_..."          # prefer HF_TOKEN env var; license must be accepted

[ui]
accent_color = "#ff2e88"
```

## Glossary trick (proper-noun accuracy)

Drop a `~/.config/transcribe/glossary.txt` with your proper nouns:

```
Nvikelo Nyathi, AriaCode Tech, Ableton, OpenClaw, Zellij,
vanity arias, Bulawayo, Ndebele, Shaka Zulu, Whisper, Groq
```

It auto-loads as the default `--prompt` so Whisper spells your names right
across every job. Trimmed to the trailing ~224 tokens (Whisper's prompt
budget) if longer. Override per-job with `--prompt "..."` or
`--initial-prompt-file path`.

## Speaker diarization

Label **who said what**. Whisper only transcribes — diarization is a separate
local pass ([`pyannote.audio`](https://github.com/pyannote/pyannote-audio)) joined
onto the transcript by timestamp, so every output format gains `Speaker N` labels.

```bash
uv tool install -e '.[diarize]'                 # one-time: adds pyannote + torch (~2 GB)
export HF_TOKEN="hf_..."                         # accept the model license first (link below)
transcribe interview.mp3 --diarize               # -> "Speaker 1: ...", "Speaker 2: ..."
transcribe call.m4a --diarize --speakers 2       # exact speaker-count hint
transcribe panel.mp4 --diarize --min-speakers 3 --max-speakers 5
transcribe chat.wav --diarize --speaker-names "Alice,Bob"   # rename Speaker 1,2
```

One-time setup:
1. Install the extra: `uv tool install -e '.[diarize]'`.
2. Create a Hugging Face token and **accept the license for BOTH gated models**
   the pipeline pulls:
   [`pyannote/speaker-diarization-3.1`](https://hf.co/pyannote/speaker-diarization-3.1)
   **and** [`pyannote/segmentation-3.0`](https://hf.co/pyannote/segmentation-3.0).
   (Accepting only the first gives a 403 on the second at model-load time.)
3. Provide the token via `HF_TOKEN`, `--hf-token`, or `[diarize] hf_token` in config.

Notes:
- **Word-level assignment.** A Whisper segment spanning a speaker change is split,
  so turn boundaries land in the right place instead of mislabeling the whole line.
- **Fail-hard by default.** If diarization errors at runtime, the run fails (exit 6)
  rather than silently writing an unlabeled transcript. Pass `--diarize-best-effort`
  to warn-and-write the transcript without labels instead.
- GPU strongly preferred for long files (`device = "cuda"`; ROCm works on the RX 6800).
- Missing extra → exit 3; missing/unauthorized token → exit 4 (caught as a preflight
  before any file is processed).

## All flags

| Flag | Short | Default | Notes |
|---|---|---|---|
| `PATH` | — | (required) | File or directory. Directory → batch. |
| `--file-type` | `-t` | `txt` | `txt` · `md` · `srt` · `json` |
| `--output` | `-o` | next to source | Existing dir → auto-named inside; else verbatim path. |
| `--backend` | `-b` | `groq` | `groq` · `local` *(local TBD)* |
| `--model` | `-m` | per-backend default | e.g. `whisper-large-v3-turbo` |
| `--language` | `-l` | auto-detect | ISO code; skips detection → slightly faster. |
| `--translate` | — | off | Whisper translate-to-English mode. |
| `--timestamps` / `--no-timestamps` | — | from config | mm:ss markers in txt/md only. srt/json always timestamped. |
| `--prompt` | `-p` | none | Initial prompt to bias vocabulary. |
| `--initial-prompt-file` | — | none | Load prompt text from file. |
| `--vad` / `--no-vad` | — | on | Silero VAD silence stripping *(local backend only)*. |
| `--diarize` / `--no-diarize` | — | from config | Label speakers (needs `[diarize]` extra + `HF_TOKEN`). |
| `--diarize-best-effort` | — | off | On diarizer runtime failure, write transcript unlabeled + warn instead of failing. |
| `--speakers` | — | auto | Exact speaker-count hint. Exclusive with `--min/--max-speakers`. |
| `--min-speakers` | — | auto | Lower bound on speaker count. |
| `--max-speakers` | — | auto | Upper bound on speaker count. |
| `--speaker-names` | — | none | Comma-separated names mapped to `Speaker 1,2,...`. |
| `--hf-token` | — | env / config | Hugging Face token for pyannote (highest precedence). |
| `--recursive` | `-r` | off | Recurse into subdirectories in batch mode. |
| `--api-key` | — | env / config | Groq key override (highest precedence). |
| `--quiet` | `-q` | off | Errors + final path only. Pipe-friendly. |
| `--verbose` | `-v` | off | Sizes, timings, language detection, segment count. |
| `--version` | — | — | Print version, exit. |

## Exit codes

| Code | Meaning |
|---|---|
| 0 | Success (batch: every file succeeded) |
| 1 | Generic / batch had failures |
| 2 | Bad usage (mutually exclusive flags, etc.) |
| 3 | Missing dependency (`ffmpeg`/`ffprobe`, or `pyannote.audio` when `--diarize`) |
| 4 | Missing or invalid Groq API key (or Hugging Face token when `--diarize`) |
| 5 | Input not found / unsupported file type |
| 6 | Backend, transcription, or diarization error after retries |

## Output formats

- **txt** — plain text, paragraph-wrapped at 88 cols. With `--timestamps`, `[mm:ss]` per segment.
- **md** — header (title, language, duration, model, backend) + body. With `--timestamps`, `` `[mm:ss]` `` markers per segment.
- **srt** — standard SubRip subtitles; opens in any video editor.
- **json** — lossless dump of every segment + per-word timestamps + probabilities (+ `speaker` when `--diarize`). Round-trips back via `TranscriptionResult.from_dict`.

With `--diarize`, every format gains speaker labels: txt `Speaker 1: ...` blocks, md `**Speaker 1:**` headings, srt `[Speaker 1]` cue prefixes, json per-segment/word `speaker`.

## Status

- ✅ **Phase 0** — skeleton (CLI parses, version, help)
- ✅ **Phase 1** — Groq happy path (single short file)
- ✅ **Phase 2** — all four formats + collision-safe writes + output-path resolution
- ✅ **Phase 5** — batch (folder + recursive + continue-on-error)
- ✅ **Phase 6** — glossary auto-load + prompt trim + README polish
- ✅ **Diarization** — optional `--diarize` speaker labels (pyannote, word-level, all formats)
- ⬜ **Phase 3** — local backend (`faster-whisper`)
- ⬜ **Phase 4** — long-file silence chunking + timestamp stitching (only needed for files >24 MB transcoded, ~7+ hrs)

See [`transcribe-cli-plan.md`](./transcribe-cli-plan.md) for the full spec.

## License

MIT. See [LICENSE](./LICENSE).
