feat: save and categorize Miruro shows

This commit is contained in:
2026-10-17 16:43:54 +02:00
parent 08e4a62228
commit f8334716af
5 changed files with 1162 additions and 23 deletions
+12 -5
View File
@@ -1,6 +1,6 @@
# tubular
A modal terminal YouTube and Miruro browser built with Rust and [Ratatui](https://ratatui.rs/). Search for videos or anime episodes, organize subscribed YouTube channels with overlapping tags, and open a selection in Terebi.
A modal terminal YouTube and Miruro browser built with Rust and [Ratatui](https://ratatui.rs/). Search for videos or anime episodes, organize YouTube channels and saved Miruro shows with overlapping tags, and open a selection in Terebi.
## Requirements
@@ -56,7 +56,7 @@ Each selection opens an independent player instance without replacing existing p
### Miruro browser
Choose **Miruro** at startup or use `--miruro` to start directly. Its interface is **catalog → series → episodes**, without YouTube's Search/Feed tabs, channel controls or category selectors.
Choose **Miruro** at startup or use `--miruro` to start directly. Its interface is **catalog → series → episodes**, with a separate saved-show library organized by overlapping category tags, without YouTube's Search/Feed tabs or channel controls.
Catalog autocomplete returns at most eight anime titles, without pagination. Titles show the available format, status, season/year and planned/listed episode counts; press `d` for the selected title's synopsis and other returned facts. The series screen lists only actual returned episodes—not planned or absolute episode numbers—with titles, dates and durations when supplied. Selected episode details appear first in the metadata popup, above the series synopsis. Listed counts do not guarantee that a supported Sun source is available.
@@ -67,16 +67,22 @@ Catalog autocomplete returns at most eight anime titles, without pagination. Tit
| Enter | Open a series, or resolve a fresh direct **ssub / sun** source and play the episode |
| `-` / `+` | 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 |
| `c` | Choose All saved shows or a category, then Enter opens a saved show’s episodes |
| `m` / `M` | Mark the selected episode as watching / watched (also in metadata) |
| `W` / `V` / `C` | Open Miruro's offline watch-later / saved / currently-watching episodes |
| `f` | Cycle All / Unwatched / Watched in watch later |
| `x` / `X` | Remove the selected / confirm removal of visible episodes from the active collection |
| `x` / `X` | Remove the selected / confirm removal of visible episodes from the active collection; `x` also removes a saved show from the library |
| `d` | Show/hide the synopsis/metadata popup; ↓ / ↑, Ctrl-j / Ctrl-k and page keys scroll; `j` / `k` change selection |
| Esc | Cancel input or close help/metadata and return to the episode/title list |
| `r` | Refresh the active screen and reload configured keys |
| `?` / `q` | Keyboard help / quit |
These actions reuse the existing `[keys]` configuration; live hints and help omit unsupported YouTube actions. Only episodes—not catalog titles—can be saved or have their watch status edited. Reopening a saved episode resolves new URLs; identity is the opaque Miruro catalog ID plus its returned episode number. Each interface shows only its provider's collections, while playback, watched state and storage remain shared. Episode markers show saved/watch-later/currently-watching membership and watched status; Terebi owns resume positions.
These actions reuse the existing `[keys]` configuration; live hints and help omit unsupported YouTube actions. Show saving/tagging uses `channel.tags`, and the library picker uses `category.browse`. From catalog results, a series screen, or the saved-show library, press `s` to edit the **show's** tags: `j` / `k` selects, Space checks/unchecks, `i` filters, and `n` enters a new tag name. Enter stages a new tag; Enter again from the tag list saves the show and all checked tags. Esc cancels without saving. A show can belong to multiple categories or remain saved without tags. The `c` picker starts in normal mode; `i` filters, Enter browses, and Esc leaves filtering before closing. Saved-show lists are alphabetical and stored offline; Enter fetches current episodes using the saved catalog ID and title. `x` removes only the show and its tags, leaving episode collections and watch status unchanged. Categories disappear when no saved shows use them. Miruro's show categories are independent of YouTube's channel tags.
Space checks categories for combined browsing; Enter opens their deduplicated union even when the filter hides them. With no checks, Enter opens the hovered category or All saved shows.
Only episodes can have their watch status edited. Reopening a saved episode resolves new URLs; identity is the opaque Miruro catalog ID plus its returned episode number. Each interface shows only its provider's collections, while playback, watched state and storage remain shared. Episode markers show saved/watch-later/currently-watching membership and watched status; Terebi owns resume positions.
Stream and external VTT subtitle headers are supplied explicitly; Terebi selects the source's declared default subtitle. Sources requiring media redirects, cross-origin HLS, proxies, iframe playback, or provider-specific playlist rewriting fail explicitly. No provider/language fallback or automatic expired-source refresh is performed.
@@ -271,6 +277,7 @@ Files are siblings of `config.toml`:
- `saved-videos.toml`: independent versioned saved-video metadata.
- `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, durable admission/manual-edit markers, and Miruro source documents.
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.
@@ -301,7 +308,7 @@ Use `x` for individual removal or `X` for bulk confirmation. Removal neither mar
## Collection removal
Removal controls apply **only in watch-later, saved-video, and currently-watching views**, including previews opened from those lists. Search, Feed, Miruro catalog/episode browsing, ordinary channel/category views, and their previews do not expose removal actions or hints. Text fields and selectors retain their normal controls.
Episode removal controls apply **only in watch-later, saved-video, and currently-watching views**, including previews opened from those lists. The Miruro saved-show library additionally supports `x` to remove a show and its tags, without removing episodes or watch status. Search, Feed, Miruro catalog/episode browsing, ordinary YouTube channel/category views, and their previews do not expose removal actions or hints. Text fields and selectors retain their normal controls.
`x` immediately removes only the selected video/episode from the active collection. `X` snapshots the current provider's visible list and opens bulk confirmation: YouTube watch later uses the current category and All/Unwatched/Watched filter, and saved videos uses the current category's entire saved list; Miruro uses its flat list and any active watch-later filter. Currently watching uses the current provider's entire list. An empty list does not open confirmation. Neither interface's removal controls touch the other provider's entries.