Rocksky.RemotePlayer (Rocksky v0.9.0)

Copy Markdown View Source

Build a Rocksky-controllable player over the remote-control WebSocket (see remote-ws/PROTOCOL.md).

connect/2,3 registers as a device in the background; you advertise what you're playing (set_now_playing/2, set_status/2, set_queue/3) and react to commands a miniplayer sends (play/pause/next/previous/seek/enqueue/queue actions). Heartbeat, reconnect, and the device-id handshake are handled by the native core.

Poll next_command/1 in a loop, or let listen/2 do it for you and dispatch each command to a handler map:

player = Rocksky.RemotePlayer.connect(token, "My Player")

Rocksky.RemotePlayer.listen(player, %{
  play: fn -> engine_play() end,
  pause: fn -> engine_pause() end,
  next: fn -> engine_next() end,
  seek: fn ms -> engine_seek(ms) end,
  enqueue: fn cmd -> engine_enqueue(cmd["tracks"], cmd["mode"]) end
})

Rocksky.RemotePlayer.set_now_playing(player, %{
  "title" => "Chaser", "artist" => "Calibro 35",
  "durationMs" => 182_320, "elapsedMs" => 0, "isPlaying" => true
})
Rocksky.RemotePlayer.set_status(player, "playing")

The handle is an opaque NIF resource (freed by GC); disconnect/1 just stops the background task. Setters return {:ok, value} | {:error, message}. Records/queue items are maps with camelCase string keys.

Summary

Functions

Connect and register a controllable player. name is the miniplayer device-picker label; url overrides the WebSocket endpoint (nil = public). Returns the opaque player handle.

Disconnect and stop the background task (the handle stays valid until GC'd).

Spawn a process that loops next_command/1 and dispatches each command to the matching function in handlers. Returns {:ok, pid}. The loop ends when the player disconnects.

Block until the next controller command, returned as a decoded command map (e.g. %{"action" => "play"} or %{"action" => "seek", "position" => ms}). Returns nil once the player is disconnected.

Advertise the currently-playing track. track is a map with camelCase string keys: "title", "artist", and optional "album", "albumArtist", "albumArt", "durationMs", "elapsedMs", "isPlaying". Call it whenever the track changes, and periodically with a fresh "elapsedMs" so controllers show smooth progress.

Advertise the playback queue + active index. items is a list of queue-item maps (camelCase string keys); index is 0-based.

Advertise transport status: "playing", "paused", or "stopped".

Functions

connect(token, name, url \\ nil)

Connect and register a controllable player. name is the miniplayer device-picker label; url overrides the WebSocket endpoint (nil = public). Returns the opaque player handle.

disconnect(handle)

Disconnect and stop the background task (the handle stays valid until GC'd).

listen(handle, handlers)

Spawn a process that loops next_command/1 and dispatches each command to the matching function in handlers. Returns {:ok, pid}. The loop ends when the player disconnects.

handlers is a map keyed by command:

  • :play, :pause, :next, :previous — 0-arity functions
  • :seek — receives the position in ms
  • :queue_jump, :queue_remove — receive the queue index
  • :enqueue — receives the full command map ("tracks", "mode", "shuffle", "startIndex")

Unhandled commands are ignored.

next_command(handle)

Block until the next controller command, returned as a decoded command map (e.g. %{"action" => "play"} or %{"action" => "seek", "position" => ms}). Returns nil once the player is disconnected.

set_now_playing(handle, track)

Advertise the currently-playing track. track is a map with camelCase string keys: "title", "artist", and optional "album", "albumArtist", "albumArt", "durationMs", "elapsedMs", "isPlaying". Call it whenever the track changes, and periodically with a fresh "elapsedMs" so controllers show smooth progress.

set_queue(handle, items, index)

Advertise the playback queue + active index. items is a list of queue-item maps (camelCase string keys); index is 0-based.

set_status(handle, status)

Advertise transport status: "playing", "paused", or "stopped".