Commit Graph

3 Commits

Author SHA1 Message Date
Georges-Antoine Assi
af4b057894 feat(v2): screenshot-forward covers + cover-art PIP for continue-playing & activity
Show a "where you left off" screenshot on the Home continue-playing rail and
the live-activity board, with a small cover-art thumbnail (PIP) in the corner
so the game stays identifiable. Both render at the image's natural aspect.

Backend:
- New shared util `continue_playing_screenshot(rom, latest_save)` resolving the
  image in priority order: latest save's screenshot, then title screen, then
  first gameplay screenshot (None → frontend falls back to cover art).
- `SimpleRomSchema.screenshot_path` populated only on the `last_played` query;
  `get_latest_saves_for_roms` batch handler (+ tests).
- ActivityEntry / ActivityEntrySchema gain `screenshot_path`, computed from the
  session player's latest save in both the socket and REST heartbeat paths.

Frontend:
- New shared `CoverArtPip.vue` (bottom-right 2D cover thumbnail), reused by
  GameCard and ActivityCard.
- Home continue-playing rail uses `screenshot_path` + PIP, natural aspect (no
  forced hero/style).
- Activity board: screenshot-forward cover + PIP, and a wrapping flex layout so
  cards share a uniform height with natural-ratio widths (gallery-card
  behavior).
- GameCover only keys the measured ratio by rom id for the rom's own cover, so
  a `coverSrc` override (screenshot) never pollutes the gallery's ratio cache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:40:56 -04:00
Georges-Antoine Assi
0460133992 Secure activity identity, cut Redis churn, remove v1 activity
Backend:
- Resolve the acting user from the authenticated socket session on
  connect instead of trusting the client-supplied user_id, so a client
  can no longer spoof a "now playing" session for another user. Only
  rom_id/device_id come from the payload.
- Emit activity:update/clear through the already-initialised socket
  server instead of opening (and leaking) a fresh AsyncRedisManager per
  REST heartbeat.
- Collapse get_all_active's per-key GET into a single MGET.
- Drop the pure pass-through _build_activity_entry helper.

Frontend:
- Remove all activity emits from the v1 EmulatorJS Player; the v2 shell
  is the single driver of the activity lifecycle.
- Remove activity from the v1 UI entirely (Activity view, ActivityBtn,
  ActivePlayers on game details, navigation, and the now-v2-only route).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:12:18 -04:00
Claude
227ae0fa41 feat: real-time user game activity monitoring
Add live tracking of which users are actively playing which games, covering
both browser-based EmulatorJS sessions and external devices via a heartbeat
endpoint. Surfaces activity on a dedicated /activity page and as an indicator
on individual game detail pages.

Backend
- New handler/activity_handler.py stores ephemeral "now playing" state in
  Redis with a 90s TTL keyed by (user_id, device_id), plus a per-ROM reverse
  index for fast lookup.
- New endpoints/activity.py exposes GET /api/activity, GET /api/activity/rom/{id},
  and POST /api/activity/heartbeat (for external devices).
- New endpoints/sockets/activity.py handles browser activity:start /
  activity:heartbeat / activity:stop events, broadcasts activity:update /
  activity:clear to all connected clients, and cleans up on socket disconnect.

Frontend
- Pinia activity store syncs via Socket.IO and provides per-ROM getters.
- ActivePlayers component on GameDetails shows who is currently playing a
  title, with avatar tooltips.
- New Activity view lists every live session with game cover, user, platform,
  and elapsed time.
- ActivityBtn in MainAppBar with a live-count badge routes to the page.
- EmulatorJS Player emits start/heartbeat/stop events and tears down the
  heartbeat interval on exit.

No DB migration required; all state is Redis-resident.

https://claude.ai/code/session_01WzWu5XEEYcAc3EJcfteiFd
2026-04-16 12:18:36 +00:00