Files
terebi/README.md
T

162 lines
11 KiB
Markdown

# terebi
A native macOS player for local media, YouTube videos, and resolved Miruro streams, with compact mode, saved playback positions, and Chromecast support.
## Build and run
Requires macOS 15+, Xcode command-line tools with Swift 6, Python 3.9+, and these build tools:
```sh
brew install pkg-config meson ninja
# Intel Macs also need: brew install nasm
make
make run
```
The first build downloads pinned dependencies. Build natively on Apple Silicon or Intel; cross-architecture and Rosetta builds are not supported.
The resulting `.build/terebi.app` is self-contained: copy the whole app to a compatible Mac of the same architecture. No separate FFmpeg, Python, or yt-dlp installation is needed to run it.
### Install
```sh
make install # builds a release and installs under ~/.local
export PATH="$HOME/.local/bin:$PATH"
terebi --help
```
Use `make install PREFIX="/custom/path"` to change the prefix, or `make release` for just `.build/release/terebi.app`. Installation places the app in `lib/terebi/terebi.app` and its CLI symlink in `bin/terebi`. Quit running players before updating.
Builds are ad-hoc signed for personal use, not notarized. Bundled licenses and source archives are in the app's Resources; public binary redistribution requires a separate dependency/license audit, including the GPLv3+ standalone yt-dlp bundle.
## Usage
Open one or more local files with **⌘O**, drag them onto the video, or use Finder's **Open With**. A selection replaces the session's queue and plays in natural filename order (for example, episode 2 before episode 10). Open YouTube with **⇧⌘O** or drop a video link. Media starts automatically and resumes its saved position; finished videos restart from the beginning. Live and audio-only media do not save progress.
| Action | Default shortcut |
| --- | --- |
| Play / pause | Space or K |
| Seek backward / forward 5 seconds | ← / → |
| Seek backward / forward 10 seconds | J / L |
| Step one frame backward / forward (pauses playback) | , / . |
| Volume down / up | ↓ / ↑ |
| Mute / unmute | M |
| Fullscreen | F or ⌃⌘F; Esc to leave |
| Compact mode | P or ⌃⌘P |
| Always on top | ⇧P |
| Close media | ⇧⌘W |
| Quit | Q or ⌘Q |
Compact mode keeps playback in the same floating window; drag to move it and double-click to restore. The titlebar pin keeps the normal window on top. macOS media keys also control playback; previous/next seek five seconds rather than changing tracks.
Playback controls stay anchored at the bottom center of the video and cannot be dragged.
Active playback prevents idle system sleep. Local video also keeps the display awake; audio-only playback allows display sleep. Pause, end, close, playback failure and quit release sleep prevention.
### Local-file queue
Use the titlebar's **Queue** icon or **View → Show Queue** to show the editable sidebar. **Add to Queue…** and dropping files onto the sidebar append without replacing the queue or interrupting the current item. Drag rows to reorder, remove them with the minus button, and double-click a row to play it. The current item is marked; context menus also offer Play, Remove and Move Up/Down.
In a normal window, showing the queue expands the window outward while preserving the video's size; hiding it removes that extra width. The window shifts left when necessary near the screen's right edge. Fullscreen keeps its fixed bounds.
The queue count and window controls share the top bar across the full expanded window; the sidebar starts with queue navigation and Add to Queue.
Playback advances automatically and stops at the end, without wrapping. **Previous Queue Item** and **Next Queue Item** are available in the sidebar and Playback menu and are disabled at their respective ends. Reordering or removing another item does not interrupt playback; removing the current item plays its next neighbor, or stops if there is none. A playback error stays on that item rather than skipping it.
The queue contains local files only and lasts until it is replaced, closed or the app quits; it has no persistence, shuffle, repeat or playlist-file import. Opening a YouTube video or resolved stream clears it. The sidebar is hidden in compact mode; Playback-menu queue navigation remains available. Sidebar appearance, drag/drop interaction and physical-receiver queue transitions still need manual verification.
### YouTube
Playback defaults to the best available SDR resolution up to **1080p**. Use **gear → Quality** to select another available resolution, including 4K, or enable automatic quality (off by default). **Show Buffer Info** displays the retained network buffer.
Only individual videos are supported, not playlists or channels. Private, age-restricted, region-blocked, or sign-in-required videos may fail; browser cookies are not read. The extractor is bundled, so YouTube compatibility updates require rebuilding/reinstalling terebi, not updating an external yt-dlp.
### Chromecast
Open media, select the **Cast** icon or **Playback → Cast to Device → Find Cast Devices…**, and choose a receiver on the same LAN. Allow macOS Local Network access and incoming firewall connections. Terebi keeps the Mac awake during the handoff and active Cast playback while allowing the screensaver and display sleep once local playback stops. Keep the Mac connected: it relays media to the receiver. Closing the lid, explicitly sleeping the Mac or critical-battery sleep can still interrupt playback.
Existing controls operate remote playback. **Play on This Mac** returns to local playback at the receiver's position without changing the local-file queue. Queue navigation and automatic advance keep the selected receiver; opening a replacement selection or different stream ends casting. Frame stepping and YouTube quality changes are local-only; live Cast playback is not seekable. Cast output is limited to 1080p/30 fps. The screen-and-triangle icon does not provide AirPlay support.
External Cast subtitles use white sans-serif text with a black outline, transparent background and no enclosing window. Font scale currently uses the receiver default (1.0), pending physical-TV tuning. Language selection and Off remain independent of video delivery; no subtitle burn-in is used.
Cast trusts the selected LAN endpoint's self-signed certificate; use it only on a trusted network. Receiver captions and perceived A/V synchronization still need physical-device verification.
## Command line
Before installation, use `.build/terebi` instead of `terebi`:
```sh
terebi "/path/to/video.mp4"
terebi 'https://youtu.be/jNQXAC9IVRw'
terebi --new-instance --url 'https://www.youtube.com/watch?v=jNQXAC9IVRw'
terebi --new-instance --no-config "/path/to/video.mp4"
terebi -- -video.mp4
terebi --help
```
The CLI opens one file or video, reusing the running player by default. With no arguments it opens or activates the player without replacing media. Put `--new-instance` and `--no-config` before media arguments. Launch success means macOS accepted the request, not that playback succeeded; playback errors appear in the window.
### Resolved sources and progress reporting
Miruro playback requires a freshly resolved source document from an external caller such as Tubular; terebi does not discover episodes or accept Miruro page URLs directly.
```sh
terebi --new-instance --source-file '/private/session/source.json' \
--progress-file '/private/session/progress.jsonl'
```
Both options require `--new-instance`; `--progress-file` also works with YouTube. Use a private session directory and a nonexistent report destination. Keep the source file present and unchanged during playback; the caller owns cleanup. Reports contain playback facts, not watched-state decisions.
See the [integration contract](docs/miruro-playback-handoff.md) for source JSON, resource-header/origin restrictions, and progress schemas. Tubular still needs to adopt Miruro's version-2 progress identity.
## Configuration
The optional config is `$XDG_CONFIG_HOME/terebi/config.toml` when `XDG_CONFIG_HOME` is absolute, otherwise `~/.config/terebi/config.toml`. Omitted settings use defaults. To print all supported settings and key actions:
```sh
terebi --default-config
```
Example partial configuration:
```toml
[playback]
automatic_quality = true
[ui]
buffer_info = true
compact_snap_to_edges = true
compact_title_progress = "percentage" # "time", "percentage" (one decimal; 99.9 rounds up to 100.0), or "none"
[keys]
play = "space"
pause = "space"
quit = "cmd+q"
quality_2160p = "ctrl+4"
```
Shortcut strings or arrays replace an action's defaults; `[]` unbinds it. Modifiers use `cmd`, `ctrl`, `alt`/`option`, and `shift`, joined with `+`. A chord may be shared by inverse actions such as play/pause, but not unrelated actions. Invalid settings and conflicting shortcuts are errors.
Settings load once at startup. Menu changes are session-only and never write the file; start a fresh player to apply edits. `--no-config` uses built-in settings on a fresh launch, not in an already-running player, and does not clear saved progress.
## Limits
- SDR output only; PQ/HLG HDR is rejected. No stream playlists, playlist-file import, embedded/YouTube captions, manual audio-track selection, rotation, or deinterlacing. The session-only local-file queue and external resolved-source WebVTT subtitles are supported.
- H.264, HEVC, and VP9 prefer VideoToolbox decoding with software fallback; other codecs, including AV1, use software. Backward frame stepping can be slow on long-GOP or unindexed media.
- Network buffering is bounded and memory-only; live/non-seekable sources disable seeking and frame stepping. Expired stream URLs are not automatically refreshed.
- Audio-device changes during playback are not handled.
## Development checks
```sh
brew install ffmpeg # fixture generation only; not required to build or run terebi
make check
```
The full suite requires a graphical macOS session and a working audio device; it launches isolated app instances and plays short test audio. Targeted `check-*` targets are listed in the [Makefile](Makefile). Visual appearance, physical media-key routing, and receiver behavior still need manual checks.
`make benchmark-playback` measures cold source startup, cached/uncached seeks, and sustained native/Cast playback with repeatable loopback fixtures. It requires the same macOS session/audio device and fixture-generation FFmpeg; results exclude live extraction, WAN/TLS, and physical-receiver latency.
The [benchmark runner](Checks/playback-benchmark.py) accepts `--compare-native <baseline-binary>` for alternating native-only comparisons; add `--seek-work` to isolate cache-miss seeks when both binaries support that mode. Preserve the baseline executable before rebuilding.
Playback terminology: [CONTEXT.md](CONTEXT.md).