Design Spotify Music Streaming
Design the catalogue and playback components of a simplified Spotify-like music streaming service. The system must allow songs to be added, updated, searched, and played while maintaining an independent playback session for every user.
All users already exist. Their user IDs are provided when the service is created. Input parameters passed to the constructor and methods are always valid.
Song Format
Each returned song is represented as:
"songId,title,artist,album,durationSeconds"
Playback State Format
Each playback state is represented as:
"status,songId,positionSeconds"
status is PLAYING, PAUSED, or STOPPED.
- When no song is selected, return
"STOPPED,NO_SONG,0".
Class
SpotifyMusicService
Constructor
SpotifyMusicService(List<String> userIds)
- Creates an empty music catalogue.
- Every value in
userIds represents an existing user.
- Each user initially has an empty playback session.
Method Signatures
Add or Update Song
void addOrUpdateSong(String songId, String title, String artist, String album, int durationSeconds)
- Adds the song to the global music catalogue when
songId does not already exist.
- When
songId already exists, replaces its title, artist, album, and duration with the supplied values.
- The method does not return a value.
- Updating a song does not reset or remove any user's current playback session.
- A user currently playing the updated song observes its updated catalogue details.
Search Songs
List<String> searchSongs(String query)
- Performs a case-insensitive substring search over song titles, artists, and albums.
- Returns each matching song using the specified song format.
- Returns matching songs in lexicographically increasing order of
songId.
- Returns an empty list when no song matches.
Play Song
String playSong(String userId, String songId)
- Starts playing the given song for the user from position
0.
- Replaces any song currently selected in the user's playback session.
- Once a user starts playing a song, its duration for that playback session remains unchanged even if the song's catalogue details are later updated.
- A user currently playing the updated song observes its updated title, artist, and album, but the duration stored in that playback session remains unchanged.
- Starting or restarting a song creates a new playback session using the song's current catalogue duration.
- Returns
"PLAYING,songId,0".
Control Playback
String controlPlayback(String userId, String action, int positionSeconds)
action is "PAUSE", "RESUME", "SEEK", or "STOP".
"PAUSE" changes a playing song to PAUSED.
"RESUME" changes a paused song to PLAYING.
"SEEK" moves playback to positionSeconds without changing the current playback status.
"STOP" clears the user's current playback session.
- For actions other than
"SEEK", positionSeconds is 0.
- For
"SEEK", 0 ≤ positionSeconds < durationSeconds.
- The playback session is always in a valid state for the supplied action.
- Returns the resulting playback state.
Rules
- Search is case-insensitive.
- Search results are sorted by
songId.
- Every user has an independent playback session.
- Starting another song replaces the user's current song.
- Playback positions do not advance automatically between method calls.
- Updating a song preserves its
songId.
- All user IDs and song IDs contain only lowercase English letters, digits, and hyphens.
- The value
NO_SONG is a special playback marker and is not a song ID.
- All constructor and method parameters satisfy the stated constraints.
Design Expectations
- Use separate components for the music catalogue and user playback sessions.
- Updating catalogue metadata should not require recreating playback sessions.
- Each user's playback state must be stored and modified independently.
- Catalogue and playback operations should safely support calls from multiple users.
Constraints
1 ≤ userIds.size() ≤ 100,000
1 ≤ number of songs ≤ 100,000
1 ≤ userId.length(), songId.length() ≤ 50
1 ≤ title.length(), artist.length(), album.length() ≤ 100
1 ≤ durationSeconds ≤ 86,400
1 ≤ query.length() ≤ 100
userId and songId contain only a-z, 0-9, and -.
- All user IDs passed to the constructor are unique.
- All string parameters are non-empty.
- Song titles, artist names, and album names do not contain commas.
- All input parameters are valid.
Examples
Example 1: Add, Update, and Search Songs
SpotifyMusicService service = new SpotifyMusicService(userIds = ["user-1", "user-2"])
service.addOrUpdateSong(songId = "song-20", title = "Silent Road", artist = "Mira Sen", album = "Journeys", durationSeconds = 210)
Output: no value
service.addOrUpdateSong(songId = "song-05", title = "Road to Dawn", artist = "Kian Roy", album = "First Light", durationSeconds = 195)
Output: no value
service.addOrUpdateSong(songId = "song-20", title = "Silent Highway", artist = "Mira Sen", album = "New Journeys", durationSeconds = 225)
Output: no value
service.searchSongs(query = "road")
Output: ["song-05,Road to Dawn,Kian Roy,First Light,195"]
Updating song-20 replaces its old details, so its previous title no longer matches "road".
Example 2: Play and Pause a Song
service.playSong(userId = "user-1", songId = "song-20")
Output: "PLAYING,song-20,0"
service.controlPlayback(userId = "user-1", action = "SEEK", positionSeconds = 45)
Output: "PLAYING,song-20,45"
service.controlPlayback(userId = "user-1", action = "PAUSE", positionSeconds = 0)
Output: "PAUSED,song-20,45"
service.controlPlayback(userId = "user-1", action = "RESUME", positionSeconds = 0)
Output: "PLAYING,song-20,45"
Example 3: Independent Playback Sessions
service.playSong(userId = "user-2", songId = "song-05")
Output: "PLAYING,song-05,0"
service.controlPlayback(userId = "user-1", action = "STOP", positionSeconds = 0)
Output: "STOPPED,NO_SONG,0"
Stopping playback for user-1 does not affect the song being played by user-2.