feat: support multiple video selection

This commit is contained in:
2026-10-22 14:01:47 +02:00
parent acfb3be1e7
commit 2d30120072
12 changed files with 883 additions and 169 deletions
+15 -5
View File
@@ -7,7 +7,7 @@ A modal terminal YouTube and Miruro browser built with Rust and [Ratatui](https:
- A recent stable Rust toolchain, including Cargo, to build and install.
- [`yt-dlp`](https://github.com/yt-dlp/yt-dlp) on `PATH` for YouTube searches, feeds, and full descriptions; Miruro uses the native API client.
- `curl` on `PATH` for YouTube thumbnails and Miruro cover art.
- The **Terebi CLI** installed on `PATH`, together with its app bundle and streaming dependencies, for playback. Both providers use `--new-instance --url … --progress-file PATH`; Miruro requires native Miruro episode-URL resolution and version-2 episode progress support.
- The **Terebi CLI** installed on `PATH`, together with its app bundle and streaming dependencies, for playback. Single videos use `--new-instance --url … --progress-file PATH`; multiple videos require `--new-instance --queue-file PATH` support. Miruro also requires native episode-URL resolution and version-2 episode progress support.
The default playback setup requires **macOS 15+**, since Terebi is a native macOS player. Install Terebi separately and follow its dependency/setup instructions; installing Tubular does not install the player.
@@ -57,7 +57,15 @@ terebi --new-instance --url 'https://www.youtube.com/watch?v=VIDEO_ID' \
--progress-file '/private/session/progress.jsonl'
```
Each selection opens an independent player instance without replacing existing playback. Tubular creates a private reporting session and consumes Terebi's progress records independently of the launcher. `--player` overrides the Terebi CLI executable; it is not a generic mpv/IINA adapter. Tubular reports launcher failures, while later streaming/playback errors appear in Terebi. Quitting Tubular leaves launched players running.
Each submitted selection opens one independent player instance without replacing existing playback. Tubular creates a private reporting session per video and consumes Terebi's progress records independently of the launcher. `--player` overrides the Terebi CLI executable; it is not a generic mpv/IINA adapter. Tubular reports launcher failures, while later streaming/playback errors appear in Terebi. Quitting Tubular leaves launched players running.
### Multiple video selection
In video and episode lists, **Space** checks/unchecks the hovered row; **Ctrl-Space** clears checks. **Enter** plays checked rows in their current displayed order, regardless of the order they were checked. With no checks, Enter plays the hovered row. These controls also work in collection views and metadata previews; Miruro catalog titles and saved shows cannot be checked.
Checks belong to each cached view/screen and follow stable video identities through pagination and reordering. Refresh/replacement queries clear them; collection filtering/removal prunes checks for hidden/removed rows. Successful launcher creation clears checks, while immediate launch failures retain them. Saving, removing and marking watch status still affect the hovered video, not every checked row. Configure `video.select.toggle` and `video.select.clear`; terminals must transmit Ctrl-Space, or bind another chord.
Two or more checked videos launch `terebi --new-instance --queue-file '/private/session/queue.json'`. The private version-1 manifest contains ordered canonical episode/video page URLs and one independent `progress_file` per item. Terebi reuses its queue navigation and resolves each item when selected, not in advance; no expiring playback sources are stored. Each item's watching/watched state and resume identity remain independent, including after Tubular restarts. Single-video launches retain the command above. See the [queue contract](docs/terebi-url-playback-handoff.md#ordered-stream-queue-handoff).
### Miruro browser
@@ -70,7 +78,8 @@ Catalog autocomplete returns at most eight anime titles, without pagination. Tit
| `i` / `/` | Edit the catalog query; Enter searches, Esc cancels |
| `p` | Reopen the provider chooser in normal mode |
| `j` / `k`, ↓ / ↑; `g` / `G`, Home / End | Select a title or episode |
| Enter | Open a series, or pass the selected episode URL to Terebi for resolution and playback |
| Space / Ctrl-Space | Check/uncheck the hovered episode / clear episode checks |
| Enter | Open a series, or play checked episodes in displayed order (hovered episode when none are checked) |
| `-` / `+` | Back / forward through cached screens, preserving query and selection |
| `w` / `v` | Save the selected episode to watch later / saved episodes |
| `s` | Edit the selected show's category tags; Enter saves the show, including with no tags |
@@ -104,7 +113,8 @@ These are the default bindings. Overrides in `[keys]` replace an action's bindin
| Key | Action |
| --- | --- |
| `j` / `k`, ↓ / ↑ | Select the next / previous video |
| Enter | Launch the selected video in Terebi |
| Space / Ctrl-Space | Check/uncheck the hovered video / clear checks |
| Enter | Play checked videos in displayed order, or the hovered video when none are checked |
| `p` | Reopen the provider chooser |
| `o` | Open the selected YouTube video in the default browser (also works in previews; configurable as `video.open`) |
| `g` / Home, `G` / End | Select the first / last loaded video |
@@ -288,7 +298,7 @@ Files are siblings of `config.toml`:
- `currently-watching.toml`: versioned metadata for unfinished videos, added by playback or marked manually.
- `watch-state.toml`: versioned global watched video IDs.
- `miruro-library.toml`: versioned saved-show catalog metadata and overlapping category tags, independent of episode collections.
- `playback-sessions/`: private per-launch progress streams, expected identities, metadata snapshots, and durable admission/manual-edit markers.
- `playback-sessions/`: private per-video progress streams, expected identities, metadata snapshots, durable admission/manual-edit markers, and ordered `queue.json` manifests for multi-video launches.
State writes are atomic and owner-only. Invalid state files or detected external edits are reported rather than overwritten; repair the file or restart before retrying. Each state file is limited to 8 MiB.