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
259 lines
11 KiB
Markdown
259 lines
11 KiB
Markdown
---
|
|
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
|
|
|