Commit Graph
16 Commits
Author SHA1 Message Date
chrisandClaude Opus 5.5 edc0c7b999 Share songs, with link previews
CI / check (push) Canceled after 0s
- "Share ↗" on every song (web and mobile): the phone's share sheet, or
  copy the link on desktop. Links go to a new song page, /t/<id>?from=you,
  with big artwork and open-in buttons for every service.
- Server-rendered previews (Open Graph / Twitter cards) for song pages and
  profiles: title, who shared it or what they're playing, and the album
  art, avatar or logo. The web server fetches from the API over the
  compose network (INTERNAL_API_URL).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 13:31:47 -04:00
chrisandClaude Opus 5.5 50f00f922b Privacy policy and terms
CI / check (push) Canceled after 0s
/privacy and /terms describe what the app actually collects and shows
(public profiles and listening, 50 recent plays, encrypted service tokens,
stripped profile pictures, Android reader scope, song lookups that carry no
identity, 14-day backups, 13+), linked from a web footer, the sign-up
forms and mobile Settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 13:07:54 -04:00
chrisandClaude Opus 5.5 85e1643c29 Email confirmation, change email/password, show/hide passwords
CI / check (push) Canceled after 0s
- Sign-up emails a confirmation link (24h); unconfirmed accounts see a
  banner on the web with "send it again", and status in Settings.
- Change email (needs the password): the new address gets a confirmation
  link and only replaces the old one once clicked; the old address gets a
  heads-up with a reset-password link. Pending changes can be cancelled.
- Change password (needs the current one): signs out other sessions.
- Password fields have Show/Hide on web and mobile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 13:05:26 -04:00
chrisandClaude Opus 5.5 45a7bd190a Safety screens: followers, blocking, reporting, admin
CI / check (push) Canceled after 0s
Web and mobile:
- Follower/following lists (tap the counts), with Follow buttons and
  "Remove" on your own followers.
- Profile ⋯ menu: block (with confirmation) / unblock, and report with a
  reason and optional note. "Follows you" label, "Follow back", and a
  banner on profiles you've blocked.
- Blocked accounts list in Settings, with unblock.
Web only: /admin for the admin account: stats, open/resolved reports,
resolve, suspend/unsuspend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 12:53:28 -04:00
chrisandClaude Opus 5.5 13df568348 Keep progress bars aligned
CI / check (push) Canceled after 0s
The server now sends each fresh position on every poll that has one, and
sends positions advanced to send time; clients count only the time since
they received it. Bars realign every poll and no longer drift by however
far the viewer's clock is off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 12:48:29 -04:00
chrisandClaude Opus 5.5 be0a00a2c1 Song progress bar, and poll right after a track ends
CI / check (push) Canceled after 0s
- Feed and profile cards show a progress bar with elapsed/total time, only
  when the source reports a real position (Plex, Jellyfin, the Android
  reader). It ticks locally between updates. Apple Music's guessed
  position is no longer reported.
- The Android reader advances MediaSession's stale position to now, stamps
  when it was read (the server corrects for upload delay), and reports
  seeks. The server pushes an update to watchers when a position jumps.
- When a polled source says where in the song someone is, the next poll is
  scheduled just after the song ends (never sooner than 5s), instead of
  waiting out the full interval.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 01:24:20 -04:00
chrisandClaude Opus 5.5 1f0cdb68f5 Password reset by email
CI / check (push) Canceled after 0s
"Forgot your password?" on web and mobile sends a one-time link (1 hour)
to the account's email; the website's reset page sets the new password,
signs out every other session and signs in. The request responds the same
whether or not the email has an account, and the email goes out in the
background so timing doesn't reveal it either. Only a hash of the link
token is stored. Sends through SMTP_* settings; without them, emails are
logged (and captured in tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-06 01:05:30 -04:00
chrisandClaude Opus 5.5 2b02aa8e89 Profile pictures, display name default, coloured service names
CI / check (push) Canceled after 0s
- Upload a profile picture on web and mobile. The API square-crops it to
  512px WebP, strips metadata (phone photos carry GPS), stores it in
  Postgres and serves it with immutable caching. Avatar URLs are stored as
  paths, so they survive moving the API. Arbitrary avatar URLs are no
  longer accepted (they'd let a profile track its viewers).
- New accounts get the username as typed as their display name; existing
  accounts are backfilled, and clearing it falls back to the username.
- Service names in Settings get a brand-coloured badge and larger type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-05 23:38:00 -04:00
chrisandClaude Opus 5.5 17ae36064c Web: redesign, logo, PWA, QR codes, Spotify guidance
- Album-art-tinted cards that crossfade on song change, equalizer bars,
  live avatar rings, drifting ambient colour, Space Grotesk display font,
  brand purple-to-blue gradient from the logo
- Feed cards rise in and glide to new positions (motion); respects
  reduced-motion
- New logo as nav mark, favicon and Apple touch icon
- Installable PWA: manifest, maskable icons, small service worker
  (cached build assets + offline page), security headers
- Show-QR-code on your own profile; centred profile header with stats
- Settings explains connecting Spotify via Last.fm or ListenBrainz

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-05 19:16:40 -04:00
chrisandClaude Opus 5.5 63d618f0e3 Unlock everything, add a donation link, credit Last.fm, nudge re-linking
- Every feature is free for everyone (the plan system stays for later);
  remaining limits are abuse guards
- Optional website donation link (NEXT_PUBLIC_DONATION_URL). Not shown in
  the mobile apps, where tips to the developer generally need in-app
  purchase
- Last.fm/ListenBrainz-sourced plays link to the user's profile, as
  Last.fm's API terms require
- Connections track when they last saw activity; a scrobbler silent for
  over a week prompts re-linking Spotify (its link expires every 6 months)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-04 08:13:54 -04:00
chrisandClaude Opus 5.5 507cffbe57 Web: Sign in with Last.fm and explain the Spotify beta limit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-04 07:12:19 -04:00
chrisandClaude Opus 5.5 23faffc251 Add pause controls and app attribution to the web app
Settings gets a Sharing card (pause 1 hour / until tomorrow / until
turned back on); the feed shows a resume banner while paused. Now-playing
cards say which app a phone reported, e.g. "via Spotify".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-03 17:37:30 -04:00
chrisandClaude Opus 5.5 6317004004 Run the whole stack in Docker
- API and web images built with turbo prune; the API image also runs the
  worker and migrations. Non-root, health-checked, Debian slim.
- docker compose up -d --build starts Postgres, Redis (persistent), a
  one-shot migrate job, API, worker and web, in dependency order.
- Fix the API production build (tsup flags moved to a config file) and
  bundle the migration script; silence Postgres NOTICE noise.
- npm run infra:up now starts only Postgres and Redis for hot-reload dev.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-03 16:02:38 -04:00
chrisandClaude Opus 5.5 ece9c4fd64 Add recently played to profiles
A play is recorded when a new track starts (pausing and resuming doesn't
count; returning after 10+ minutes does). Profiles show the last 10,
excluding the track playing now; 50 are kept per user. Recent plays are
free on every plan; the paid flag is renamed to fullHistory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-03 16:02:38 -04:00
chrisandClaude Opus 5.5 87353ccca3 Show your own now-playing at the top of the feed
The feed only listed people you follow, so with no follows it looked like
nothing was being picked up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-03 16:02:38 -04:00
chrisandClaude Opus 5.5 5004beb10d Add Next.js web app
Feed with live updates and pinning, profiles with follow/pin, featured song
picker, settings for all music services, and account deletion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
2026-10-03 09:59:02 -04:00