Three roadmap items together since they touch overlapping code:
Live-stream recording
- New `live: bool` argument on Downloader::start. When true, attaches
`--live-from-start --wait-for-video 30`, suppresses
`--break-on-existing` (every recording is a unique file), and
prepends a UTC timestamp suffix to the output filename so
re-recordings of the same broadcaster don't collide.
- The job label gains a "🔴 LIVE" marker so a long-running record is
obviously different from a VOD pull.
- Both UIs gain a "🔴 Live stream" checkbox in the download panel,
hidden when Music mode is selected. Works for Twitch *and* YouTube
Live since yt-dlp recognizes both from the URL.
- Web `POST /api/download` accepts a `live: bool` body field; all
other callers (scheduled re-check, right-click "Check for new
videos") pass false.
Recent-additions feed
- `library::Video` gains `mtime_unix: Option<u64>` — populated from
the metadata we already read for `file_size`, so no extra fs hit.
- New `SidebarView::Recent` (desktop) + `showRecent` (web) sort the
whole library by mtime descending and cap at 100 entries.
- Sidebar entry "🕒 Recent additions" appears only when the library
has any dated content, so a fresh install doesn't show an empty view.
Per-platform impersonation
- New `Platform::impersonate_target()`:
- Twitch → None (skip impersonation; their OAuth/Helix dislikes
mismatched TLS fingerprints).
- TikTok → "Chrome-Android-131" (mobile profile matches their
first-party app surface).
- Everything else → "Chrome-146:Macos-26".
- `Downloader::apply_impersonation` now takes the platform and routes
through the override; `repair()` (always YouTube-implicit) and
`start_music()` (uses classify_url on the URL) wire through too.
- `/api/preview` does the same classification before dispatching to
yt-dlp.
- 3 new tests confirm Twitch → None, TikTok → mobile, YouTube → desktop.
44 unit tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>