Metadata-Version: 2.4
Name: snippet-cast
Version: 0.1.54
Summary: Turn annotated Python snippets into narrated screencast videos.
Author-email: Kasper Munch <kaspermunch@birc.au.dk>
License: MIT
Project-URL: Homepage, https://github.com/munch-group/snippet-cast
Project-URL: Repository, https://github.com/munch-group/snippet-cast
Project-URL: Documentation, https://munch-group.org/snippet-cast
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: <3.15,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pillow
Requires-Dist: pygments
Provides-Extra: piper
Requires-Dist: piper-tts<2,>=1.5.0; extra == "piper"
Provides-Extra: jupyter
Requires-Dist: ipython; extra == "jupyter"
Dynamic: license-file

# snippet-cast

Turn an annotated Python snippet into a narrated screencast video.

Narration is written as trailing `#:` comments on the snippet's own lines, so
the input stays valid, runnable Python:

```python
def fib(n):             #: We define fib, taking one argument, n.
    a, b = 0, 1          #: Start from the first two Fibonacci numbers.
    for _ in range(n):   #: Loop n times.
        a, b = b, a + b  #: Advance the pair; b becomes the running sum.
    return a             #: Return a — the nth Fibonacci number.
```

Each `#:` line becomes one "beat": the code is revealed up to that line, the
line is highlighted, and its narration is spoken. `snippet-cast` renders
syntax-highlighted code frames with a progressive reveal, a Python-Tutor-style
live variable panel, optional burned-in captions and a typing-in animation,
synthesises speech per line, and stitches everything into an MP4 with ffmpeg.

## Installation

Requires **Python 3.10+** and **ffmpeg** (with `ffprobe`) on `PATH`.

```bash
pip install snippet-cast
# or
pixi add snippet-cast
# or
conda install -c munch-group snippet-cast
```

## Usage

```bash
snippet-cast snippet.py -o out.mp4 --tts silent --subtitles   # fast, voiceless proof
snippet-cast snippet.py -o out.mp4 --typing --subtitles       # type each new line in
snippet-cast loop.py    -o out.mp4 --every --subtitles        # animate each loop iteration
```

Or from Python:

```python
from snippet_cast import build

build("snippet.py", "out.mp4", tts="silent", subtitles=True)
```

Or in a Jupyter notebook — write the snippet directly in a cell instead of a
separate `.py` file:

```
pip install snippet-cast[jupyter]
```

```
%load_ext snippet_cast.magic
```

```
%%snippet-cast -o out.mp4 --tts silent --subtitles
def fib(n):             #: We define fib, taking one argument, n.
    a, b = 0, 1          #: Start from the first two Fibonacci numbers.
    for _ in range(n):   #: Loop n times.
        a, b = b, a + b  #: Advance the pair; b becomes the running sum.
    return a             #: Return a — the nth Fibonacci number.
result = fib(7)          #: Call fib with seven; result becomes {result}.
```

The cell magic takes the same flags as the CLI and displays the rendered MP4
inline.

See [SETUP.md](SETUP.md) for all TTS backends — the zero-setup `say` (macOS)
and `manual` (your own recordings, including `--record` for recording live
via the microphone) backends, plus configuring Piper (local) and ElevenLabs
(cloud) — and `snippet-cast --help` for all options.

## Configuration

`-o`/`--output` sets an explicit path; without it, `-n`/`--name` (default
`out`) and `-d`/`--output-dir` (default `.`, created if missing) build one as
`output-dir/name.mp4`:

```bash
snippet-cast snippet.py --tts silent -n intro -d ./videos   # -> ./videos/intro.mp4
```

Every option (except `-o`/`--output`) also has a `SNIPPET_CAST_<NAME>`
environment variable default — an explicit flag always wins over its env
var:

```python
import os
os.environ["SNIPPET_CAST_TTS"] = "say"
os.environ["SNIPPET_CAST_SUBTITLES"] = "1"
os.environ["SNIPPET_CAST_PAUSE"] = "0.6"
os.environ["SNIPPET_CAST_OUTPUT_DIR"] = "./videos"
```

set in one notebook cell, applies to every `%%snippet-cast` cell after it
(picked up fresh each time, so setting it in a later cell still works).

Runs are **quiet by default** — notes, warnings and errors still go to
stderr, but the per-beat progress needs `-v`/`--verbose`. The other shipped
defaults are `--tts say` (macOS; pass `--tts silent` elsewhere) and
`--order exec`, which highlights each line on the way in and again on the way
out, in the order Python visits them; `--order source` gives the plain
top-to-bottom playback.
Toggle flags (`--every`, `--subtitles`, `--typing`, `--record`,
`--export-script`) accept `--no-X` to override an env-var-forced default back
off for one run.

## Development

This repository is built from the munch-group library template.

### Initial set up

```bash
pixi run init
```

### Get updates to upstream fork

Add upstream if not already added

```bash
git remote add upstream https://github.com/munch-group/snippet-cast.git
```

Fetch upstream changes

```bash
git fetch upstream
```

Either rebase your changes on top of upstream (cleaner history)

```bash
git rebase upstream/main
```

Or, merge upstream into your fork (preserves history)

```bash
git merge upstream/main
```

If you want to see what's changed upstream before applying:

```bash
git log HEAD..upstream/main
```

See the actual diff

```bash
git diff HEAD...upstream/main
```

Then push your updated fork:

```bash
git push origin main
```

If you rebased and need to force push
    
```bash
git push origin main --force-with-lease
```
