connect

package
v1.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 3, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func Discover(timeout time.Duration, selfPort int) ([]Device, error)

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

func (h *Host) Addr() string

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

func (h *Host) Start() error

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) Stop

func (rc *RemoteClient) Stop() (State, error)

Stop halts playback.

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(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 Track

type Track = internalcontrol.Track

Track is a now-playing or queue entry in a State.

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL