Skip to content

Repository files navigation

Bettercast

A modern asciinema player for the terminal with full media-player controls.

Bettercast replays .cast recordings (v2 and v3) with seeking, variable playback speed, text search, bookmarks, and a visual progress bar — features missing from asciinema play.

Installation

uv tool install .

Or run directly:

uv run bettercast recording.cast

Usage

bettercast <file.cast> [--speed 1.0] [--idle-threshold 2.0] [--no-idle-compress]
Flag Description
--speed Initial playback speed (default: 1.0)
--idle-threshold Skip idle gaps longer than this many seconds (default: 2.0)
--no-idle-compress Disable idle time compression

Keyboard Shortcuts

Playback

Key Action
Space Play / Pause
Left / Right Seek backward / forward 5 seconds
Shift+Left / Shift+Right Seek backward / forward 30 seconds
Home / End Jump to start / end
. / , Step forward / backward one frame
[ / ] Decrease / increase speed by 0.5x
l Toggle loop mode

Navigation

Key Action
/ Open search
n / N Next / previous search match
g Go to timestamp (enter MM:SS or H:MM:SS)
Escape Dismiss search or timestamp overlay

Bookmarks

Key Action
m Bookmark current position
b Open bookmark list
{ / } Jump to previous / next bookmark

In the bookmark list:

Key Action
Up / Down Navigate bookmarks
Enter Jump to selected bookmark
d Delete selected bookmark
Escape Close bookmark list

Other

Key Action
c Copy visible screen text to clipboard
? Toggle full help overlay
q Quit

Auto-record Terminal Sessions

To automatically record every interactive terminal session with asciinema, add this to your ~/.zshrc. Sessions are saved to ~/.logs/ and can be replayed with bettercast:

if [[ ( "$TERM_PROGRAM" == "vscode" || "$TERM_PROGRAM" == "ghostty" || "$TERM_PROGRAM" == "iTerm.app" ) && -z "${ASCIINEMA_RECORDING:-}" ]]; then
  if command -v asciinema >/dev/null 2>&1; then
    mkdir -p "$HOME/.logs"
    TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
    PREFIX="${TERM_PROGRAM}"
    export ASCIINEMA_RECORDING=1
    exec asciinema rec "$HOME/.logs/$PREFIX-$TIMESTAMP.cast" -c "$SHELL"
  fi
fi

Adjust the TERM_PROGRAM list to match the terminals you want to record. The ASCIINEMA_RECORDING guard prevents recursive recording when a shell is spawned inside an already-recorded session.

Search Index Cache

Text search requires replaying the whole recording once to build an index, which can take a minute on very large casts. The index is saved next to the recording as <name>.cast.index.gz so it travels with the log — copy or move both files together and the cache stays valid. Reopening an indexed cast is instant. The sidecar is safe to delete at any time; stale or corrupt sidecars (including after the cast grows or changes) are ignored and rebuilt automatically. If the cast's directory isn't writable, bettercast still works — the index just isn't saved.

Supported Formats

  • Asciicast v2 — absolute timestamps, width/height header
  • Asciicast v3 — relative (delta) timestamps, term.cols/term.rows header

Format is auto-detected from the header.

Development

Four gates run in CI on every push and pull request, and each one is runnable locally with the same command CI uses:

uv run pytest -q               # tests
uv run ruff check .            # lint
uv run ruff format --check .   # formatting
uv run mypy                    # type check

Rule selection and type-check scope live in pyproject.toml rather than in workflow flags, so a local run and a CI run always agree on what passes. .python-version pins local development to 3.12, the oldest supported runtime, so a local venv can't silently drift onto a version CI never exercises. CI tests 3.12 and 3.13; its explicit python-version input overrides this file, so the matrix still covers both. uv run ruff format . rewrites files in place.

Visual regression snapshots

tests/test_screenshots.py compares rendered SVG screenshots against references in tests/snapshots/. A missing reference is a failure, not a pass — otherwise a deleted snapshot would silently disable its test. After an intentional UI change, regenerate the references explicitly:

BETTERCAST_UPDATE_SNAPSHOTS=1 pytest tests/test_screenshots.py

That writes the references and reports the tests as skipped, so an update run can't be mistaken for a passing one. Review the resulting diff before committing it.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages