Documentation
¶
Overview ¶
Package control exposes playback control + status over a small HTTP/JSON API. It is the shared foundation for remote control (one OpenDeezer client driving another) and the MCP server (an AI agent driving playback). A frontend wires it like the MPRIS bridge: provide a status snapshot func + a set of command callbacks, plus the Deezer client for read-only browse (search/playlists).
Auth has three modes, picked by Config. Credentials are accepted via request HEADERS only (never the query string, which leaks into logs/history):
- Token: a bearer token in "X-OpenDeezer-Token". Strongest.
- Same-account: no token, but a controller must prove it is logged into the SAME Deezer account by sending its OWN Deezer user id in "X-OpenDeezer-Account". A controller learns that id from its own login, not from this server — /whoami deliberately does NOT echo the user id, so a bystander can't read the credential and replay it. Convenience auth for a trusted LAN: the user copies no token; their own devices just connect. The user id is only semi-private, so this is LAN-trust grade, not a secret.
- Session (web remote): a phone pairs with a 6-digit code minted at enable time; on success it receives a short-lived session token sent as X-OpenDeezer-Session. CSRF-safe because the token lives in localStorage (not a cookie) and custom headers cannot be set cross-origin.
- None: open (only safe bound to localhost).
Mutating endpoints require POST and reject requests carrying a browser Origin header, so a web page the user happens to visit can't drive playback (CSRF). The exception is requests that also carry a valid X-OpenDeezer-Session token: those come from our own SPA (same origin in the browser), so they are allowed. GET /whoami is unauthenticated so a controller can discover the account NAME (not id) and auth mode of a server before connecting.
Index ¶
- type Account
- type Client
- func (c *Client) CancelSleepTimer() (State, error)
- func (c *Client) CycleRepeat() (State, error)
- func (c *Client) EQ() (EQState, error)
- func (c *Client) Next() (State, error)
- func (c *Client) PlayPause() (State, error)
- func (c *Client) PlayPlaylist(id string) (State, error)
- func (c *Client) PlayTrack(id string) (State, error)
- func (c *Client) Playlists() (json.RawMessage, error)
- func (c *Client) Prev() (State, error)
- func (c *Client) Restart() (State, error)
- func (c *Client) Search(q string) (json.RawMessage, error)
- func (c *Client) Seek(ms int64) (State, error)
- func (c *Client) SetEQ(params url.Values) (EQState, error)
- func (c *Client) SetRepeat(mode string) (State, error)
- func (c *Client) SetShuffle(on bool) (State, error)
- func (c *Client) SetSleepTimer(minutes int, endOfTrack bool) (State, error)
- func (c *Client) SetVolume(v float64) (State, error)
- func (c *Client) Status() (State, error)
- func (c *Client) Stop() (State, error)
- func (c *Client) ToggleShuffle() (State, error)
- func (c *Client) Whoami() (Whoami, error)
- type Commands
- type Config
- type EQ
- type EQController
- type EQState
- type Server
- func (s *Server) Addr() string
- func (s *Server) Close()
- func (s *Server) DisablePairing()
- func (s *Server) EnablePairing() string
- func (s *Server) PairingActive() bool
- func (s *Server) PairingCode() string
- func (s *Server) SetClientInfo(client, device string)
- func (s *Server) SetEQ(eq *EQ)
- func (s *Server) SetVersion(v string)
- func (s *Server) Start() error
- type State
- type Track
- type Whoami
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Account ¶
Account is the controlled client's Deezer identity, supplied by a snapshot provider so the HTTP goroutine never reads the deezer.Client's login fields directly (those are written by Login on another goroutine).
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client talks to a control Server over HTTP. It is the shared driver for the MCP server and the TUI's remote-play feature: both point it at another OpenDeezer client's control API and issue the same commands a local user would.
func NewClient ¶
NewClient builds a control client. base is the server's URL; token/account are the credentials (send whichever the server requires; empty ones are omitted).
func (*Client) CancelSleepTimer ¶ added in v1.6.0
CancelSleepTimer disarms the peer's sleep timer.
func (*Client) CycleRepeat ¶
func (*Client) EQ ¶ added in v1.7.0
EQ returns the peer's equalizer snapshot (enabled, mono downmix, preamp, per-band gains, active preset and the preset/band lists).
func (*Client) Playlists ¶
func (c *Client) Playlists() (json.RawMessage, error)
Playlists returns the raw playlists JSON from the server.
func (*Client) Search ¶
func (c *Client) Search(q string) (json.RawMessage, error)
Search returns the raw search-results JSON from the server.
func (*Client) SetEQ ¶ added in v1.7.0
SetEQ applies a partial equalizer update. params carries any of the POST /eq query params — on=0|1, mono=0|1, preset=name, band=N&db=X, preamp=dB — and the server applies whichever are present (at least one is required).
func (*Client) SetRepeat ¶ added in v1.0.1
SetRepeat sets the repeat mode on the peer to mode ("off", "all", or "one").
func (*Client) SetShuffle ¶ added in v1.0.1
SetShuffle sets shuffle on (true) or off (false) on the peer.
func (*Client) SetSleepTimer ¶ added in v1.6.0
SetSleepTimer arms the peer's sleep timer: pause after `minutes` (with a fade-out), or when the current track ends if endOfTrack is true.
func (*Client) ToggleShuffle ¶
type Commands ¶
type Commands struct {
PlayPause func()
Next func()
Prev func()
Stop func()
Restart func() // seek to 0
CycleRepeat func()
ToggleShuffle func()
SetRepeat func(mode string) // mode: "off"|"all"|"one" (SET variant)
SetShuffle func(on bool) // on: true/false (SET variant)
Seek func(ms int64)
SetVolume func(v float64)
PlayTrack func(id string)
PlayPlaylist func(id string)
SetSleepTimer func(minutes int, endOfTrack bool) // arm the sleep timer
CancelSleepTimer func() // disarm it
}
Commands are the mutating actions a controller exposes (each may be nil).
type Config ¶
type Config struct {
Addr string // host:port ("127.0.0.1:7654" localhost, ":7654" LAN)
Token string // bearer token; "" disables token auth
SameAccountOnly bool // when Token=="", require a matching Deezer account id
WebRemote bool // allow LAN bind with session (pairing) as the sole auth
}
Config configures the control server.
type EQ ¶ added in v1.7.0
type EQ struct {
State func() EQState
SetEnabled func(on bool)
SetMono func(on bool)
SetPreamp func(db float64)
SetPreset func(name string) error
SetBand func(band int, db float64) error
}
EQ is the optional equalizer bridge; when nil the /eq endpoints 404. Hosts wire the funcs to the audio player (each may be nil individually).
func PlayerEQ ¶ added in v1.7.0
func PlayerEQ(get func() EQController, presets []string) *EQ
PlayerEQ builds the /eq bridge from a live player getter (get may return nil while the engine is starting) and the core's preset-name list.
type EQController ¶ added in v1.7.0
type EQController interface {
EQEnabled() bool
MonoDownmix() bool
EQPreampDB() float64
EQGains() []float64
EQPreset() string
EQBands() []float64
SetEQEnabled(on bool)
SetMonoDownmix(on bool)
SetEQPreamp(db float64)
SetEQPreset(name string) error
SetEQGain(band int, db float64) error
}
EQController is the player subset the /eq endpoint drives; *audio.Player satisfies it (declared here so this package needn't import the engine).
type EQState ¶ added in v1.7.0
type EQState struct {
Enabled bool `json:"enabled"`
Mono bool `json:"mono"`
PreampDB float64 `json:"preampDb"`
GainsDB []float64 `json:"gainsDb"`
Preset string `json:"preset"`
Bands []float64 `json:"bands"`
Presets []string `json:"presets"`
}
EQState is the equalizer snapshot returned by GET /eq (same wire shape as corelib's DZEQJSON / odmobile's EQJSON, so every controller renders one UI).
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server serves the control API.
func New ¶
func New(cfg Config, status func() State, account func() Account, cmds Commands, client *deezer.Client) *Server
New builds a control server from cfg. status + account are snapshot providers (called from the HTTP goroutine, so they must be race-free reads); client supplies the browse endpoints (search/playlists).
func (*Server) DisablePairing ¶ added in v1.0.1
func (s *Server) DisablePairing()
DisablePairing clears the pairing code so no new phones can pair. Existing valid session tokens remain usable for their remaining TTL.
func (*Server) EnablePairing ¶ added in v1.0.1
EnablePairing mints a fresh 6-digit code, activates pairing, and returns the code. Safe to call multiple times; each call resets the code.
func (*Server) PairingActive ¶ added in v1.0.1
PairingActive reports whether pairing is currently enabled.
func (*Server) PairingCode ¶ added in v1.0.1
PairingCode returns the current 6-digit code (empty when not active).
func (*Server) SetClientInfo ¶
SetClientInfo records the client/platform id + device label for /whoami.
func (*Server) SetVersion ¶
SetVersion records the app version reported by /whoami.
type State ¶
type State struct {
State string `json:"state"` // playing | paused | stopped | loading | error
Track *Track `json:"track,omitempty"`
PositionMS int64 `json:"positionMs"`
DurationMS int64 `json:"durationMs"`
Volume float64 `json:"volume"` // 0..1
Repeat string `json:"repeat"` // off | all | one
Shuffle bool `json:"shuffle"`
Format string `json:"format,omitempty"`
Queue []Track `json:"queue,omitempty"`
// Sleep timer (0/false when not armed).
SleepActive bool `json:"sleepActive,omitempty"`
SleepEndOfTrack bool `json:"sleepEndOfTrack,omitempty"`
SleepRemainingMS int64 `json:"sleepRemainingMs,omitempty"`
}
State is the playback snapshot returned by GET /status.
type Track ¶
type Track struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
ArtistID string `json:"artistId,omitempty"`
Album string `json:"album"`
Explicit bool `json:"explicit"`
DurationMS int64 `json:"durationMs"`
ArtworkURL string `json:"artworkUrl,omitempty"`
}
Track is a now-playing / queue entry in the API.
type Whoami ¶
type Whoami struct {
Name string `json:"name"`
Offer string `json:"offer,omitempty"`
Auth string `json:"auth"` // token | account | session | none
Version string `json:"version,omitempty"` // OpenDeezer version
Client string `json:"client,omitempty"` // client/platform id (tui, macos, gnome…)
Device string `json:"device,omitempty"` // human device label ("OpenDeezer TUI")
}
Whoami is the unauthenticated identity returned by GET /whoami. It carries the account display NAME (for the controller to recognise its own device) but never the user id: in same-account mode that id IS the credential, so echoing it here would let any bystander read and replay it.