Releases: ReidenXerx/whiz
Release list
v0.9.0 — auto-enable screenshots+speakers for video
For video inputs, --screenshots and --speakers (auto-detect) are now on by default — whiz transcribe recording.mov produces a labeled transcript plus per-segment frames for AI analysis / HTML output without extra flags.
What's new
- Auto-enabled for video inputs:
--screenshotsand--speakers(auto-detect) turn on automatically for video files. Audio inputs are unaffected. - Opt-out flags:
--no-screenshotsand--no-speakers(added to bothtranscribeandmerge) disable the auto-enabled defaults. - Graceful diarization fallback: when diarization is only auto-enabled (not explicitly requested) but sherpa-onnx or its models aren't installed, whiz prints a one-line hint and skips speaker labels (VAD stays on, screenshots still run) instead of crashing.
- JSON forced when screenshots on: needed to drive the per-segment frame path even without diarization.
- Privacy: replaced real names in README/source/tests with generic placeholders (Alice/Bob/Carol/Dave).
Upgrade
pipx install --force git+https://github.com/ReidenXerx/whiz.gitCo-Authored-By: Oz oz-agent@warp.dev
v0.8.1 — Test suite + resumability
New
--resumeflag onwhiz transcribe— skips whisper-cli when its JSON output already exists and goes straight to diarization + merge. An ergonomic alias forwhiz mergetriggered fromwhiz transcribe, so you don't have to switch subcommands to re-tune--speakersagainst an existing transcription.
Tests
- pytest suite (67 tests, <0.1s, no external deps) covering the pure-Python modules:
test_merge.py— overlap assignment, speaker ordering by talk time, relabel, representative quotes, SRT/TXT formatting, JSON parsing (-oj+ verbose_jsonwords), HTML escapingtest_models.py— alias derivation, discovery, resolve (exact/short/path/unknown), pick_best preference, VAD discovery (isolated from host models via monkeypatch)test_screenshots.py— frame naming, string-append path helpers for dotted stems, manifest round-trip + malformed-row handlingtest_ai.py— prompt resolution, transcript rendering, even-spread subsampling, base64, mocked HTTP (API-key header, vision image content, HTTPError/URLError/no-choices)test_diarize_cache.py— diar cache round-trip + param-mismatch + threshold epsilon; profiles cosine similarity + one-to-one matching + save/load/forget
Install
pipx install --force --editable '.[test]'
pytest tests/Full changelog: v0.8.0...v0.8.1
v0.8.0 — Speaker voice profiles
✨ Speaker voice profiles (cross-recording recognition)
When you name a speaker (with --name-speakers or --speakers-names), whiz now saves a voice profile: a 512-dim embedding vector for that speaker's audio, computed with the same sherpa-onnx embedding extractor used for diarization. On later recordings, each detected cluster's embedding is compared (cosine similarity) to the stored profiles and a name is auto-assigned when the best match exceeds speaker_match_threshold (default 0.8).
So the first time you transcribe a meeting with --speakers-names Enric,Vadim,Thomas,Dziyana, those voice profiles are stored. The next recording with the same people is labeled automatically, no flags needed.
New
whiz/profiles.py—compute_speaker_embeddings()(audio chunked into 30s streaming windows, utterances <0.3s skipped),cosine_similarity(),match_speakers()with one-to-one assignment (a name never goes to two clusters),auto_assign_names(), JSON profile store at~/.config/whiz/speakers/<Name>.jsonwhiz speakerssubcommands:list,forget <name>,match <file>(dry-run showing every cluster-vs-profile cosine score)- Config:
speaker_match_threshold(default 0.8),save_voice_profiles(default true) --no-voice-profilesflag ontranscribe+mergeto skip auto-matching and profile saving
Usage
# First recording: name speakers — profiles saved automatically
whiz transcribe --speakers 4 --speakers-names Enric,Vadim,Thomas,Dziyana meeting1.mov
# Later recording: speakers auto-matched, no flags needed
whiz transcribe --speakers 4 meeting2.mov
# Inspect / tune / remove
whiz speakers list
whiz speakers match meeting2.mov --speakers 4
whiz speakers forget DziyanaNaming precedence: voice-profile auto-match → --speakers-names → --name-speakers (interactive, with auto-matched names shown as defaults).
Full changelog: v0.7.0...v0.8.0
v0.7.0 — HTML transcript + word timestamps
✨ Self-contained HTML transcript
Add html to --outputs to write a portable <stem>.speakers.html alongside the input. Each segment is a color-coded cue with a timestamp link, the speaker label, and (when --screenshots was set) the on-screen frame inlined as a base64 data: URI — so the single file is fully portable (email, wiki, offline) with no external image dependencies.
New
--outputs htmlonwhiz transcribeandwhiz mergemerge.format_speakers_html()— inline CSS, deterministic per-speaker colors (hash-based palette), HTML-escaped text, base64 frame inlining from<stem>.frames/WhisperSeg.wordsfield +parse_whisper_json()reads verbose_jsonwordsarrays (groundwork for karaoke-style word highlighting)
Usage
# Transcribe + diarize + capture frames + write HTML transcript
whiz transcribe --speakers 4 --screenshots --outputs srt,html recording.mov
# Add HTML to an existing run via merge (reuses diarization cache)
whiz merge --speakers 4 --screenshots --outputs html recording.movThe HTML file can be large (one base64 JPEG per segment), but opens in any browser with no server and no missing images.
Full changelog: v0.6.0...v0.7.0
v0.6.0 — AI analysis via Ollama
✨ AI analysis (summary, action items, vision)
whiz analyze sends a prior transcript (and optionally on-screen frames) to a chat model via an OpenAI-compatible API (Ollama by default). Produces a markdown .analysis.md alongside the input and prints the response to stdout.
New
whiz/ai.py— urllib OpenAI-compatible client (chat_text/chat_vision), base64 frames at send time,ai_max_framescap (spread evenly), clear error hints (text-only model rejecting images → vision hint; server unreachable → "is ollama running?")whiz analyzesubcommand:--summary,--actions,--prompt(freeform with{transcript}placeholder),--vision(send on-screen frames)- Config:
ai_base_url(defaulthttp://localhost:11434/v1),ai_model,ai_api_key,ai_max_frames(default 50)
Usage
# Setup
whiz config set ai_model=gpt-4o-mini # or any Ollama model
# Summary + action items (default)
whiz analyze recording.mov
# Vision analysis (requires a prior --screenshots run + vision model)
whiz analyze recording.mov --vision --summaryWorks with Ollama, LM Studio, vLLM, and any OpenAI-compatible server. No new dependencies — uses urllib.request.
Full changelog: v0.5.0...v0.6.0
v0.5.0 — Diarization cache, naming, screenshots
✨ Quality-of-life + video screenshots
Three cheap wins that ship immediate value, plus the centerpiece video-screenshot pipeline that later phases build on.
New
Phase 1 — cheap wins
- Diarization cache (
<wav>.diar.json) — the expensive embedding pass runs once; laterwhiz mergeruns with the same--speakers/--cluster-thresholdreuse the cache and skip the ~3 min embedding pass. Re-tuning names/thresholds is now instant. --speakers-namesnon-interactive flag — assign names by total talk time (most talkative first) without an interactive prompt.- Whisper-cli progress passthrough — run via
subprocess.Popen, stream output line-by-line to stderr with an elapsed-time prefix.--print-progresson by default when stderr is a TTY.
Phase 2 — video screenshots
whiz/screenshots.py— extract one JPEG per transcribed segment at its start timestamp via ffmpeg seek-before-input (seg0001.jpg↔ cue 1).--screenshotsflag ontranscribe+merge;--screenshot-width(default 1280).<stem>.frames.jsonmanifest — paths only (no bytes), small and re-runnable. The join key for AI analysis (whiz analyze --vision) and the self-contained HTML transcript (which inlines frames as base64).
Full changelog: v0.4.0...v0.5.0
v0.4.0 — Interactive speaker naming + whiz merge
New
--name-speakers— after transcription + diarization, whiz shows one representative quote per detected speaker (the longest utterance — most identifying) and interactively prompts for a real name. Blank input keeps the defaultSpeaker Alabel.whiz mergesubcommand — re-run only diarization + merge against an existing whisper JSON, so you can tune speaker count / threshold / names without redoing the expensive transcription.- Default
cluster_thresholdraised to0.9per sherpa-onnx guidance.
Full changelog: v0.3.1...v0.4.0
v0.3.1 — whisper JSON path fix + cluster_threshold 0.9
Fixes
- Resolve whisper-cli JSON output at
<wav>.json(whisper-cli names outputs after the full input path, sofoo.wav.json— the.wavis part of the stem). Preserves dots in stems like...16.03.40. - Raise default
cluster_thresholdto0.9(fewer, more accurate speakers).
Full changelog: v0.3.0...v0.3.1
v0.3.0 — Multi-speaker diarization
✨ Speaker diarization via sherpa-onnx
whiz transcribe --speakers runs true diarization (pyannote segmentation + 3D-Speaker embedding clustering) and emits labeled Speaker A: / Speaker B: output.
--speakers [N]— optional integer = known speaker count (more accurate); omit = auto-detect--cluster-threshold— clustering threshold when auto-detecting (larger = fewer speakers)- Downloads diarization models (~90 MB) via
whiz models download-diarization - Produces
*.speakers.srt+*.speakers.txtalongside the input
Full changelog: v0.2.1...v0.3.0
v0.2.1 — VAD download 404 fix
Fixes
- The Silero VAD model moved from
ggerganov/whisper.cppto a separate repoggml-org/whisper-vadwith versioned filenames. whiz now downloads from the new repo, fixing the 404. whiz models download-vad [version]acceptsv5.1.2,v6.2.0, or full filenames.
Full changelog: v0.2.0...v0.2.1