Commit graph

6 commits

Author SHA1 Message Date
npeter83
04c971f623 feat(feed): resume progress bar, play/continue/restart, in-progress filter
Video cards show a resume progress bar for started-but-unfinished videos and a
hover overlay: Play on every card, Continue + Restart on in-progress ones. The
in-app player now resumes from (and checkpoints to) the server position instead
of localStorage, accepts an explicit startAt (Restart -> 0), and refreshes the
feed on close so the card bar reflects the session. Sidebar gains an
'In progress' show filter.
2026-06-14 18:40:12 +02:00
npeter83
a640b181ee fix(ui): robust avatars — no-referrer + graceful fallback
Channel/account avatars come from Google's image CDNs (yt3.ggpht.com,
lh3.googleusercontent.com). On a feed page dozens load at once; the CDN
rate-limits the referrer-bearing burst (429), so a random subset rendered the
browser's broken-image icon (the URLs themselves are valid — verified 200).

Add a shared <Avatar> that sets referrerPolicy="no-referrer" (which the CDNs
serve without throttling) and falls back to a neutral initial placeholder on
error instead of the broken-image icon. Use it for video-card, player, channel
manager, header and settings avatars.
2026-06-12 18:01:43 +02:00
npeter83
1bdde051c1 feat(player): linkify descriptions and play YouTube links inline
- Narrow the title hover target to the actual text, not the whole row.
- Linkify descriptions: timestamps (mm:ss / hh:mm:ss) seek the player; emails
  become mailto:; hashtags link to YouTube's hashtag feed; other URLs open in a
  new tab. Blank lines are stripped so the popover isn't mostly whitespace.
- YouTube links play in the inline player: a link to the current video seeks
  (honoring t=), a link to another video navigates the player to it, with a Back
  button to the original. While on a linked video the title/author come from the
  player and its views/date/duration + a clickable channel come from the detail
  endpoint, which falls back to the YouTube API (videos.list, attributed to the
  user) for videos not in our DB.
2026-06-12 17:39:20 +02:00
npeter83
195044042f feat(player): polish the description popover (opaque, portaled, opens upward)
The popover bled through (it used the translucent glass surface), was clipped by
the modal card's overflow, and ran off the bottom of the viewport. Make it an
opaque surface, render it through a portal to <body> with fixed positioning so
nothing clips it, and anchor it above the title so it grows upward where there's
room. Add a small hover grace so the pointer can travel title → popover.
2026-06-12 17:39:08 +02:00
npeter83
fcb77ac2e1 feat(player): watched controls, compact layout, description popover
- Watched: an explicit toggle in the modal (Mark watched / Watched→unmark) plus
  auto-mark when playback reaches the end (within 10s, or on the ended event).
- Compact layout: drop the header bar and the redundant 'Open on YouTube' button
  (the embed's own YouTube logo already jumps out); Close moves to the title row,
  channel + meta share one line — fits without a scrollbar at higher zoom.
- Card actions reflect status: watched shows a double-check, saved a filled
  bookmark, with matching tooltips.
- Description: new GET /api/videos/{id} exposes the already-stored description,
  shown in a popover when hovering the modal title (fetched lazily).
2026-06-12 17:39:01 +02:00
npeter83
99dfa7691c feat(player): in-app modal YouTube player with resume
Left-clicking a feed card (Ctrl/Cmd/middle still open youtube.com in a new tab)
opens a modal that plays the video in-app via the YouTube IFrame Player API
instead of leaving the app. Using the JS API (not a bare embed) lets us read the
playback position: it's checkpointed per-video in localStorage and on close, and
restored via the 'start' param when the video is reopened. The modal closes via
a header button, the backdrop, or ESC (ESC only while focus is on our page — a
cross-origin iframe owns its own key events).
2026-06-12 17:38:45 +02:00