Add web dashboard auth system and session management actions

Implement per-admin authentication with IRC-managed accounts
(addSongAdmin/removeSongAdmin/listSongAdmins), session-based login,
and admin presence tracking via WebSocket. Legacy webAuthToken
retained as fallback.

Add rename, clear, and delete actions for archived sessions with
themed modal confirmations (admin-only UI).

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-28 12:02:42 -04:00
parent 7340d59b8e
commit ea40251e0e
7 changed files with 1221 additions and 75 deletions

View File

@@ -0,0 +1,258 @@
---
name: Apple Music Integration
overview: Exploratory investigation of Apple Music API integration options for adding requests to a user's Apple Music queue/playlist and automatically detecting when songs are played, with assessment of feasibility, prerequisites, and architectural approaches.
todos:
- id: prereqs
content: Set up Apple Developer Program account, create MusicKit identifier, generate private key
status: pending
- id: apple-music-module
content: Create SongRequest/apple_music.py with JWT generation and REST API wrapper
status: pending
- id: musickit-js-embed
content: Embed MusicKit JS v3 in the dashboard, add authorize flow and queue control
status: pending
- id: now-playing-detection
content: Add playbackStateDidChange listener in dashboard JS + /api/now-playing endpoint in web.py
status: pending
- id: auto-played-matching
content: Implement trackId matching logic in store/plugin to auto-mark requests as played
status: pending
- id: config-values
content: Add config entries for Apple Developer key, team ID, key ID, playlist ID, and feature toggles
status: pending
- id: recently-played-poller
content: (Optional) Add server-side polling of recently-played API as a fallback
status: pending
isProject: false
---
# Apple Music Integration: Feasibility and Implementation Plan
## Prerequisites (Required for Both Features)
Before any integration work, you need:
- **Paid Apple Developer Program membership** ($99/year) -- required to generate MusicKit keys
- **MusicKit identifier + private key** from Certificates, Identifiers & Profiles in the Apple Developer portal
- **Developer Token (JWT)** -- an ES256-signed JWT with your Team ID and key ID, valid up to 6 months
- **Music User Token** -- obtained via MusicKit JS authorization flow in the browser; required for any personal library/playlist operations. The dashboard operator must have an **active Apple Music subscription**
The current plugin uses the **public iTunes Search API** (no auth needed). Both proposed features require authenticated access to a specific user's Apple Music account.
---
## Feature 1: Add Requests to Apple Music Playlist/Queue
### How It Would Work
There are two viable approaches, each with different trade-offs:
### Approach A: Server-Side REST API (Add to Playlist)
The Apple Music REST API supports adding tracks to a user's library playlist:
```
POST https://api.music.apple.com/v1/me/library/playlists/{playlist_id}/tracks
Authorization: Bearer {developer_token}
Music-User-Token: {user_token}
{
"data": [
{ "id": "{catalog_song_id}", "type": "songs" }
]
}
```
**Architecture:**
```mermaid
sequenceDiagram
participant IRC as IRC User
participant Bot as Limnoria Bot
participant iTunes as iTunes Search API
participant Dashboard as Web Dashboard
participant AM as Apple Music API
IRC->>Bot: !request Artist - Title
Bot->>iTunes: Search query
iTunes-->>Bot: Track results (includes trackId)
Bot->>Dashboard: WebSocket push (new card)
Dashboard->>AM: POST /v1/me/.../tracks (on approve)
AM-->>Dashboard: 200 OK (added to playlist)
Dashboard->>Bot: WebSocket notify (added)
```
**Implementation steps:**
- Add a new module `[SongRequest/apple_music.py](SongRequest/apple_music.py)` for JWT generation (using `PyJWT` + `cryptography`) and REST API calls
- Add config values for Apple Developer key path, key ID, team ID, target playlist ID, and stored Music User Token
- The web dashboard handles the MusicKit JS authorization flow once (popup login), stores the Music User Token, and sends it to the bot backend
- When a request is approved (or auto-approved), the bot calls `POST /v1/me/library/playlists/{id}/tracks` with the iTunes `trackId` (which maps to Apple Music catalog IDs)
- New config option: `appleMusicAutoAdd` (boolean) to toggle this behavior
**Key concern:** The `trackId` from the iTunes Search API should map to Apple Music catalog song IDs, but this needs verification. The iTunes Search API returns `trackId` (numeric), while the Apple Music API expects string catalog IDs. These are typically the same value, just as a string.
**Pros:** Fully server-side, works without the dashboard open, songs appear in a persistent playlist
**Cons:** Adds to a playlist only (not the live playback queue), tracks go to the end of the playlist, no position control, Music User Token expires and needs periodic re-auth
### Approach B: MusicKit JS Web Player (Add to Queue + Play)
Embed MusicKit JS in the web dashboard to directly control Apple Music playback:
```javascript
const music = MusicKit.getInstance();
await music.authorize();
await music.setQueue({ songs: [catalogSongId] });
// Or append to existing queue
await music.playNext({ songs: [catalogSongId] });
```
**Architecture:**
```mermaid
sequenceDiagram
participant IRC as IRC User
participant Bot as Limnoria Bot
participant Dashboard as Web Dashboard + MusicKit JS
participant AM as Apple Music (Playback)
IRC->>Bot: !request Artist - Title
Bot->>Dashboard: WebSocket push (new request)
Note over Dashboard: Operator approves
Dashboard->>AM: MusicKit.playNext(songId)
AM-->>Dashboard: Song added to queue
Note over Dashboard: Apple Music plays on this device
```
**Implementation steps:**
- Add MusicKit JS SDK to `[SongRequest/templates/index.html](SongRequest/templates/index.html)` via `<script src="https://js-cdn.music.apple.com/musickit/v3/musickit.js">`
- Add a "Connect Apple Music" button in the dashboard that calls `music.authorize()`
- On approve/auto-approve, call `music.playNext({ songs: [trackId] })` to queue the song
- Optionally, add a mini player widget to the dashboard showing the current queue
**Pros:** Actually controls the live playback queue, songs play in order, real-time control from the dashboard
**Cons:** Requires the dashboard to be open in a browser on the machine doing playback, browser tab must stay active, only controls playback on that specific device/session
### Recommendation
**Approach B (MusicKit JS)** is better suited to the DJ/streamer use case -- it puts songs directly into the playback queue rather than just appending to a playlist. **Approach A** is useful as a secondary feature (e.g., building a "requested songs" playlist for later). Both can coexist.
---
## Feature 2: Track When Requests Get Played Automatically
### The Problem
Apple Music has **no server-side webhook or push notification** for "now playing" events. There is no way for the bot to passively learn that a song started playing without active client-side monitoring.
### Viable Approaches
### Approach A: MusicKit JS Event Monitoring (Best Fit)
If MusicKit JS is already embedded (from Feature 1, Approach B), the dashboard can listen for playback events:
```javascript
const music = MusicKit.getInstance();
music.addEventListener('playbackStateDidChange', (event) => {
if (event.state === MusicKit.PlaybackStates.playing) {
const item = music.nowPlayingItem;
// Send track ID to bot backend via WebSocket/fetch
fetch('/api/now-playing', {
method: 'POST',
body: JSON.stringify({ trackId: item.id, title: item.title })
});
}
});
```
**Architecture:**
```mermaid
sequenceDiagram
participant AM as Apple Music (Playback)
participant Dashboard as Web Dashboard + MusicKit JS
participant Bot as Limnoria Bot
participant IRC as IRC Channel
AM->>Dashboard: playbackStateDidChange (playing)
Dashboard->>Dashboard: Check nowPlayingItem.id
Dashboard->>Bot: POST /api/now-playing {trackId}
Bot->>Bot: Match trackId against approved requests
Bot->>Bot: Update status to "played"
Bot->>IRC: [NOW PLAYING] Song - Artist (requested by nick)
Bot->>Dashboard: WebSocket push (card updated)
```
**Implementation steps:**
- Add `playbackStateDidChange` and `nowPlayingItemDidChange` event listeners in the dashboard JS
- When a new song starts playing, the dashboard POSTs the track info to a new `/api/now-playing` endpoint
- The bot matches the `trackId` against approved requests in the database
- If matched, auto-updates the request status to "played" and announces in IRC
- Add a `played_at` timestamp column to the requests table for precise tracking
**Pros:** Automatic, real-time, no manual "Mark Played" clicking needed
**Cons:** Requires the dashboard + MusicKit JS to be running, only detects playback from the MusicKit JS session (not from the Apple Music app directly)
### Approach B: Recently Played Polling (Server-Side Fallback)
Poll the Apple Music REST API's recently-played endpoint:
```
GET https://api.music.apple.com/v1/me/recent/played/tracks
Authorization: Bearer {developer_token}
Music-User-Token: {user_token}
```
**Implementation steps:**
- Add a background polling loop (every 30-60 seconds) that fetches recently played tracks
- Compare against approved requests by `trackId`
- Auto-mark matched requests as "played"
**Pros:** Works without the dashboard being open, detects plays from any device/app
**Cons:** Not real-time (30-60s delay), the recently-played API has undocumented limitations (unclear update frequency, unclear history depth, may miss short plays), Music User Token must be stored server-side and refreshed
### Approach C: Hybrid
Use MusicKit JS events (Approach A) when the dashboard is open, and fall back to recently-played polling (Approach B) when it's not. The bot prefers the real-time signal but has the poller as a safety net.
### Recommendation
**Approach A (MusicKit JS events)** is the most reliable and real-time option, especially since the dashboard is already the operator's control panel. **Approach B** could supplement it but has reliability concerns due to the poorly-documented recently-played API.
---
## Summary of Feasibility
| Aspect | Feasibility | Notes |
| -------------------------------- | ------------ | ------------------------------------------ |
| Add to playlist (REST) | High | Well-documented API, straightforward |
| Add to queue (MusicKit JS) | High | Requires dashboard open during playback |
| Auto-detect played (MusicKit JS) | High | Real-time, but scoped to dashboard session |
| Auto-detect played (server-side) | Medium | Recently-played API is poorly documented |
| Server-side webhook/push | Not possible | Apple provides no such mechanism |
## Key Files to Modify
- `[SongRequest/templates/index.html](SongRequest/templates/index.html)` -- embed MusicKit JS, add authorize flow, add event listeners
- `[SongRequest/web.py](SongRequest/web.py)` -- new `/api/now-playing` endpoint, MusicKit config endpoint
- `[SongRequest/plugin.py](SongRequest/plugin.py)` -- auto-play detection callback, IRC announcements
- `[SongRequest/config.py](SongRequest/config.py)` -- new config values for Apple Developer credentials
- New: `[SongRequest/apple_music.py](SongRequest/apple_music.py)` -- JWT generation, REST API wrapper (for Approach A playlist add and/or recently-played polling)
- `[SongRequest/store.py](SongRequest/store.py)` -- optional `played_at` column, query for matching trackId against approved requests
## New Dependencies
- `PyJWT` + `cryptography` -- for generating Apple Developer JWT tokens (server-side)
- No new JS dependencies -- MusicKit JS is loaded from Apple's CDN