Documentation
¶
Overview ¶
Package connect implements OpenDeezer Connect: LAN discovery and remote control of OpenDeezer devices, in both directions.
OpenDeezer Connect is symmetric — this package exposes both sides:
- out — discover and control other devices: Discover + RemoteClient
- in — be discoverable and controllable: Host (and the lower-level Advertise if you manage your own control server)
Transport: IPv4 UDP multicast (group 239.255.42.99, port 7655) with a directed-broadcast fallback so it works on networks that filter multicast. Only LAN/loopback sources are answered; no credentials are exchanged during discovery.
Out: discover devices ¶
devices, err := connect.Discover(2*time.Second, 0)
for _, d := range devices {
fmt.Printf("%s at %s (client: %s)\n", d.Name, d.Addr, d.Client)
}
Out: drive a device ¶
rc := connect.NewRemoteClient(devices[0].Addr, token, "")
st, _ := rc.PlayPause()
fmt.Println("state:", st.State)
In: be a controllable device ¶
Host ties a control endpoint together with LAN advertising, so this process becomes discoverable and accepts the same commands a RemoteClient sends:
host := connect.NewHost(
connect.HostConfig{
Control: connect.Config{Addr: ":7654", SameAccountOnly: true},
Name: "My Player", Client: "myapp", Version: "1.0",
},
func() connect.State { return snapshot() },
func() connect.Account { return connect.Account{UserID: uid, Name: name} },
connect.Commands{PlayPause: player.TogglePause, Stop: player.Stop},
dzClient, // browse routes; nil to disable
)
host.Start()
defer host.Close()
In: advertise only (custom control server) ¶
resp, _ := connect.Advertise(func() connect.AdvertiseInfo {
return connect.AdvertiseInfo{Name: "My Player", Client: "myapp", Version: "1.0"}
}, controlPort)
defer resp.Close()
Index ¶
- Variables
- type Account
- type AdvertiseInfo
- type Commands
- type Config
- type Device
- type Host
- type HostConfig
- type RemoteClient
- func (rc *RemoteClient) Next() (State, error)
- func (rc *RemoteClient) PlayPause() (State, error)
- func (rc *RemoteClient) PlayPlaylist(id string) (State, error)
- func (rc *RemoteClient) PlayTrack(id string) (State, error)
- func (rc *RemoteClient) Prev() (State, error)
- func (rc *RemoteClient) Restart() (State, error)
- func (rc *RemoteClient) SeekMS(ms int64) (State, error)
- func (rc *RemoteClient) SetRepeat(mode string) (State, error)
- func (rc *RemoteClient) SetShuffle(on bool) (State, error)
- func (rc *RemoteClient) SetVolume(v float64) (State, error)
- func (rc *RemoteClient) Status() (State, error)
- func (rc *RemoteClient) Stop() (State, error)
- func (rc *RemoteClient) Whoami() (Whoami, error)
- type Responder
- type State
- type Track
- type Whoami
Constants ¶
This section is empty.
Variables ¶
var ErrAdvertise = errors.New("connect: control endpoint is serving but LAN advertising failed")
ErrAdvertise is wrapped into the error returned by Host.Start when the control endpoint bound successfully but LAN advertising failed. The endpoint is left serving (reachable by address); callers must still call Host.Close.
Functions ¶
This section is empty.
Types ¶
type Account ¶
type Account = internalcontrol.Account
Account is this device's Deezer identity snapshot, provided to NewHost. It is the same type as control.Account.
type AdvertiseInfo ¶
type AdvertiseInfo = internaldiscovery.Info
AdvertiseInfo is the identity advertised to probers. The function you pass to Advertise returns one of these on each probe so changes (e.g. a re-login updating the display name) are reflected immediately.
type Commands ¶
type Commands = internalcontrol.Commands
Commands are the playback actions an inbound controller can dispatch to this device via a Host. It is the same type as control.Commands.
type Config ¶
type Config = internalcontrol.Config
Config configures the control endpoint a Host exposes (the inbound side). It is the same type as control.Config.
type Device ¶
type Device = internaldiscovery.Device
Device is a discovered OpenDeezer instance.
- Name — the account's display name
- Addr — control API address (host:port) to pass to NewRemoteClient
- Client — client/platform id (e.g. "tui", "macos", "gnome")
- Version — OpenDeezer version
func Discover ¶
Discover multicasts a discovery probe and returns all OpenDeezer devices that reply within timeout.
selfPort is this device's own control port. Replies arriving from a local interface with that port are filtered out so you never list yourself. Pass 0 if you are not running a control server.
type Host ¶
type Host struct {
// contains filtered or unexported fields
}
Host is the inbound side of OpenDeezer Connect: a control endpoint other devices can drive, advertised on the LAN so Discover finds it. It is the mirror image of RemoteClient (which drives a host) and pairs with Advertise/Discover.
Construct one with NewHost, call Host.Start to bind and advertise, and Host.Close to stop. Host is safe for concurrent use once started.
func NewHost ¶
func NewHost( cfg HostConfig, status func() State, account func() Account, cmds Commands, dz *sdkdeezer.Client, ) *Host
NewHost builds a Connect host.
- cfg — control bind/auth + advertised identity
- status — playback snapshot provider (race-free reads)
- account — logged-in identity provider; its Name is preferred for the advertised name. It is also the same-account auth credential source: with Control.SameAccountOnly set, account must be non-nil or Host.Start fails (every request would 401). Pass nil only when SameAccountOnly is off, to advertise cfg.Name.
- cmds — the actions remote controllers can dispatch (same command set a RemoteClient sends)
- dz — Deezer client for the endpoint's browse routes; nil to disable
func (*Host) Addr ¶
Addr returns the control endpoint's listen address (valid after Host.Start).
func (*Host) Close ¶
func (h *Host) Close()
Close stops advertising and shuts down the control endpoint.
func (*Host) Server ¶
func (h *Host) Server() *sdkcontrol.Server
Server returns the underlying control server so callers can enable web-remote pairing (sdkcontrol.Server.EnablePairing), read the listen address, or override the device label.
func (*Host) Start ¶
Start binds the control endpoint and begins advertising it on the LAN via OpenDeezer Connect (UDP port 7655). The advertised control port is taken from the endpoint's actual listen address.
Advertising is only useful when the control endpoint binds a LAN-reachable (non-loopback) address; a loopback bind is advertised but reachable only on this machine.
When advertising fails, Start leaves the control endpoint serving (reachable by address) and returns an error wrapping ErrAdvertise; the caller must still call Host.Close to release it. Any other error means nothing is running.
type HostConfig ¶
type HostConfig struct {
// Control is the control-endpoint configuration (listen address and auth
// mode). For a LAN-reachable device, bind a non-loopback address such as
// ":7654" and set Token or SameAccountOnly.
Control Config
// Name is the advertised display name. When an account snapshot is provided
// to [NewHost] and reports a non-empty name, that name is used instead so a
// re-login is reflected automatically.
Name string
// Client is the advertised client/platform id (e.g. "myapp").
Client string
// Device is the human device label reported by [RemoteClient.Whoami]
// (e.g. "Kitchen Speaker"). Defaults to Client when empty.
Device string
// Version is the advertised OpenDeezer/app version.
Version string
}
HostConfig configures a Host: how the control endpoint binds and authenticates, plus the identity advertised over LAN discovery.
type RemoteClient ¶
type RemoteClient struct {
// contains filtered or unexported fields
}
RemoteClient drives a discovered OpenDeezer device via its HTTP/JSON control API. All mutation methods return the device's post-command playback snapshot.
Thread-safe.
func NewRemoteClient ¶
func NewRemoteClient(addr, token, accountID string) *RemoteClient
NewRemoteClient builds a remote-control client for the device at addr (host:port as returned by [Device.Addr]). Provide token or accountID depending on the device's auth mode (see RemoteClient.Whoami):
- token auth → token="<bearer>", accountID=""
- account auth → token="", accountID="<your Deezer user id>"
- none → token="", accountID=""
A device reporting "session" auth (WebRemote mode) is driveable only from its embedded browser SPA, which authenticates with a paired session token sent in the X-OpenDeezer-Session header (stored in the browser's localStorage, not a cookie); RemoteClient has no way to supply that credential and cannot control such a device.
func (*RemoteClient) Next ¶
func (rc *RemoteClient) Next() (State, error)
Next skips to the next track.
func (*RemoteClient) PlayPause ¶
func (rc *RemoteClient) PlayPause() (State, error)
PlayPause toggles play/pause on the device.
func (*RemoteClient) PlayPlaylist ¶
func (rc *RemoteClient) PlayPlaylist(id string) (State, error)
PlayPlaylist instructs the device to play the playlist with the given id.
func (*RemoteClient) PlayTrack ¶
func (rc *RemoteClient) PlayTrack(id string) (State, error)
PlayTrack instructs the device to play the track with the given Deezer id.
func (*RemoteClient) Prev ¶
func (rc *RemoteClient) Prev() (State, error)
Prev jumps to the previous track.
func (*RemoteClient) Restart ¶
func (rc *RemoteClient) Restart() (State, error)
Restart seeks to position 0 in the current track.
func (*RemoteClient) SeekMS ¶
func (rc *RemoteClient) SeekMS(ms int64) (State, error)
SeekMS seeks to ms milliseconds from the start of the current track.
func (*RemoteClient) SetRepeat ¶
func (rc *RemoteClient) SetRepeat(mode string) (State, error)
SetRepeat sets the repeat mode: "off", "all", or "one".
func (*RemoteClient) SetShuffle ¶
func (rc *RemoteClient) SetShuffle(on bool) (State, error)
SetShuffle enables (true) or disables (false) shuffle on the device.
func (*RemoteClient) SetVolume ¶
func (rc *RemoteClient) SetVolume(v float64) (State, error)
SetVolume sets the volume on the device (0.0 = silent, 1.0 = full).
func (*RemoteClient) Status ¶
func (rc *RemoteClient) Status() (State, error)
Status returns the device's current playback snapshot.
func (*RemoteClient) Whoami ¶
func (rc *RemoteClient) Whoami() (Whoami, error)
Whoami fetches the device's identity and auth mode. This endpoint is unauthenticated and can be called before supplying credentials.
type Responder ¶
type Responder = internaldiscovery.Responder
Responder is a running OpenDeezer Connect discovery responder. Stop it with Close.
func Advertise ¶
func Advertise(info func() AdvertiseInfo, controlPort int) (*Responder, error)
Advertise starts an OpenDeezer Connect responder on UDP port 7655. info is called on each incoming probe to get the current display name and version. controlPort is the TCP port of the local control server that controllers should connect to.
Call Close on the returned Responder to stop advertising.
type State ¶
type State = internalcontrol.State
State is the playback snapshot returned by RemoteClient.Status and all mutation methods.
type Whoami ¶
type Whoami = internalcontrol.Whoami
Whoami holds a device's identity and the auth mode it requires.
- Name — account display name (not the user id)
- Offer — plan name (e.g. "Deezer Premium")
- Auth — "token" | "account" | "session" | "none"
- Client — client/platform id
- Device — human device label