local whisper transcription for users without an elevenlabs key - #151
local whisper transcription for users without an elevenlabs key#151DonIsmaelito wants to merge 13 commits into
Conversation
…ames in the skill contract
…it engine selection
There was a problem hiding this comment.
2 issues found across 19 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="helpers/timeline_view.py">
<violation number="1" location="helpers/timeline_view.py:59">
P3: This change forces the deprecated full-range `yuvj420p` pixel format, while every other ffmpeg call in the repo (`helpers/render.py` lines 124/310/692, `helpers/grade.py` line 292) uses `yuv420p`. `yuvj*` formats are deprecated in FFmpeg 7.0+ and produce a deprecation warning on every frame extraction (hidden here because stderr is DEVNULL), and may be removed in a future release. If the goal is full-range JPEG output to avoid washed-out frames, keep limited-range `yuv420p` and set the range explicitly instead, e.g. `scale=320:-2,format=yuv420p` plus `-color_range`/`out_range=full`, which is forward-compatible and consistent with the rest of the repo.</violation>
</file>
<file name="tests/test_comment_style.py">
<violation number="1" location="tests/test_comment_style.py:15">
P3: The punctuation guard doesn't enforce the convention the module docstring states. PUNCTUATION only matches [.,:;()\"'`], so a comment like "# why? wow!" passes audit() even though the rule says comments must not carry punctuation. Add the missing sentence punctuation (at least ? and !) to the character class.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| "-frames:v", "1", | ||
| "-q:v", "4", | ||
| "-vf", "scale=320:-2", | ||
| "-vf", "scale=320:-2,format=yuvj420p", |
There was a problem hiding this comment.
P3: This change forces the deprecated full-range yuvj420p pixel format, while every other ffmpeg call in the repo (helpers/render.py lines 124/310/692, helpers/grade.py line 292) uses yuv420p. yuvj* formats are deprecated in FFmpeg 7.0+ and produce a deprecation warning on every frame extraction (hidden here because stderr is DEVNULL), and may be removed in a future release. If the goal is full-range JPEG output to avoid washed-out frames, keep limited-range yuv420p and set the range explicitly instead, e.g. scale=320:-2,format=yuv420p plus -color_range/out_range=full, which is forward-compatible and consistent with the rest of the repo.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/timeline_view.py, line 59:
<comment>This change forces the deprecated full-range `yuvj420p` pixel format, while every other ffmpeg call in the repo (`helpers/render.py` lines 124/310/692, `helpers/grade.py` line 292) uses `yuv420p`. `yuvj*` formats are deprecated in FFmpeg 7.0+ and produce a deprecation warning on every frame extraction (hidden here because stderr is DEVNULL), and may be removed in a future release. If the goal is full-range JPEG output to avoid washed-out frames, keep limited-range `yuv420p` and set the range explicitly instead, e.g. `scale=320:-2,format=yuv420p` plus `-color_range`/`out_range=full`, which is forward-compatible and consistent with the rest of the repo.</comment>
<file context>
@@ -54,7 +56,7 @@ def extract_frames(video: Path, start: float, end: float, n: int, dest_dir: Path
"-frames:v", "1",
"-q:v", "4",
- "-vf", "scale=320:-2",
+ "-vf", "scale=320:-2,format=yuvj420p",
str(out),
]
</file context>
| "-vf", "scale=320:-2,format=yuvj420p", | |
| "-vf", "scale=320:-2,format=yuv420p", |
|
|
||
| ROOT = Path(__file__).resolve().parents[1] | ||
| SKIP_DIRS = {".venv", "venv", "node_modules", "__pycache__", ".git", "media", "edit"} | ||
| PUNCTUATION = re.compile(r"[.,:;()\"'`]") |
There was a problem hiding this comment.
P3: The punctuation guard doesn't enforce the convention the module docstring states. PUNCTUATION only matches [.,:;()"'`], so a comment like "# why? wow!" passes audit() even though the rule says comments must not carry punctuation. Add the missing sentence punctuation (at least ? and !) to the character class.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_comment_style.py, line 15:
<comment>The punctuation guard doesn't enforce the convention the module docstring states. PUNCTUATION only matches [.,:;()\"'`], so a comment like "# why? wow!" passes audit() even though the rule says comments must not carry punctuation. Add the missing sentence punctuation (at least ? and !) to the character class.</comment>
<file context>
@@ -0,0 +1,83 @@
+
+ROOT = Path(__file__).resolve().parents[1]
+SKIP_DIRS = {".venv", "venv", "node_modules", "__pycache__", ".git", "media", "edit"}
+PUNCTUATION = re.compile(r"[.,:;()\"'`]")
+DEFINITIONS = (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
+
</file context>
| PUNCTUATION = re.compile(r"[.,:;()\"'`]") | |
| PUNCTUATION = re.compile(r"[.,:;()\"'`!?]") |
…n repeats stream onset energy and add the cuda extra
|
Addressed the review in 5da3820:
The |
There was a problem hiding this comment.
1 issue found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="helpers/transcribe_batch.py">
<violation number="1" location="helpers/transcribe_batch.py:118">
P3: The batch entry point preflights the local library once, but transcribe_one() preflights it again on every file it processes. Since the local engine is always a single worker, every local batch run re-runs the hardware probe and library import a second time. Drop the per-file preflight in transcribe_one (or guard it) so the "settle once" intent in batch mode actually holds.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| # settle the local library once here so a missing install exits with its message instead of failing every worker | ||
| local_options = local_options_from(args) | ||
| if engine == "local": | ||
| preflight_local(local_options) |
There was a problem hiding this comment.
P3: The batch entry point preflights the local library once, but transcribe_one() preflights it again on every file it processes. Since the local engine is always a single worker, every local batch run re-runs the hardware probe and library import a second time. Drop the per-file preflight in transcribe_one (or guard it) so the "settle once" intent in batch mode actually holds.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/transcribe_batch.py, line 118:
<comment>The batch entry point preflights the local library once, but transcribe_one() preflights it again on every file it processes. Since the local engine is always a single worker, every local batch run re-runs the hardware probe and library import a second time. Drop the per-file preflight in transcribe_one (or guard it) so the "settle once" intent in batch mode actually holds.</comment>
<file context>
@@ -111,6 +112,11 @@ def main() -> None:
+ # settle the local library once here so a missing install exits with its message instead of failing every worker
+ local_options = local_options_from(args)
+ if engine == "local":
+ preflight_local(local_options)
+
workers = worker_count(engine, args.workers)
</file context>
There was a problem hiding this comment.
local_stt.preflight() is memoized per process (_PREFLIGHTED, added in 5da3820/961f3d4): the batch entry point pays for the probe and import once, and the per-file call inside transcribe_one is a dict lookup on an already-imported module. It stays there so direct callers of transcribe_one keep the fail-before-extraction guarantee. test_preflight_settles_once covers the single probe.
… process and make the doc blocks match both install paths
|
Second pass addressed in the follow-up commit:
|
Lets people edit without an ElevenLabs account. Transcription is the first step of every session and used to hard-exit when
ELEVENLABS_API_KEYwas missing, even though the rest of the pipeline is ffmpeg and PIL.Builds on the agent guidance PR; its commits show here until that PR merges.
What changes
helpers/local_stt.py: local Whisper large-v3-turbo throughmlx-whisperon Apple Silicon orfaster-whisper(CTranslate2, int8 on CPU, float16 on CUDA) elsewhere. A hardware probe (local_stt.py probe) picks the library, reports install state, free disk, and model size, and prints the one install command, so a machine downloads only the model it will use. Weights come fromhuggingface_hub.snapshot_downloadpinned to a commit and land in the standard Hugging Face cache. A missing library exits with the exact install command; nothing here runs pip.helpers/transcribe.pyandtranscribe_batch.py: explicit engine selection, never a silent fallback.--enginewins, thenVIDEO_USE_TRANSCRIBER(elevenlabsorlocal) from.envor the environment, then ElevenLabs when a key resolves. Every run printsengine: <name> (from <source>), and says when a key is present but unused. Transcripts gain a top-levelenginekey; files from before the key exist are treated as ElevenLabs and reused untouched.--forcere-transcribes and swaps the file in only after the new run succeeds, so a paid Scribe transcript is never lost to a failed local run. The batch helper runs one worker for the local engine (one model per process).wordswithtype,text,start,end,speaker_id), sopack_transcripts.py,render.py, and the EDL validators work unchanged.VIDEO_USE_TRANSCRIBER. Which library runs is an implementation detail with developer-only--libraryand--modeloverrides.pyproject.tomladds two extras,stt-mlxandstt-cpu; core dependencies are unchanged.install.mdstep 5 asks once for a key or the local engine and persists the choice;SKILL.mdgets a "Local transcription" subsection stating what the local engine does not provide (speaker labels, audio events,spacingentries; fillers best effort) and rewrites the two Whisper anti-pattern lines;README.mdmentions the option.Measured on an M2 MacBook Air, 8 GB, with the pinned fp16 turbo model
Two minutes of a keynote recording against the cached Scribe transcript for the same audio, comparing only words inside runs of three or more consecutive matches so a repeated phrase cannot pair with the wrong occurrence:
Remaining outliers are stutter runs ("the, the, the transformation") that Whisper collapses into one long word; Scribe keeps each repetition. That is the verbatim gap the docs now warn about. The 2 min sample transcribes in about 2.5 min on this machine after a one-time 1.6 GB download.
Tests
python -m pytest -q(57 tests). New coverage: library choice per machine, missing-library message, pinned download, word normalization, each artifact guard, onset trimming, verbatim prompt gating, dotenv precedence, the printed engine line, invalid values failing before any subprocess, the ElevenLabs path calling Scribe exactly once, an old transcript withoutenginebeing reused, and--forcekeeping the old file when the new run raises. No test loads a model or touches the network.🤖 Generated with Claude Code
https://claude.ai/code/session_01WF63xJ295MfvG1f2BTKQnx
Summary by cubic
Users without an ElevenLabs key can now transcribe and edit videos with a local Whisper engine. Transcription previously hard-exited when
ELEVENLABS_API_KEYwas missing, even though the rest of the pipeline is ffmpeg and PIL.New Features
mlx-whisperon Apple Silicon,faster-whisperelsewhere) with a hardware probe that prints the one CUDA-aware install command, downloads only the needed model, and preflights the environment before a run.--enginewins, thenVIDEO_USE_TRANSCRIBER, then the API key, and each run prints the engine and its source; quoted dotenv values are honored.enginekey to transcripts; files without it are reused as ElevenLabs, and--forcereplaces a transcript only after success.Migration
VIDEO_USE_TRANSCRIBER=localin.envand install thestt-mlx,stt-cpu, orstt-cudaextra; install now asks once for a key or the local engine.spacingentries, and fillers are best effort.Written for commit 961f3d4. Summary will update on new commits.