Update ROADMAP: reflect what's actually shipped

Score moved from 8-ahead/8-behind to 11-ahead/4-behind. The four
remaining Tartube-leads items are now Phase 1's entire scope:

- 1.5 per-channel/video notes
- 1.7 format conversion pipeline
- 1.8 per-distro packaging (.deb, .rpm, .exe, .dmg, CI matrix)
- maturity / edge cases (continuous, not a discrete item)

Phase 1 also picks up two extension items for things that shipped at
"v1" depth but where Tartube has more polish:
- 1.3+ filter presets (chips ship, naming/saving doesn't)
- 1.2+ N-level folder nesting (one level ships, deeper doesn't)

Phase 2 2.4 narrowed to just the restore direction since backup ships.

Added a "Recently shipped" section so the doc records what closed
out each line rather than silently dropping it from the table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-05-27 02:25:18 -07:00
parent 615bc0cd4b
commit a8b14f250a

View file

@ -15,7 +15,7 @@ catch up on quickly (Rust + axum + a real web UI + bundled toolchain + a
modern security model) but trails on feature breadth and years-of-edge-cases modern security model) but trails on feature breadth and years-of-edge-cases
maturity. The plan below closes those gaps first, then pushes past. maturity. The plan below closes those gaps first, then pushes past.
## Current state vs Tartube (2026-05-25) ## Current state vs Tartube (2026-05-27)
| Area | Us | Tartube | Verdict | | Area | Us | Tartube | Verdict |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@ -28,76 +28,38 @@ maturity. The plan below closes those gaps first, then pushes past.
| Bundled curl_cffi for impersonation | ✅ | ❌ user-installed | We lead | | Bundled curl_cffi for impersonation | ✅ | ❌ user-installed | We lead |
| Themes | ✅ 10 themes | ❌ GTK default | We lead | | Themes | ✅ 10 themes | ❌ GTK default | We lead |
| Live-stream recording | ✅ | ✅ recent | Tied | | Live-stream recording | ✅ | ✅ recent | Tied |
| Per-channel custom download options | ❌ global only | ✅ deep | **Tartube leads** | | WebSocket real-time progress | ✅ | ❌ polling | We lead |
| Folder/group hierarchy | ❌ flat per-platform | ✅ N-level | **Tartube leads** | | Mobile-responsive web UI | ✅ | ❌ desktop only | We lead |
| Filter UI (date range, size, watched) | partial sort only | ✅ rich | **Tartube leads** | | Per-channel custom download options | ✅ JSON-blob overrides | ✅ deep | Tied |
| Comments capture | ❌ | ✅ `--write-comments` | **Tartube leads** | | Folder/group hierarchy | ✅ one-level | ✅ N-level | Tartube leads (depth only) |
| Filter UI (date / size / watched) | ✅ chip-style | ✅ rich + presets | Tartube leads (presets only) |
| Comments capture | ✅ `--write-comments` + viewer | ✅ raw JSON | We lead |
| System tray | ✅ ksni (Linux SNI) | ✅ GTK | Tied |
| Library backup | ✅ DB snapshot | ✅ | Tied |
| Per-channel notes / annotations | ❌ | ✅ | **Tartube leads** | | Per-channel notes / annotations | ❌ | ✅ | **Tartube leads** |
| System tray | ❌ removed early | ✅ | **Tartube leads** |
| Format conversion / re-encode | ❌ remux only | ✅ ffmpeg pipeline | **Tartube leads** | | Format conversion / re-encode | ❌ remux only | ✅ ffmpeg pipeline | **Tartube leads** |
| Maturity / edge cases | ~weeks | ~years | **Tartube leads** | | Maturity / edge cases | ~months | ~years | **Tartube leads** |
| Per-distro packaging | PKGBUILD only | .deb .rpm .pkg.tar.zst | **Tartube leads** | | Per-distro packaging | PKGBUILD only | .deb .rpm .pkg.tar.zst | **Tartube leads** |
Score: 8 ahead, 8 behind, 1 tied — i.e. we're already half the way there on Score: 11 ahead, 4 behind, 6 tied. The remaining four gaps are concrete,
features Tartube has, plus we have a real lead on architecture. bounded items — see Phase 1 below.
## Phase 1 — Tartube feature parity ## Phase 1 — Remaining Tartube parity items
The eight items Tartube has that we don't, ordered by how visibly they The four items Tartube still has that we don't, ordered by how visibly
change the user experience. they change the user experience.
### 1.1 Per-channel custom download options
Tartube's headline feature. Each channel gets its own quality / format /
extractor args / cookies override that take priority over the global config.
- New `channel_settings` SQLite table (channel_path → JSON blob).
- `Downloader::start` consults overrides before applying global defaults.
- Right-click → "Channel options…" opens a per-channel settings dialog.
- Settings cascade: channel override > group override (Phase 1.2) > global.
### 1.2 Folder / group hierarchy
Lets a user organize channels into "Music", "News", "Coding", etc., with
per-group default download options.
- `channel_groups` SQLite table: `(id, parent_id, name)`.
- `Channel` gains `group_id: Option<i64>`.
- Sidebar renders as a tree; drag-and-drop to reorganize (desktop) /
right-click "Move to group…" (web).
- Group-level download options inherited by member channels unless
overridden per-channel.
### 1.3 Filter UI (date / size / status)
Today we have sort. Tartube has full filtering with chips and saved sets.
- Filter chips above the video grid: "watched / unwatched / in-progress",
"today / this week / this month / older", "< 100 MB / > 1 GB", "has
subtitles", "has chapters".
- Saved filter presets — name a set of chips, restore later.
- Filter state persists across rescans within a session.
### 1.4 Comments capture
yt-dlp's `--write-comments` dumps the full comment tree as JSON sidecar.
- Toggle in download dialog: "Include comments (slow)".
- Stored as `<stem>.comments.json` next to the video.
- New `/api/comments/:id` endpoint returns paginated comments.
- Web video player modal gets a "Comments" tab next to "Chapters".
### 1.5 Per-channel / per-video notes ### 1.5 Per-channel / per-video notes
Free-text user annotations on any channel or video. Free-text user annotations on any channel or video.
- New `notes` table: `(target_kind, target_id, body, updated_at)`. - New `notes` table: `(target_kind, target_id, body, updated_at)`.
- Pencil icon on the channel sidebar entry + video card. - Pencil icon on the channel sidebar entry + video card.
- Notes are searchable from the global filter. - Notes are searchable from the global filter (search hits note body too).
- Web UI: edit-in-place textarea on click.
### 1.6 System tray
Resurrect the dropped tray module. Minimize to tray, show download
progress overlay, click to open the main window.
- Conditional on a non-headless desktop session.
- Right-click → Show / Hide / Quit / Pause downloads.
### 1.7 Format conversion pipeline ### 1.7 Format conversion pipeline
Post-download re-encode option: H.264/AAC mp4 at a configurable CRF, or Post-download re-encode option: H.264/AAC mp4 at a configurable CRF, or
audio extraction at a target bitrate. Useful for shrinking large 4K files. audio extraction at a target bitrate. Useful for shrinking large 4K files.
@ -107,18 +69,38 @@ audio extraction at a target bitrate. Useful for shrinking large 4K files.
- Visible in the job log as a separate "transcoding" phase. - Visible in the job log as a separate "transcoding" phase.
### 1.8 Per-distro packaging ### 1.8 Per-distro packaging
Tartube ships .deb, .rpm, .pkg.tar.zst, .exe, .dmg. We ship a PKGBUILD. Tartube ships .deb, .rpm, .pkg.tar.zst, .exe, .dmg. We ship a PKGBUILD.
- Codeberg CI matrix for `cargo build --release` on each target. - Codeberg CI matrix for `cargo build --release` on each target.
- Helpers to generate .deb (via `cargo-deb`), .rpm (via `cargo-generate-rpm`), - Helpers to generate .deb (via `cargo-deb`), .rpm (via `cargo-generate-rpm`),
Windows MSI (via `cargo-wix`), and macOS .app + .dmg. Windows MSI (via `cargo-wix`), and macOS .app + .dmg.
- GitHub-style release artifacts attached to each tag. - Release artifacts attached to each tag.
### 1.3+ Filter presets (extension of completed 1.3)
The chip filters ship, but Tartube also lets you *name* a filter set and
restore it later. Small UI layer on top of what's already implemented.
- "Save current filters as…" button next to the clear-filters link.
- Presets stored in localStorage (web) / SQLite (desktop).
- Dropdown chip to apply a saved preset.
### 1.2+ N-level folder nesting (extension of completed 1.2)
Folders are flat right now (one level under each platform). Tartube
supports arbitrary nesting.
- Add `parent_id: Option<i64>` to `channel_folders`.
- Sidebar renders the tree recursively with disclosure triangles.
- "Move folder into folder" action in the manager.
## Phase 2 — Polish where Tartube is mature ## Phase 2 — Polish where Tartube is mature
Things we win on architecturally but lose on real-world ruggedness. Things we win on architecturally but lose on real-world ruggedness.
### 2.1 Integration test coverage ### 2.1 Integration test coverage
47 unit tests is good for parsers and helpers, useless for end-to-end 47 unit tests is good for parsers and helpers, useless for end-to-end
correctness. We need real-yt-dlp integration tests against a recorded correctness. We need real-yt-dlp integration tests against a recorded
fixture corpus. fixture corpus.
@ -129,30 +111,35 @@ fixture corpus.
- Headless web-UI tests via `headless_chrome` or `playwright`. - Headless web-UI tests via `headless_chrome` or `playwright`.
### 2.2 Documentation site ### 2.2 Documentation site
README is the only user-facing doc. Need a real docs site with: README is the only user-facing doc. Need a real docs site with:
- Per-platform setup guide (deeper than current README sections). - Per-platform setup guide (deeper than current README sections).
- Troubleshooting playbook for the top 10 yt-dlp errors. - Troubleshooting playbook for the top 10 yt-dlp errors.
- Architecture page (for contributors). - Architecture page (for contributors).
- Hosted via Codeberg Pages or GitHub Pages mirror. - Hosted via Codeberg Pages.
### 2.3 Error recovery / structured logging ### 2.3 Error recovery / structured logging
Today: errors land in the job log and that's it. Tartube has a "rescue Today: errors land in the job log and that's it. Tartube has a "rescue
recipes" feature where common errors map to documented fixes. recipes" feature where common errors map to documented fixes.
- `Job::failure_reason: Option<ErrorClass>` enum (`RateLimited`, - `Job::failure_reason: Option<ErrorClass>` enum (`RateLimited`,
`MissingCookies`, `CodecMissing`, `DiskFull`, `Other`). `MissingCookies`, `CodecMissing`, `DiskFull`, `Other`).
- UI surfaces the class with a one-click suggested action. - UI surfaces the class with a one-click suggested action.
- Anonymous opt-in error telemetry to surface new patterns. - Optional anonymous error telemetry to surface new patterns.
### 2.4 Backup / restore ### 2.4 Library restore (backup is done)
Tartube has database export. We have nothing.
- `/api/backup` → tar of `yt-offline.db` + `config.toml` + `cookies.txt`. Snapshot DB download works. Restore doesn't.
- `/api/restore` (idempotent merge of watched + positions).
- "Export library snapshot" button in settings. - "Import library backup…" file picker in settings.
- Validate schema version before merging.
- Idempotent merge of watched + positions + flags (so re-importing the
same backup twice doesn't break anything).
### 2.5 Stability hardening ### 2.5 Stability hardening
Long-running deployments will surface real bugs. Long-running deployments will surface real bugs.
- Replace remaining `.lock().unwrap()` with poisoning-aware accessors. - Replace remaining `.lock().unwrap()` with poisoning-aware accessors.
@ -160,45 +147,53 @@ Long-running deployments will surface real bugs.
and re-queue. and re-queue.
- Disk-full preflight: refuse to start a download when target dir has - Disk-full preflight: refuse to start a download when target dir has
less free space than the average video size on disk. less free space than the average video size on disk.
- Panic hook that writes to `yt-offline.crash.log` so users have something
to attach to bug reports.
## Phase 3 — Surpass ## Phase 3 — Surpass
Once we're at parity, we push past Tartube on its own ground. Once we're at parity, we push past Tartube on its own ground.
### 3.1 Cross-compile macOS + Windows binaries ### 3.1 Cross-compile macOS + Windows binaries
The current "later" roadmap item, blocked behind 1.8. The current "later" roadmap item, blocked behind 1.8.
### 3.2 Android client ### 3.2 Android client
Native client over the existing web API. Background download via Native client over the existing web API. Background download via
WorkManager + JobScheduler. Push notifications via Tailscale-routed WorkManager + JobScheduler. Push notifications via Tailscale-routed
HTTPS or a userland push channel. HTTPS or a userland push channel.
### 3.3 Real-time progress via WebSocket
Replace the adaptive HTTP poll with a persistent `/ws/progress`
connection. Server pushes job updates; client renders without polling.
Saves the per-second tick and gives instant feedback.
### 3.4 Smart auto-tagging ### 3.4 Smart auto-tagging
Cluster channels by uploader frequency, content type, and metadata. Cluster channels by uploader frequency, content type, and metadata.
Suggest groups ("looks like a music channel — move to Music?"). Builds Suggest groups ("looks like a music channel — move to Music?"). Builds
on Phase 1.2's group system. on Phase 1.2's group system.
### 3.5 Federation / multi-host ### 3.5 Federation / multi-host
A "remote library" mode where one yt-offline instance can browse A "remote library" mode where one yt-offline instance can browse
another's library (read-only) over the same axum API. Useful for a another's library (read-only) over the same axum API. Useful for a
"home archive + travel laptop" setup. "home archive + travel laptop" setup.
### 3.6 Comment viewer with diff ### 3.6 Comment viewer enhancements
The Phase 1.4 comments JSON is just data. We can build the *viewer*
Tartube doesn't have: threaded display, search, "new since last visit" The comments capture (1.4) already ships a viewer. Surpass Tartube
highlights, sentiment filter. (which only dumps the raw JSON) with:
- Threaded display with collapse/expand at any depth.
- Full-text search within a video's comments.
- "New since last visit" highlights.
- Sentiment / keyword filter chips.
### 3.7 Library-wide deduplication ### 3.7 Library-wide deduplication
Right now `maintenance` finds duplicates by yt-dlp video ID. Surpass by Right now `maintenance` finds duplicates by yt-dlp video ID. Surpass by
fingerprinting media bytes (`ffmpeg`-derived perceptual hashes) so the fingerprinting media bytes (`ffmpeg`-derived perceptual hashes) so the
same video re-uploaded under a different ID gets flagged. same video re-uploaded under a different ID gets flagged.
### 3.8 Plugin / scripting hook ### 3.8 Plugin / scripting hook
Lua or WASM-based hooks that run on download events: pre-download Lua or WASM-based hooks that run on download events: pre-download
filename rewriter, post-download archive uploader, custom metadata filename rewriter, post-download archive uploader, custom metadata
enricher. Inverts the "we hardcode everything" model. enricher. Inverts the "we hardcode everything" model.
@ -213,13 +208,39 @@ Probably never, or much later.
- Integration with Plex / Jellyfin / Kodi as a *source plugin* rather than a - Integration with Plex / Jellyfin / Kodi as a *source plugin* rather than a
symlink generator. symlink generator.
## Recently shipped (highlights)
Roughly reverse-chronological. Items that closed out a roadmap line.
- **System tray** (1.6) — ksni-based SNI tray, minimize-to-tray opt-in.
- **Filter chips** (1.3) — watch / date / size / has-subs / has-chapters,
AND together, persisted to localStorage.
- **Web downloads modal** — bottom #jobs bar replaced with a ⬇ button
in the header opening a full modal. `d` keyboard shortcut.
- **Desktop screens refactor** — Settings/Stats/Maintenance moved from
floating windows to full CentralPanel views with vertical scroll.
- **WebSocket job progress** (3.3) — replaced HTTP polling.
- **Mobile-responsive web UI** — proper media queries at 640px / 380px.
- **Library backup** (2.4 — backup direction) — DB download from settings.
- **Theme contrast fixes** — every theme now passes per-state fg_stroke
contrast checks.
- **Shuffle play** — random unwatched video on the desktop + web.
- **Keyboard shortcuts**`/` `r` `d` `?` in the web UI.
- **Bulk tagging + channel-name search** — multi-select + flag bulk-set.
- **Channel folders + per-folder Check all** (1.2) — one-level grouping.
- **Per-channel download options** (1.1) — JSON-blob overrides applied
on scheduled re-checks.
- **Per-video state flags + smart folders + comments capture** (1.3/1.4)
— favourite / bookmark / waiting / archive flags as smart-folder views;
`--write-comments` with viewer tab.
## How to read this ## How to read this
- **Phase 1** is the next ~3 months of work if pursued steadily. - **Phase 1** is the remaining parity work — four bounded items.
- **Phase 2** is concurrent polish — pick up between Phase 1 items. - **Phase 2** is concurrent polish — pick up between Phase 1 items.
- **Phase 3** is the year-1 ambition. - **Phase 3** is the year-1 ambition once we're at parity.
- **Phase 4** items might be valuable, but commit to nothing. - **Phase 4** items might be valuable, but commit to nothing.
Items inside a phase are loosely ordered by user-visible impact, not strict Items inside a phase are loosely ordered by user-visible impact, not strict
prerequisite. **1.1 (per-channel options)** is the single biggest gap and prerequisite. **1.8 (per-distro packaging)** is the biggest multiplier
should be tackled first. for new users; **1.5 (notes)** is the biggest workflow gap.