Rework the share dialog into two clear modes and add the public /watch player page:
- ShareDialog: (A) 'Share with a user' — autocomplete picker over registered users (was a blind
email box that 404'd on non-users); (B) 'Share a link' — create/list/copy/revoke public links
with allow-download toggle, optional expiry (1/7/30d), optional password; per-link view count.
- WatchPage: standalone login-free player at /watch/<token> (routed in main.tsx like /privacy),
self-contained mini-i18n (en/hu/de by browser language); password gate → unlock → play; shows a
Download button only when the link allows it.
- api: ShareLink/ShareRecipient types + link CRUD + recipients; share i18n (en/hu/de).
Verified end-to-end in a real browser: user picker, link create, public playback, stream-only vs
downloadable, password gate + unlock, no console errors.
VideoEditor modal on a finished Library download: HTML5 <video> scrubber, filmstrip timeline
(lazy server storyboard sprite) with draggable in/out handles, draggable/resizable crop overlay,
per-edit Precise (re-encode) vs Fast (stream-copy) cut toggle, split-into-N fan-out (N trim jobs),
optional clip name. Edited clips show a 'Clip' badge; 'editing' phase gets a % bar. New api
enqueueEdit/downloadStoryboard/storyboardImageUrl + EditSpec type; editor i18n (en/hu/de).
ffmpeg post-steps have no byte-progress, so instead of a silent 'Processing' the row now names
the current step (Merging / Extracting audio / Embedding thumbnail / Removing sponsors / Writing
metadata) with an indeterminate pulse. Byte-progress phases (video/audio) keep the % bar.
i18n en/hu/de.
The progress bar looked broken (95% -> 5% -> recount) because yt-dlp downloads the video and
audio streams separately (each 0->100%) then merges — with no indication of which step is
running. Now:
- worker labels the phase from the stream codecs (video / audio) and via a postprocessor hook
(merging / processing), so the user sees what's happening
- DownloadCenter shows the phase as the status and renders an indeterminate pulse (no bogus %)
during merge/processing; phase i18n en/hu/de
- yt-dlp retries (3) + fragment_retries (5) so transient network blips self-heal instead of
failing the job (seen once as a DNS 'No address associated with hostname' error)
Verified: phase transitions queued -> video -> processing -> done.