A Raspberry Pi that boots into a fullscreen video channel. Plug in a USB drive, it plays. No apps, no accounts, no menus.
Your folder structure is the channel guide. Up/down arrows switch channels, left/right scrubs. Swap USB drives like tapes — each one carries its own watch state, so a drive works on any RPI running PondTV. Pull the power when you're done.
One long-lived mpv process, fullscreen over DRM/KMS — no desktop, no X11. Channel switches are a socket message, not a new process, so they're instant.
Channels from folders. The first folder under a top-level category is a channel; everything deeper collapses into it. Loose files in a category are one channel. Natural sort keeps episode ordering sane. The list is derived on every mount, never stored.
State on the USB. A .pondtv/ directory at the drive root holds resume positions and seen flags, keyed by relative path. Writes are atomic (temp → fsync → rename) and infrequent. The RPI's OS can run on a read-only overlay root, so hard power-off can't brick either side.
Full architecture in docs/PLAN.md.
USB_DRIVE/
├── Movies/
│ └── The Film/
│ ├── The Film.mkv
│ └── The Film.srt ← same name = auto-subtitles
├── TV_Shows/
│ └── ShowName/ ← whole show = one channel
│ ├── Season 01/
│ │ ├── S01E01.mp4
│ │ └── S01E02.mp4
│ └── Specials/
├── Ripped YT/ ← loose files = one channel
│ ├── Video 01.mp4
│ └── Video 02.mp4
└── Selections/ ← hand-picked mix = one channel
| Key | Action |
|---|---|
| Space / Enter | Play / Pause |
| ← / → | Seek 10s (hold = rewind / fast-forward) |
| A / D | Previous / next video in channel |
| ↑ / ↓ | Change channel |
| Backspace | Restart video |
| S | Mark seen (skip to next) |
| T | Trailer mode (skip to unwatched) |
| B | Browser — channel & episode guide |
In the browser: ↑/↓ move the cursor, → (or D / Space) drills into a channel or plays the highlighted episode, ← (or A / Backspace) goes back, S toggles seen, B closes. Watched items show a filled mark, the playing item is flagged, and each channel shows seen/total. Playback chrome is a typographic lower-third (title / seek / pause) — not a terminal menu. Pause shows a quiet one-line hint; scrub shows the hold-ramp step (10s → 30s → 60s). Enter motion: lower-thirds slide up, modals dim-fade, toasts pop (PONDTV_UI_MOTION=0 to disable).
Input is abstracted behind an action enum — HDMI-CEC or a GPIO rotary encoder can plug in later.
RPI 4, RPI OS Lite (64-bit), USB drive with media.
sudo apt install -y mpv python3 python3-venv python3-pip exfatprogs ntfs-3g
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
sudo python3 -m pondtvInstall as a boot service:
sudo cp packaging/pondtv.service /etc/systemd/system/ && sudo systemctl enable --now pondtvSee docs/DEVELOPMENT.md for RPI setup details, running mpv from SSH, and testing without a real USB drive.
Run PondTV on your laptop without a Pi — for testing, demoing, or just watching.
The ./run launcher bootstraps its own isolated venv and installs PondTV in it
so you never think about venvs, pip, or extras. One command:
brew install mpv # one-time system dep (or: sudo apt install mpv)
./run # pops a folder picker on macOS
./run --root ~/fakedrive # scriptable; skips the pickerFirst run sets up behind the scenes and is slow once; later runs skip straight
to launching. On macOS the OS prompts once for Accessibility permission —
grant it so PondTV can read keys regardless of which window has focus
(System Settings → Privacy & Security → Accessibility).
The chosen folder acts as the "USB drive", mpv renders to a desktop window, and
the keyboard is read system-wide via pynput.
Controls are identical to the appliance (Space play/pause, arrows to scrub/surf,
B opens the browser, S marks seen, Esc/Q quits), watch state writes to a
.pondtv/ folder at the chosen root, and the channel model / state persistence /
browser / OSD are all the same code path as the Pi build.
By default the desktop window is app-like, not a video player: no title bar, no mpv bottom control bar, no filename OSD, the cursor hides over the video, and the window stays on top with a clean "PondTV" title. The only UI is PondTV's own ASS overlay. If you ever want the plain mpv window back (e.g. to debug), disable it:
PONDTV_DESKTOP_APP=0 ./run
# or set `desktop_app: false` in ~/.config/pondtv/config.ymlBoth targets render the UI in one constant internal canvas (default
1920×1080, configurable via PONDTV_CANVAS=1080|720|1600x900) and let libass
scale it to the physical output — the Pi's HDMI mode or the pinned desktop
window — so lower-thirds and soft modals are identical everywhere and don't
depend on window size or video resolution. See docs/PLAN.md
(On-screen UI) for the hybrid ASS model.
Optional config.yml, layered: defaults → machine (/etc/pondtv/ or ~/.config/pondtv/) → per-drive (.pondtv/config.yml on the USB) → env vars. Later wins. See packaging/config.example.yml.
trailer_default: false
seek_seconds: 10.0
allow_quit: true- OSD redesign (in progress) — Capsule TTX direction is locked in the browser
prototype (
tests/ux_screens.html, fromtests/gen_ux_screens.py): capsule shells with teletext-style page grammar, context-rich surfaces, and microinteraction motion (scrub morph, browser expand, skip wipe). Nothing from it has landed in the app yet — iterate in the prototype first. - Sleep timer — clean shutdown on a timer
- Smart-seen — auto-mark watched when leaving near the end ✓ (implemented)
- HDMI-CEC input — use the TV's remote (input now pluggable via
InputSource) - GPIO channel knob — rotary encoder as a physical channel dial
- Prebuilt OS image — flashable SD card, ready to boot
Recently landed (2026 refactor): a central event queue + single dispatch thread,
PlaybackController extraction, precomputed season metadata, state pruning on
mount, playlist prefetch, accelerating hold-seek, debounced drive mount, a
constant internal 1080p canvas, and a hybrid ASS OSD (typographic
lower-thirds for playback; soft modals for browser/idle). See
docs/REFACTOR_PLAN.md for the audit and status.
MIT