- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
This document explains SuvMusic’s social features: Listen Together (multi-device synchronized playback), Discord Rich Presence integration, and Last.fm scrobbling. It covers protocol specifications, message formats, synchronization mechanisms, network reliability, error handling, and privacy considerations.
Social features are implemented across three primary areas:
- Listen Together: WebSocket-based room orchestration, protobuf-encoded messages, and ExoPlayer integration for synchronized playback.
- Discord Rich Presence: A lightweight Discord Gateway client implementing RPC-like presence updates.
- Last.fm Scrobbling: A Last.fm client and manager that submits listening history and metadata.
graph TB
subgraph "Listen Together"
LTM["ListenTogetherManager"]
LTC["ListenTogetherClient"]
MC["MessageCodec"]
PR["Protocol Models"]
LS["ListenTogetherServers"]
end
subgraph "Discord"
DM["DiscordManager"]
DRPC["DiscordRPC"]
RE["RpcEntities"]
end
subgraph "Last.fm"
LFMM["LastFmManager"]
LFMRepo["LastFmRepository"]
LFMClient["LastFmClient"]
SM["SessionManager"]
end
LTM --> LTC
LTC --> MC
MC --> PR
LTC --> LS
DM --> DRPC
DRPC --> RE
LFMM --> LFMRepo
LFMRepo --> LFMClient
LFMM --> SM
Diagram sources
- ListenTogetherManager.kt:1-828
- ListenTogetherClient.kt:1-1205
- MessageCodec.kt:1-355
- Protocol.kt:1-320
- ListenTogetherServers.kt:1-42
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- RpcEntities.kt:1-179
- LastFmManager.kt:1-190
- LastFmRepository.kt:1-43
- LastFmClient.kt:1-230
- SessionManager.kt:1-2416
Section sources
- ListenTogetherManager.kt:1-828
- ListenTogetherClient.kt:1-1205
- DiscordManager.kt:1-161
- LastFmManager.kt:1-190
- Listen Together Manager: Bridges the WebSocket client with the ExoPlayer, orchestrates synchronization, drift correction, and buffering.
- Listen Together Client: WebSocket client that manages rooms, user roles, and message routing; persists sessions and handles reconnections.
- Protocol Models: Strongly typed message types, payloads, and enums for playback actions and room state.
- Message Codec: Encodes/decodes protobuf envelopes with optional compression.
- Discord Manager: Orchestrates Discord RPC lifecycle, settings, and presence updates.
- Discord RPC: Implements Discord Gateway protocol (identify, heartbeat, presence update).
- Last.fm Manager: Monitors playback and submits scrobbles and “now playing” updates.
- Last.fm Client/Repository: Performs authenticated HTTP requests to Last.fm API and manages session keys.
Section sources
- ListenTogetherManager.kt:1-828
- ListenTogetherClient.kt:1-1205
- Protocol.kt:1-320
- MessageCodec.kt:1-355
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- LastFmManager.kt:1-190
- LastFmClient.kt:1-230
- LastFmRepository.kt:1-43
The social features are layered:
- Presentation/UI: Settings screens for Discord and Last.fm.
- Domain/Data: SessionManager stores credentials and preferences.
- Feature Modules:
- Listen Together: Client and Manager coordinate with ExoPlayer and protobuf messages.
- Discord: Manager delegates to RPC client for presence updates.
- Last.fm: Manager triggers HTTP requests via repository/client.
sequenceDiagram
participant UI as "Settings Screens"
participant DM as "DiscordManager"
participant DRPC as "DiscordRPC"
participant LFMM as "LastFmManager"
participant LFMRepo as "LastFmRepository"
participant LFMClient as "LastFmClient"
UI->>DM : Update settings/token
DM->>DRPC : Connect/update presence
DRPC-->>DM : Connection status
UI->>LFMM : Toggle scrobbling
LFMM->>LFMRepo : updateNowPlaying/scrobble
LFMRepo->>LFMClient : HTTP requests
LFMClient-->>LFMRepo : Results
LFMRepo-->>LFMM : Results
Diagram sources
- DiscordSettingsScreen.kt:1-421
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- LastFmSettingsScreen.kt:1-387
- LastFmManager.kt:1-190
- LastFmRepository.kt:1-43
- LastFmClient.kt:1-230
- Message Types and Actions: Enumerated message types and playback actions define the protocol surface.
- Room State Model: Tracks host, users, current track, play state, position, volume, and queue.
- Client Responsibilities:
- Connect/reconnect with exponential backoff and jitter.
- Persist session tokens and room metadata for graceful rejoin.
- Manage notifications for join requests and suggestions.
- Route decoded payloads to events and update room state.
- Manager Responsibilities:
- Bridge ExoPlayer events to playback actions.
- Apply remote actions with drift correction and buffering.
- Handle track changes, queue operations, and volume sync.
- Enforce privacy mode and host/guest roles.
sequenceDiagram
participant Host as "Host Device"
participant Client as "ListenTogetherClient"
participant Codec as "MessageCodec"
participant Server as "Listen Together Server"
participant Guest as "Guest Device"
participant Manager as "ListenTogetherManager"
Host->>Client : Player event (play/pause/seek/change)
Client->>Codec : Encode payload
Codec-->>Client : Bytes
Client->>Server : SEND PLAYBACK_ACTION
Server-->>Guest : BROADCAST SYNC_PLAYBACK
Guest->>Manager : Handle event
Manager->>Manager : Apply state (seek/play/pause)
Manager->>Manager : Drift correction
Manager->>Client : sendBufferReady (if buffering)
Guest->>Client : BUFFER_READY
Server-->>Host : BUFFER_WAIT/BUFFER_COMPLETE
Host->>Manager : Handle buffer complete
Manager->>Manager : Start playback with anchors
Diagram sources
- ListenTogetherClient.kt:704-1020
- MessageCodec.kt:30-84
- ListenTogetherManager.kt:418-556
- Protocol.kt:9-66
Section sources
- Protocol.kt:1-320
- ListenTogetherClient.kt:1-1205
- ListenTogetherManager.kt:1-828
- MessageCodec.kt:1-355
- ListenTogetherEvent.kt:1-35
- ListenTogetherServers.kt:1-42
- Manager:
- Initializes RPC client, applies settings, and monitors privacy mode.
- Updates presence with title/artist, image URL, and timestamps.
- RPC Client:
- Implements Discord Gateway handshake, heartbeat, and presence updates.
- Supports reconnection and resume flows.
- UI:
- Settings screen allows token entry, enabling/disabling presence, and preview.
sequenceDiagram
participant UI as "DiscordSettingsScreen"
participant DM as "DiscordManager"
participant DRPC as "DiscordRPC"
UI->>DM : setDiscordToken/setDiscordRpcEnabled
DM->>DRPC : connect/close
DM->>DRPC : updateActivity(title, artist, image, timestamps)
DRPC-->>DM : Connection status/state
Diagram sources
- DiscordSettingsScreen.kt:1-421
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- RpcEntities.kt:1-179
Section sources
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- RpcEntities.kt:1-179
- DiscordSettingsScreen.kt:1-421
- Manager:
- Listens to ExoPlayer events, extracts metadata, and schedules scrobble timing.
- Submits “now playing” and scrobble events via repository/client.
- Repository/Client:
- Performs authenticated requests to Last.fm API using API key/shared secret.
- Supports session creation, scrobble, and “now playing” updates.
- Session Management:
- Stores Last.fm session key and user preferences securely.
sequenceDiagram
participant Player as "ExoPlayer"
participant LFMM as "LastFmManager"
participant LFMRepo as "LastFmRepository"
participant LFMClient as "LastFmClient"
participant SM as "SessionManager"
Player->>LFMM : onMediaItemTransition/onIsPlayingChanged
LFMM->>SM : Read scrobble settings/session
LFMM->>LFMRepo : updateNowPlaying (optional)
LFMRepo->>LFMClient : HTTP request
LFMM->>LFMM : Monitor playback progress
LFMM->>LFMRepo : scrobble (when threshold met)
LFMRepo->>LFMClient : HTTP request
Diagram sources
- LastFmManager.kt:1-190
- LastFmRepository.kt:1-43
- LastFmClient.kt:1-230
- SessionManager.kt:494-605
Section sources
- LastFmManager.kt:1-190
- LastFmRepository.kt:1-43
- LastFmClient.kt:1-230
- SessionManager.kt:494-605
- LastFmSettingsScreen.kt:1-387
- Listen Together depends on:
- ExoPlayer for playback state and media items.
- OkHttp WebSocket for transport.
- Protobuf codec for message framing and compression.
- DataStore for persisted session and preferences.
- Discord depends on:
- Ktor WebSockets for Gateway connectivity.
- UI settings for token management.
- Last.fm depends on:
- HTTP client for API calls.
- SessionManager for secure credential storage.
graph LR
LTM["ListenTogetherManager"] --> LTC["ListenTogetherClient"]
LTC --> MC["MessageCodec"]
MC --> PR["Protocol Models"]
LTC --> OK["OkHttp WebSocket"]
DM["DiscordManager"] --> DRPC["DiscordRPC"]
DRPC --> Ktor["Ktor WebSockets"]
LFMM["LastFmManager"] --> LFMRepo["LastFmRepository"]
LFMRepo --> LFMClient["LastFmClient"]
LFMM --> SM["SessionManager"]
Diagram sources
- ListenTogetherManager.kt:1-828
- ListenTogetherClient.kt:1-1205
- MessageCodec.kt:1-355
- Protocol.kt:1-320
- DiscordManager.kt:1-161
- DiscordRPC.kt:1-352
- LastFmManager.kt:1-190
- LastFmRepository.kt:1-43
- LastFmClient.kt:1-230
- SessionManager.kt:1-2416
Section sources
- ListenTogetherClient.kt:320-326
- DiscordRPC.kt:44-48
- LastFmClient.kt:34-43
- Compression: Protobuf payloads are compressed when exceeding a threshold to reduce bandwidth.
- Backoff and Heartbeats: Exponential backoff with jitter prevents thundering herds; periodic pings maintain liveness.
- Drift Correction: Continuous small adjustments and hard seeks minimize long-term drift during synchronized playback.
- Resource Throttling: Last.fm scrobbling checks are suspended when paused and scheduled at intervals to reduce CPU usage.
[No sources needed since this section provides general guidance]
- Listen Together
- Reconnection Loops: Exponential backoff with max attempts prevents infinite retries; inspect logs for failure reasons.
- Session Persistence: Sessions are saved for a grace period; expired sessions are cleared automatically.
- Privacy Mode: Rooms are left when privacy mode is enabled.
- Buffering: Buffering waits for all guests; timeouts force-play if needed.
- Discord
- Authentication Failures: Specific close codes halt reconnection; verify token validity.
- Presence Updates: Ensure settings are enabled and not in privacy mode.
- Last.fm
- Scrobble Timing: Adjust minimum duration and scrobble percentage to suit user preference.
- Session Keys: Stored securely; clear and re-authenticate if issues persist.
Section sources
- ListenTogetherClient.kt:652-702
- ListenTogetherClient.kt:1190-1203
- DiscordRPC.kt:114-132
- LastFmManager.kt:116-164
SuvMusic’s social features combine robust networking (WebSocket + protobuf), precise playback synchronization, and user-friendly integrations with Discord and Last.fm. The architecture emphasizes reliability, privacy-aware behavior, and extensible protocols suitable for future enhancements.