#pugstr
ππ’ππ π΄ββ οΈπΆ
ryan@spatia-arcana.com
npub1m64h...uaks
Play Flappy Nostrich @ flappy-nostrich.vercel.app/
Est. 776032 ππ«π€
#pugstr
#pugstr

Hmm. The same signaling events I use to do live playlists could let me remote control desktop playback on #gruuv from my phone π€
#gruuv - testing again π
- yesterday's test of live queue gave me a lot of good feedback for the clanker. I think this update is fairly stable, and they should work. Playback sync & view bugs have been fixed, theoretically.
- something something totally refactor app.tsx, so many lines of code π€·ββοΈ


gruuv
Decentralized Music Sharing
GM Nostr, I'm up early Sunday. Mosha is still resting πΆπ€π€«
Draft Spec: Live Music Rooms (Host-Authoritative)
This specification defines a set of events for hosting real-time, synchronized music listening rooms over Nostr. It relies on a
host-authoritative model where a single DJ broadcasts their queue and playback state, and guests synchronize their local audio
players.
Rooms are identified by a unique d tag (typically the host's pubkey) and categorized using the t tag gruuv-live-room.
1. Live Room Queue (Kind: 32401)
A replaceable event that represents the current playlist/queue of the live room. The host publishes this whenever the track list is
modified (tracks added, removed, or reordered).
* Kind: 32401
* Tags:
* d: The unique Room ID (Recommended: the host's pubkey).
* t: gruuv-live-room
* title: (Optional) The display name of the station.
* Content: A stringified JSON array of track objects.
Content Schema:
1 [
2 {
3 "id": "<string> (Original kind 36787 event ID or fallback ID)",
4 "title": "<string>",
5 "artist": "<string>",
6 "album": "<string>",
7 "url": "<string> (Direct media URL)",
8 "sha256": "<string> (Optional, media hash)",
9 "image": "<string> (Optional, album art URL)",
10 "duration": "<number> (Seconds)",
11 "pubkey": "<string> (Original uploader's pubkey)"
12 }
13 ]
2. Live Playback State (Kind: 32402)
A replaceable event that acts as the real-time synchronization heartbeat. The host publishes this immediately on any playback change
(play, pause, seek, track skip) and also on a regular heartbeat interval (e.g., every 30 seconds).
* Kind: 32402
* Tags:
* d: The Room ID (must match the 32401 d tag).
* t: gruuv-live-room
* Content: A stringified JSON object representing the exact playhead state.
Content Schema:
1 {
2 "sha256": "<string> (ID or SHA256 of the currently playing track from the queue)",
3 "position": "<number> (Playhead position in seconds at the time of the event)",
4 "status": "<string> ('PLAYING' | 'PAUSED')",
5 "createdAt": "<number> (Unix timestamp of when the state was captured)"
6 }
Client Sync Logic: When a guest receives this event, if status is PLAYING, they should calculate the corrected playhead position by
adding the elapsed time since the event's created_at timestamp: correctedPosition = position + (now - created_at).
3. Live Room Chat (Kind: 22402)
An ephemeral event representing a chat message sent by any user within the context of the live room.
* Kind: 22402 (Ephemeral, does not need to be stored long-term by relays).
* Tags:
* d: The Room ID (must match the station's d tag).
* t: gruuv-live-room
* Content: <string> The plaintext chat message.
---
Subscription Example
To discover live rooms and listen to their events, clients should use the following filter:
1 {
2 "kinds": [32401, 32402, 22402],
3 "#t": ["gruuv-live-room"],
4 "limit": 100
5 }
Note: You can easily restrict the subscription to a specific room by adding #d: ["<room_id>"] to the filter.
Good morning. My plan for the day is black coffee, dog walks, and adding live play queues with ephemeral chat to gruuv.
Have a good day π«‘
