Skip to content

feat: content badges for live and last played media - #1186

Merged
rowkav09 merged 2 commits into
mainfrom
feat/content-badges
Oct 11, 2026
Merged

rowkav09 merged 2 commits into
mainfrom
feat/content-badges

Conversation

@rowkav09

Copy link
Copy Markdown
Member

The hosted service now serves two content badges next to the usage counters, so a README can carry one badge for the film last watched and another for what is playing now.

  • Add GET /badges/now.json (the card's live playback) and GET /badges/last.json (the same, falling back to the most recent playback of that kind whenever nothing of that kind is playing). Both take ?id=<cardId> or ?user=<login> and an optional ?kind=track|episode|movie|show|unknown filter (default any).
  • Labels follow the kind: "listening to" / "watching" / "now playing" live, "last listened" / "last watched" / "last played" for the fallback. Paused playback says so. Idle or unmatched kinds read "not playing" in grey. Messages are capped at 100 characters.
  • On an accepted non-idle ingest, record the last playback of each kind under np:last:user:<githubId>:<kind> or np:last:card:<cardId>:<kind>: only the title, subtitle, state and kind the public card already shows, expiring after 7 days. Recording is best effort and never fails an accepted ingest; idle updates never touch records.
  • Legacy per-PC cards migrate their records to the user scope when they alias during GitHub sign-in, never overwriting a newer record. Revoking a legacy card or signing out everywhere deletes them right away.
  • Refactor card reads to share one presence resolver; badge reads reuse it without counting a card render, so badge traffic does not inflate the requests badge.
  • Badge responses are Shields endpoint JSON with a 60-second cache; no artwork, positions, timestamps, tokens or device data can appear in them.

Validation:

  • Full test suite passes locally: 2,100 tests, 0 failures; guide-path runs and syntax checks pass.
  • New tests cover per-kind recording, kind filtering, the last-played fallback, alias migration with the no-regress guard, revoke and sign-out cleanup, failing-record resilience, badge reads not counting renders, message truncation, error mapping (400/404/405) and cache headers, plus the vercel.json rewrite wiring.

Deployment: the badges go live when the hosted service deploys after merge. This PR does not deploy production or touch the desktop app.

Add /badges/now.json and /badges/last.json to the hosted service. Both
take ?id= or ?user= plus an optional ?kind= filter (track, episode,
movie, show, unknown, default any), so a README can carry one badge for
the film last watched and another for what is playing now.

On an accepted non-idle ingest the service records the last playback of
each kind under np:last:user or np:last:card, holding only the title,
subtitle, state and kind the public card already shows, with a 7-day
expiry. Legacy cards migrate their records to the user scope when they
alias during GitHub sign-in, never overwriting a newer record. Revoking
a legacy card or signing out everywhere deletes them.

Card reads now share one presence resolver; badge reads reuse it without
counting a card render. Badge responses refresh after 60 seconds and
show the media text as a Shields endpoint badge with kind-based labels.

Document the content badges and the 7-day retention in the privacy and
hosted-service docs.
@codecov

codecov Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.63014% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
hosted/lib/app.js 95.55% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@rowkav09
rowkav09 merged commit e123fe2 into main Oct 11, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant