Turn raw screen recordings + narration text into polished narrated screencasts
Project description
screencast-narrator
Turn raw screen recordings + narration text into polished narrated screencasts.
screencast-narrator is a Python library and CLI that takes a Playwright (or any) screen recording together with timestamped narration text and produces a final video with:
- Text-to-speech narration synced to on-screen actions
- QR-code-based frame-accurate synchronization
- Automatic freeze-frame insertion when narration overflows action duration
- Dead-air gap detection and cutting
- Interactive HTML timeline visualizations
Installation
pip install screencast-narrator
For TTS support (Kokoro):
pip install screencast-narrator[tts]
System dependencies:
# macOS
brew install ffmpeg zbar
# Ubuntu/Debian
apt-get install ffmpeg libzbar0
# Windows
winget install GyanDev.FFmpeg
# The pyzbar Python package ships its own libzbar DLL but needs the Visual C++ 2013 runtime:
winget install Microsoft.VCRedist.2013.x64
# Or download manually from https://aka.ms/highdpimfc2013x64enu
Quick Start
As a library with Playwright
from pathlib import Path
from playwright.sync_api import sync_playwright
from screencast_narrator.timeline import ScreencastTimeline
from screencast_narrator.sync_frames import inject_sync_frame
from screencast_narrator.merge import process
output_dir = Path("my-screencast")
output_dir.mkdir(exist_ok=True)
timeline = ScreencastTimeline(output_dir, video_enabled=True)
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(
record_video_dir=str(output_dir / "videos"),
record_video_size={"width": 1920, "height": 1080},
)
page = context.new_page()
timeline.video_recording_started()
# Narration bracket with sync frames
timeline.begin_narration_bracket()
inject_sync_frame(page, timeline.active_narration_id(), "START")
page.goto("https://example.com")
timeline.add_action("Navigate to example.com")
inject_sync_frame(page, timeline.active_narration_id(), "END")
timeline.add_narration("We open the example website.", start_ms=0, end_ms=2000)
timeline.end_narration_bracket()
timeline.video_recording_ended()
context.close()
browser.close()
# Produce the final narrated video
process(output_dir)
As a CLI
screencast-narrator /path/to/recording-output/
The directory must contain:
- A
timeline.jsonfile (produced byScreencastTimeline) - A video file (
.webm) in avideos/subdirectory
Architecture
The pipeline has these stages:
-
Timeline recording (
timeline.py) — During your test/recording, track narrations, actions, highlights, and sync frames with timestamps. -
Sync frame injection (
sync_frames.py) — Inject green QR-code overlay frames into the browser at narration bracket boundaries for frame-accurate sync. -
TTS generation (
tts.py) — Convert narration text to speech audio files. Pluggable backend; ships with Kokoro TTS. -
Sync detection (
sync_detect.py) — Extract frames from the recorded video, detect green sync frames, decode QR codes to map video frames to narration events. -
Freeze frame calculation (
freeze_frames.py) — When narration audio is longer than the on-screen action, calculate where to insert freeze frames (paused video) so audio and video stay in sync. Avoids placing freezes during visual highlights. -
Video merge (
merge.py) — Orchestrate FFmpeg to build the final video: insert freeze frames, overlay audio, cut dead air gaps, concatenate segments. -
Timeline visualization (
timeline_html.py) — Generate interactive HTML timelines showing original events, adjusted (post-freeze) positions, and a combined three-column view.
Modules
| Module | Purpose |
|---|---|
timeline.py |
Event recording during screen capture |
sync_frames.py |
QR code overlay injection for frame-accurate sync |
sync_detect.py |
Green frame detection and QR decoding from video |
freeze_frames.py |
Freeze frame calculation algorithm |
tts.py |
Pluggable TTS backend (Kokoro default) |
ffmpeg.py |
FFmpeg subprocess helpers |
merge.py |
Main merge pipeline and CLI entry point |
timeline_html.py |
HTML timeline visualization generator |
Custom TTS Backend
Implement the TTSBackend protocol:
from screencast_narrator.tts import TTSBackend
from pathlib import Path
class MyTTS(TTSBackend):
def generate(self, text: str, output_path: Path) -> None:
# Generate audio file at output_path
...
# Use it
from screencast_narrator.merge import process
process(target_dir, tts_backend=MyTTS())
Development
git clone https://github.com/mmarinschek/screencast-narrator.git
cd screencast-narrator
pip install -e ".[dev,tts]"
# Run tests
pytest tests/ -v
# On macOS, if pyzbar can't find libzbar:
DYLD_LIBRARY_PATH=/opt/homebrew/lib pytest tests/ -v
License
Apache License 2.0 — see LICENSE.
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 screencast_narrator-0.1.2.tar.gz.
File metadata
- Download URL: screencast_narrator-0.1.2.tar.gz
- Upload date:
- Size: 36.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5756c6bfd547e7e1c0268dc2dacd753437f45154692c60d1709d853cfd624adb
|
|
| MD5 |
01a331c4400614c20b4110a3b0c1f985
|
|
| BLAKE2b-256 |
30bde143149bd3c5c6528354331f2e20f2fea6d54f4270f1bd25052f7520ebe2
|
Provenance
The following attestation bundles were made for screencast_narrator-0.1.2.tar.gz:
Publisher:
publish.yml on mmarinschek/screencast-narrator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
screencast_narrator-0.1.2.tar.gz -
Subject digest:
5756c6bfd547e7e1c0268dc2dacd753437f45154692c60d1709d853cfd624adb - Sigstore transparency entry: 1051656262
- Sigstore integration time:
-
Permalink:
mmarinschek/screencast-narrator@f025a63c6a89482b9f7b6ba7444d0172d98681a3 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/mmarinschek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f025a63c6a89482b9f7b6ba7444d0172d98681a3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file screencast_narrator-0.1.2-py3-none-any.whl.
File metadata
- Download URL: screencast_narrator-0.1.2-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea6e9f676e82c69a31908885d7db7c611e03f67dddb5c0cae5d7f7105f2fca10
|
|
| MD5 |
aadf8733df6fd58b918aea5caea2d127
|
|
| BLAKE2b-256 |
038f80e4a74df6439218166ee281255ae46cb871c274fbda12057bad30d6da80
|
Provenance
The following attestation bundles were made for screencast_narrator-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on mmarinschek/screencast-narrator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
screencast_narrator-0.1.2-py3-none-any.whl -
Subject digest:
ea6e9f676e82c69a31908885d7db7c611e03f67dddb5c0cae5d7f7105f2fca10 - Sigstore transparency entry: 1051656272
- Sigstore integration time:
-
Permalink:
mmarinschek/screencast-narrator@f025a63c6a89482b9f7b6ba7444d0172d98681a3 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/mmarinschek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f025a63c6a89482b9f7b6ba7444d0172d98681a3 -
Trigger Event:
push
-
Statement type: