Turn any note into a narrated video. scribecast façade + vidkit engine (HyperFrames / Manim / Remotion), edge-tts narration, ffmpeg mux. CLI + library + MCP server.
Project description
scribecast
Turn any note — from gbrain, PraxVault, ksum, a file, stdin, or several merged — into a narrated video. scribecast is the façade; vidkit is the engine. Both ship in ONE pip-installable, delete-proof wheel (this repo).
note ref ──► resolver ──► script (cards) ──► render (engine) ──► narrate (edge-tts) ──► mux ──► mp4
gbrain/vault/ hyperframes/ (vidkit, video
ksum/file/stdin/merge manim/remotion duration authoritative)
scribecast imports vidkit (from vidkit_core import ...) — it never shells out to a vendored
copy. Note sources are read by calling the existing local CLIs (gbrain/ksum/praxvault-ask) as
subprocess for note resolution only.
Install (delete-proof wheel — NOT editable)
pip install . # ships vidkit_core + vqkit + scribecast in one wheel
pip install .[narrate] # + edge-tts narration
pip install .[manim] # + manim engine (heavy, opt-in)
pip install .[all] # narrate + manim
pip install .[test] # pytest + pytest-xdist + coverage (for the test suite)
After install you can delete this source dir — the scribecast and vidkit CLIs keep working
(verified by the delete-proof scenario; see scenarios/run_scenarios.py::s10_delete_proof).
The Remotion engine additionally needs Node.js + the remotion_kit (with npm install run).
Use — three interfaces, one core
CLI (humans, Raycast, scripts):
scribecast render file:notes.md -o out.mp4 # render a file note
scribecast render gbrain:two-brain-architecture # render a gbrain page
scribecast render file:a.md --merge file:b.md file:c.md # merge notes into one video
scribecast render file:n.md --engine manim --voice en-GB-RyanNeural
scribecast render file:n.md --engine remotion # 1080p React/Remotion engine
scribecast recommend file:n.md # advisory engine pick (user always decides)
scribecast sources # list note sources + availability
scribecast config # effective config + where each value came from
scribecast -v render file:n.md # -v = INFO logs, -vv = DEBUG (to stderr)
Library (Python):
from scribecast import render_note, resolve, recommend_engine
res = render_note("file:notes.md", out="out.mp4") # -> RenderResult(output, engine, duration, ...)
text = resolve(["gbrain:slug-a", "file:b.md"]) # merge
rec = recommend_engine(text) # advisory
MCP (any agent — Claude/Hermes/Cursor/workers):
python -m scribecast.mcp # stdio JSON-RPC: render_note_video, recommend_engine, list_sources, list_voices
Zero mcp-sdk dependency (minimal stdio JSON-RPC) so it installs with the base wheel. Logs go to stderr (stdout is the JSON-RPC transport).
Obsidian plugin (obsidian-plugin/): render the current note from inside Obsidian. It is a
thin consumer of the installed scribecast CLI (no vendoring). Commands: "Render active note to
video", "Recommend engine for active note"; settings for binary path / engine / voice / output dir.
Build: cd obsidian-plugin && npm install && npm run build, then copy manifest.json + main.js
into <vault>/.obsidian/plugins/scribecast/.
Logging
The package uses the stdlib logging framework with library-correct discipline: importing
scribecast is silent (a NullHandler is attached), so it never pollutes a host application's
output. Logging is enabled by the entry points:
- CLI:
-v(INFO) /-vv(DEBUG) — records go to stderr. - MCP: always configured to stderr on server start.
- env:
SCRIBECAST_LOG_LEVEL=DEBUG|INFO|WARNING|ERROR. Every important path logs: note resolution (+ subprocess run/timeout/failure), engine selection, each render stage (script → render → narrate → mux → probe → done), and MCP tool dispatch + errors.
Configurable (flag > env > config-file > default)
- config file:
$SCRIBECAST_CONFIGor~/.scribecast/config.toml - env:
SCRIBECAST_ENGINE,SCRIBECAST_VOICE,SCRIBECAST_SOURCE,SCRIBECAST_OUT_DIR, ... - flags:
--engine --voice --source -o ... scribecast configshows the effective value AND which layer set it.
Engine selection — recommend, never decide
The selector RECOMMENDS one of hyperframes | manim | remotion from note content (math/LaTeX →
manim, web/React → remotion, else hyperframes). The user's explicit --engine always wins —
the recommendation is advisory and is always shown for transparency. (Heuristic today; an LLM
recommender can be added without changing the user-wins contract.)
Default engine: hyperframes (cards)
The default renderer builds titled text cards with Pillow → an ffmpeg image-sequence mp4 → narration → mux. Needs only Pillow + ffmpeg (no browser, no Manim). All three engines work:
- hyperframes (default, dep-light): Pillow text-cards → ffmpeg, 1280×720.
- manim (opt-in
[manim]): cinematic viavqkit.CinematicScene(MovingCameraScene + MathTex), 1280×720. Math/LaTeX spans render as real equations; prose renders as Text. - remotion (opt-in): React/Remotion
ScribecastCardscomposition (spring entrances, gradient), 1920×1080. Needs Node +remotion_kit. License-gated for orgs > 3 (Remotion License v1).
A forced --engine that lacks its dependency raises a clear error — never a silent fallback.
Sources
| source | how | tool |
|---|---|---|
file |
read a local path | — |
stdin |
piped text | — |
gbrain |
gbrain get <slug> |
gbrain |
vault |
praxvault-ask <query> |
praxvault-ask |
ksum |
ksum <url|file> --no-save |
ksum |
openmemory |
openmemory get <id> |
openmemory (optional) |
merge |
multiple refs → one video | — |
A missing source tool raises a clear ResolverError (never silent-empty).
Tests
pytest -q # 126 fast unit tests in ~1s (real-render tests deselected)
pytest -q --run-slow # full 163 tests incl. real manim/remotion/ffmpeg renders
pytest -q --run-slow -n auto # full set in parallel (pytest-xdist)
coverage run --source=scribecast -m pytest --run-slow && coverage report # 100% (692/692)
python scenarios/run_scenarios.py # 10 realistic end-to-end scenarios (pass/fail + evidence)
python scenarios/benchmark.py # per-engine timing baseline -> scenarios/baseline.json
Real-render tests are auto-marked slow (see tests/conftest.py) and deselected by default for a
fast dev loop; --run-slow runs them and the coverage gate enforces 100%. The mux-truncation
regression test (tests/test_mux_no_truncation.py) proves a short narration never truncates a
longer video — the bug this project fixed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scribecast-0.1.0.tar.gz.
File metadata
- Download URL: scribecast-0.1.0.tar.gz
- Upload date:
- Size: 79.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffd9e21cf1c0d7d704d5bf088f776061b5e01f964ad01e569d9eb93c810374a1
|
|
| MD5 |
0e8cfd38d47b2cadad78e99132e28a79
|
|
| BLAKE2b-256 |
2e468fd2012751e2847535e73aa2fade9627665d5c79204db1fc9d5ce29f6789
|
File details
Details for the file scribecast-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scribecast-0.1.0-py3-none-any.whl
- Upload date:
- Size: 68.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bece5f8eafed9f0b3f429bb0e0c726d84bbdcf7ee9d791cbcbafa075183988ac
|
|
| MD5 |
d6f65829cf26e21b32020549092b1e27
|
|
| BLAKE2b-256 |
eaf7e487b1f952b639860c539d91be7d7c58fb970c9c2f9c395824c48ab43ac6
|