metube/UI_REDESIGN.md

324 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MeTube UI Redesign — Design Doc
Date: 2026-08-09
Status: Proposal (not yet implemented)
## 1. Background
MeTube (this fork) is a self-hosted web GUI for yt-dlp: Python 3.13 backend
(`aiohttp` + `python-socketio`, ~2,000 LOC) and an Angular 19 frontend
(Bootstrap 5.3 + ng-bootstrap, ~2,400 LOC, PWA).
The frontend is a single-screen app built around one monolithic component:
- `ui/src/app/app.component.ts` — 744 LOC
- `ui/src/app/app.component.html` — 595 LOC, with the same table markup
triplicated across three tabs
- `ui/src/app/app.component.sass` — 341 LOC
- Supporting: `downloads.service.ts` (socket/HTTP state), 2 checkbox
components, 4 pipes, theme/format helpers
The UI is stock Bootstrap: three tabs (Downloading / Pending / Completed),
each a 7-column `<table>`; an inline add-form; a collapsible "Advanced
Options" card; `alert()` for errors; cookie-persisted preferences.
## 2. Problems with the current UI
1. **Table-based lists**: 7 text columns, horizontal `overflow-auto` scroll on
mobile, no thumbnails — recognizing "which video is this" from text is slow.
2. **No design language**: default Bootstrap chrome, no empty states, no
skeleton loaders, "Connecting to server..." as the loading experience.
3. **Errors via `alert()`**: blocking, non-actionable, disappears.
4. **Monolithic component**: imperative `@ViewChild(...).nativeElement.disabled`
manipulation (7+ instances), triplicated template markup, dead tooling
(tslint/codelyzer/protractor). Every change is slower and riskier than it
should be.
5. **List tooling doesn't match real data volumes**: the primary user runs
~230 queued / ~300 completed items. Plain scrolling tables are unusable at
that scale — no filter, no sort, weak batch selection, no virtualization.
## 3. Decision: redesign in place, do NOT rewrite the stack
Evaluated and rejected: rewriting the frontend in Next.js (or Vite + React),
and rewriting the backend in Node.
Rationale:
- ~90% of the user-visible benefit comes from the **design**, not the
framework. Cards, thumbnails, filter/sort, batch actions, virtualization
are all achievable in the existing Angular + Bootstrap stack (Angular CDK
has virtual scrolling; Bootstrap renders cards fine).
- The rewrite's only unique benefit is code health / developer velocity,
which doesn't pay off for what is largely a one-off redesign.
- Regression risk: cookie-persisted prefs, folder tag-creation, socket
string-parsing quirks, PWA/service-worker setup would all need to be
rediscovered and re-tested.
- Next.js specifically is the wrong shape for this app: a LAN SPA with no
SEO/SSR needs. If a rewrite ever happens, Vite + React is the right-sized
tool — but it is not worth doing now.
- The backend must stay Python regardless: yt-dlp is Python-native, and the
subtle, battle-tested logic (precheck worker, filename-conflict resolution,
multiprocess lifecycle, playlist expansion, cookie wiring) lives in
`app/ytdl.py`. A Node port is 1.53 weeks of risk for zero user-visible gain.
If the frontend keeps needing substantial work after this redesign and Angular
becomes a friction point, revisit a rewrite then — with a proven design to
port, it becomes a mechanical ~2-day job.
## 4. Goals / Non-goals
Goals:
- Modern card-based, thumbnail-first UI for all three lists.
- Handle 200500-item lists comfortably: filter, sort, folder grouping,
multi-select batch actions, virtualized rendering.
- Keep "now downloading" always visible.
- Replace `alert()` with toasts; add empty states and skeleton loading.
- Mobile-friendly (cards stack; no horizontal table scroll).
- Refactor the monolith into components as part of the work (only as much as
the redesign requires — no gratuitous re-architecture).
Non-goals:
- No framework/stack change (stays Angular 19 + Bootstrap 5.3).
- No backend queue/precheck/persistence changes, except the additive
thumbnail endpoint (§6.3).
- No new features beyond what the design requires (no new download options,
no playlist monitor — that's a separate planned effort).
## 5. Proposed design
### 5.1 Layout (desktop)
```
┌──────────────────────────────────────────────────────────────────────────┐
│ ▶ MeTube ⬇ 2 active · 12.4 MB/s 🌙 ⚙ │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 🔗 Paste video / playlist URL… [Add] │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ [Best][1080p][720p] [MP4▾][MP3][M4A] 📁 movies ⋯ Advanced │
├──────────────────────────────────────────────────────────────────────────┤
│ ── NOW DOWNLOADING (2) ───────────────────────────── 12.4 MB/s total ── │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ [thumb] Title ███████████████░░░░░ 62% 12.4MB/s ETA 0:43 ✕ │ │
│ │ [thumb] Title ████░░░░░░░░░░░░░░░ 18% 3.1MB/s ETA 4:12 ✕ │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ [ Queued 230 ] [ Completed 303 ] 📁 All folders ▾ ▦ | ☰ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ ☑ Select 🔍 Filter titles… Sort: Date added ▾ Group: 📁 │ │
│ ├───────────────────────────────────────────────────────────────────┤ │
│ │ ▸ 📁 shows (86) ☐ all │ │
│ │ ▸ 📁 music (54) ☐ all │ │
│ │ ▾ 📁 movies (90) ☐ all │ │
│ │ ☐ [thumb] Movie title one 720p · queued 2d ago ✕ │ │
│ │ ☑ [thumb] Movie title two 720p · queued 2d ago ✕ │ │
│ │ ☐ [thumb] Movie title three 720p · queued 3d ago ✕ │ │
│ │ …(virtualized scroll)… │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ 2 selected ── [ ▶ Start ] [ ✕ Cancel ] [ 📁 Move to… ] │
└──────────────────────────────────────────────────────────────────────────┘
```
### 5.2 Advanced options slide-over
Replaces the inline collapsible card. Rarely-changed controls move here;
cookie-persisted defaults keep the main bar to one row.
```
┌────────────────────────────┐
│ Advanced options ✕ │
│ │
│ Auto-start [ toggle ]│
│ Playlist strict [ toggle ]│
│ Playlist limit [ 10 ] │
│ │
│ ── Batch ────────────── │
│ [Import URLs] [Export] │
│ [Copy all URLs] │
│ │
│ ── Cookies ──────────── │
│ domain.com ✓ saved │
│ [Paste cookie…] │
└────────────────────────────┘
```
### 5.3 Mobile
Cards stack vertically; the active panel, tabs, filter bar, and list reflow to
single-column. No horizontal scrolling anywhere. The ⋮ menu and slide-over
become bottom sheets.
```
┌─────────────────────────┐
│ ▶ MeTube 🌙 ⚙ │
│ ⬇2 · 12MB/s │
├─────────────────────────┤
│ ┌─────────────────────┐ │
│ │🔗 Paste URL… [Add]│ │
│ └─────────────────────┘ │
│ [Best][1080p][720p] │
│ 📁 movies ⋯ More │
│ │
│ NOW DOWNLOADING (2) │
│ ┌─────────────────────┐ │
│ │[thumb] │ │
│ │ Title goes here │ │
│ │ ████████░░░ 62% │ │
│ │ 12MB/s · ETA 0:43 ✕ │ │
│ └─────────────────────┘ │
│ │
│ [Queued 230][Done 303] │
│ ┌────────┐ ┌────────┐ │
│ │ thumb │ │ thumb │ │
│ │ title… │ │ title… │ │
│ │ 384MB ⋮│ │ 1.2GB ⋮│ │
│ └────────┘ └────────┘ │
└─────────────────────────┘
```
### 5.4 Key design decisions and rationale
1. **Tabs for the archives, pinned panel for active downloads.**
Stacked sections (all three lists on one scrolling page) were considered
and rejected: at 230/303 items the lists never fit on a screen, so the
"pipeline at a glance" argument fails. Tabs hide state, so each tab carries
a count badge. The active-downloads panel stays pinned above the tabs
because its size is bounded by the concurrency limit (~3) — it costs
nothing and keeps live progress always visible.
2. **Tables → thumbnail-first cards/rows.**
Recognition-by-image is far faster than recognition-by-text; it also
eliminates the 7-column mobile squeeze. Completed items get a grid/list
toggle (▦|☰) — the completed tab is a media library (browse, play, move,
delete), not an audit log.
3. **Command bar instead of a form.**
Usage is 95% two actions: paste URL, watch progress. Quality/format become
one-tap chips with cookie-persisted defaults; folder picker stays; prefix
and everything else moves to the slide-over.
4. **List tooling matched to volume** (the main volume-driven change):
- Per-tab filter box (title substring), sort (date added, title, size,
folder), and folder grouping.
- Folder groups are collapsible with per-group select-all.
- Multi-select with a batch action bar (start / cancel / delete / move to
folder). With 230 queued items the real operations are plural.
- Virtualized scrolling (Angular CDK `cdk-virtual-scroll-viewport`) — render
~30 rows instead of 533 DOM subtrees. Concrete perf win over the current
tables.
5. **Toasts replace `alert()`** — non-blocking, can carry actions (e.g.
"Duplicate skipped — view item"). The recent-events panel becomes a
collapsible activity drawer (diagnostic info: available, not permanently
occupying screen space).
6. **Theme**: keep the existing dark/light/auto Bootstrap `data-bs-theme`
mechanism; refine palette to one accent color. Header gets slim stat chips
(active count, total speed) replacing the current navbar counters.
### 5.5 Open question: per-folder navigation
If queue work is frequently per-folder ("start all the music ones"), folder
groups may deserve first-class status — e.g. a qBittorrent-style left sidebar
of folders with counts instead of group headers inside the list. Decide based
on actual usage before implementation; default is the group-header design
above (simpler, keeps single-column layout).
## 6. Implementation plan
### 6.1 Component refactor (Angular, in place)
Break the monolith into:
- `CommandBarComponent` — URL input, quality/format chips, folder picker,
slide-over trigger. Owns the add-form state and cookie-persisted defaults.
- `AdvancedOptionsComponent` — slide-over (auto-start, playlist options,
import/export/copy, cookie paste).
- `ActiveDownloadsComponent` — pinned now-downloading panel (rows + total
speed).
- `DownloadListComponent` — reusable list for Queued and Completed tabs:
filter, sort, folder grouping, multi-select, batch action bar,
`cdk-virtual-scroll-viewport`. Grid/list toggle applies to Completed.
- `DownloadRowComponent` — thumbnail, title, progress/status line, folder
badge, actions (cancel / start / move / delete / reveal).
- `ToastsComponent` / toast service — replaces `alert()`.
- Keep: `downloads.service.ts` (socket + HTTP state) largely as-is;
`downloads.pipe.ts` (eta/speed/fileSize); theme helpers. The triplicated
tab markup collapses into two instances of `DownloadListComponent`.
### 6.2 Frontend behaviors to preserve exactly
- Cookie-persisted prefs: quality, format, theme, active tab, sort, grouping.
- Folder picker with tag-creation (currently ng-select) and per-row folder
editing (calls `POST /set_folder`; moves completed files on disk).
- Socket.IO event handling: events arrive as JSON *strings* and are parsed
client-side; full state re-sync on connect via the `all` event.
- PWA/service-worker setup (`custom-service-worker.js`, `ngsw-config.json`).
- Import/Export/Copy URLs batch modal.
- Existing HTTP API usage: `POST /add`, `POST /delete`, `POST /start`,
`POST /set_folder`, `POST /cookie`, `GET /history`, `GET|POST /events`,
`GET /version`.
### 6.3 Backend addition: thumbnail endpoint (additive only)
Cards need thumbnails. yt-dlp metadata already contains `thumbnail` URLs, but
hot-linking remote thumbnails from a LAN app leaks requests to third-party
hosts and breaks for expired URLs. Add one endpoint:
- `GET /thumbnail?id=<download_id>` — backend fetches/caches the thumbnail
(cache under `STATE_DIR/thumbnails/`, keyed by download id), returns the
image with long-lived cache headers. Fall back to a placeholder icon when
no thumbnail exists (e.g. audio-only downloads).
No other backend changes. Queue, precheck, conflict resolution, persistence,
socket events are untouched.
### 6.4 Phasing
1. **Phase 1 — component split** (no visual change): extract components from
the monolith, keep the tables. Establishes the structure the redesign
builds on; low regression risk, easily reviewable.
2. **Phase 2 — list redesign**: `DownloadListComponent` with cards, filter,
sort, folder groups, multi-select, virtualization; pinned active panel;
tabs with count badges.
3. **Phase 3 — command bar + slide-over**: new add flow, toasts, activity
drawer, empty states, skeletons.
4. **Phase 4 — thumbnails**: backend endpoint + wire into rows/grid.
5. **Phase 5 — polish**: palette/accent, mobile bottom sheets, cleanup of dead
tooling (tslint/codelyzer/protractor configs) if desired.
Phases 13 deliver the bulk of the UX gain; Phase 4 is the biggest visual
upgrade but is separable if backend changes need to wait.
### 6.5 Effort estimate
| Phase | Estimate |
|---|---|
| 1. Component split | 0.51 day |
| 2. List redesign | 11.5 days |
| 3. Command bar + toasts | 0.51 day |
| 4. Thumbnails (front + back) | 0.5 day |
| 5. Polish | 0.5 day |
| **Total** | **~34 days** |
(For reference, the rejected full rewrite in React was estimated at 35 days
for the frontend alone, plus regression risk on working behaviors.)
## 7. Risks
- **Virtual scroll + folder groups**: `cdk-virtual-scroll-viewport` works best
with flat lists; collapsible groups need flattening logic in the component.
Mitigation: flatten grouped data into a single array with group-header rows
(standard pattern).
- **Thumbnail fetching load**: 300 completed items loading thumbnails at once.
Mitigation: backend cache + lazy loading (`loading="lazy"` / viewport-based).
- **Bootstrap theming limits**: the refined palette may fight Bootstrap
defaults. Mitigation: CSS custom properties on top of `data-bs-theme`,
not a Bootstrap rebuild.
- **Regression in preserved behaviors** (§6.2): mitigate with manual test
checklist per phase; the project has no real frontend test suite, and
adding one is out of scope.