### Usage example: login and get token Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/login5.md Shows how to create a Login5 client, authenticate with stored credentials, and retrieve an access token. Requires a logger, HTTP client, device ID, and client token. ```go import "github.com/devgianlu/go-librespot/login5" import credentialspb "github.com/devgianlu/go-librespot/proto/spotify/login5/v3/credentials" c := login5.NewLogin5(logger, client, deviceId, clientToken) if err := c.Login(ctx, &credentialspb.StoredCredential{Username: user, Data: cred}); err != nil { return fmt.Errorf("login5 failed: %w", err) } fmt.Println("token:", c.AccessToken()(ctx, false)) ``` -------------------------------- ### Usage Examples Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/librespot.md Examples of using the librespot package, including SpotifyIdFromUri and error handling with HTTPStatusError. ```APIDOC ## Usage Examples ### Description Examples of using the librespot package, including SpotifyIdFromUri and error handling with HTTPStatusError. ### Import ```go import librespot "github.com/devgianlu/go-librespot" ``` ### SpotifyIdFromUri ```go id, err := librespot.SpotifyIdFromUri("spotify:track:4iObEfGnTmVIBMcuDowJ0") if err != nil { return err } fmt.Println(id.Uri(), id.Base62(), id.Hex()) ``` ### Other Functions - `librespot.NewMediaFromTrack(...)` - Creates a media object from a track. - `librespot.ObfuscateUsername("user@example.com")` => "us***@example.com" - Obfuscates a username. ### Error Handling ```go var statusErr *librespot.HTTPStatusError if errors.As(err, &statusErr) { switch { case statusErr.StatusCode >= 500: // retryable server failure case statusErr.StatusCode >= 400: // refusal: restricted/rate-limited media } } ``` ``` -------------------------------- ### librespot package usage example Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/librespot.md Usage example for the librespot package, imported as the alias librespot. Shows how to parse a Spotify URI, print its representations, create a NullLogger, and includes commented-out calls to NewMediaFromTrack and ObfuscateUsername. ```go import librespot "github.com/devgianlu/go-librespot" id, err := librespot.SpotifyIdFromUri("spotify:track:4iObEfGnTmVIBMcuDowJ0") if err != nil { return err } fmt.Println(id.Uri(), id.Base62(), id.Hex()) logger := librespot.NullLogger{} // librespot.NewMediaFromTrack(...) // librespot.ObfuscateUsername("user@example.com") => "us***@example.com" ``` -------------------------------- ### Install dependencies and run tests on macOS Source: https://github.com/devgianlu/go-librespot/blob/master/CONTRIBUTING.md Install Go and native decoder dependencies, then run the same test tags as CI and build the daemon binary. ```sh brew install go libogg libvorbis flac mpg123 pkgconf ``` ```sh go test -count=1 -tags "test_unit test_integration" ./... ``` ```sh go build -o /tmp/go-librespot-test ./cmd/daemon ``` -------------------------------- ### zeroconf Usage Examples Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/zeroconf.md Code examples demonstrating the usage of the zeroconf package for advertising a Spotify Connect device. ```APIDOC ## zeroconf.NewZeroconf ### Description Creates a new zeroconf instance for advertising a Spotify Connect device on the local network. It initializes the mDNS service with the provided logger, port, device name, device ID, device type, and network interfaces. ### Method Constructor ### Signature ```go func NewZeroconf(logger *slog.Logger, port int, name string, deviceId string, deviceType devicespb.DeviceType, interfaces []string, avahi bool) (*Zeroconf, error) ``` ### Parameters - **logger** (*slog.Logger) - Required - Logger for logging. - **port** (int) - Required - Port number for the service. - **name** (string) - Required - Device name to advertise. - **deviceId** (string) - Required - Unique device identifier. - **deviceType** (devicespb.DeviceType) - Required - Type of the device (e.g., SPEAKER). - **interfaces** ([]string) - Required - List of network interface names to use; empty for all. - **avahi** (bool) - Required - Whether to use Avahi for mDNS. ### Returns - **z** (*Zeroconf) - The zeroconf instance. - **err** (error) - Error if initialization fails (e.g., unknown interface name or mDNS registration failure). ### Example ```go z, err := zeroconf.NewZeroconf(logger, 0, "raspberry", deviceId, devicespb.DeviceType_SPEAKER, []string{}, true) if err != nil { return fmt.Errorf("failed initializing zeroconf: %w", err) } ``` ``` ```APIDOC ## zeroconf.Zeroconf.Serve ### Description Starts serving the zeroconf advertisement and handles incoming user requests from Spotify apps trying to claim the device. The provided callback is invoked for each new user request. ### Method Method ### Signature ```go func (z *Zeroconf) Serve(handler func(req NewUserRequest) bool) error ``` ### Parameters - **handler** (func(req NewUserRequest) bool) - Required - Callback function that receives a NewUserRequest and returns a boolean indicating whether to accept the request. ### Returns - **err** (error) - Error if serving fails. ### Example ```go if err := z.Serve(func(req zeroconf.NewUserRequest) bool { fmt.Println("new user:", req.Username, "from:", req.DeviceName) accepted := authenticate(req.Username, req.AuthBlob) return accepted }); err != nil { return err } ``` ``` ```APIDOC ## zeroconf.Zeroconf.Close ### Description Closes the zeroconf instance, stopping the advertisement and releasing resources. ### Method Method ### Signature ```go func (z *Zeroconf) Close() ``` ### Example ```go defer z.Close() ``` ``` ```APIDOC ## zeroconf.Zeroconf.SetCurrentUser ### Description Sets the current user for the zeroconf advertisement. ### Method Method ### Signature ```go func (z *Zeroconf) SetCurrentUser(username string) ``` ### Parameters - **username** (string) - Required - The username to set. ### Example ```go z.SetCurrentUser("") ``` ``` ```APIDOC ## zeroconf.Zeroconf.SetDeviceName ### Description Updates the device name in the advertisement. Logs on failure. ### Method Method ### Signature ```go func (z *Zeroconf) SetDeviceName(name string) ``` ### Parameters - **name** (string) - Required - The new device name. ### Example ```go z.SetDeviceName("kitchen") ``` ``` -------------------------------- ### Usage example: creating resolver and fetching endpoints Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/apresolve.md Creates an ApResolver with preferFirewallFriendlyPorts set to true, fetches all endpoints, and retrieves rotating address functions for accesspoint, spclient, and dealer. The Get* functions return addresses in order, refetching when exhausted. ```go import "github.com/devgianlu/go-librespot/apresolve" resolver := apresolve.NewApResolver(logger, client, true /* prefer :443/:80 */) if err := resolver.FetchAll(ctx); err != nil { return fmt.Errorf("failed getting endpoints: %w", err) } apAddr := resolver.GetAccesspoint(ctx) // func(ctx) string, rotating spAddr := resolver.GetSpclient(ctx) dealerAddr := resolver.GetDealer(ctx) ``` -------------------------------- ### Install go-librespot with Homebrew Source: https://github.com/devgianlu/go-librespot/blob/master/README.md Installs go-librespot on macOS and Linux using Homebrew. Requires the Homebrew package manager to be installed. ```shell brew install go-librespot ``` -------------------------------- ### Serve Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/zeroconf.md Starts the HTTP server for Spotify client communication. ```APIDOC ## Serve ### Description Serves the HTTP endpoints (`/` with form `action=getInfo|addUser`), dispatching `addUser` requests to the handler and replying with the result. Returns when the listener dies (nil if intentionally closed). ### Signature ```go func (z *Zeroconf) Serve(handler HandleNewRequestFunc) error ``` ### Parameters - **handler** (HandleNewRequestFunc) - Required - Callback for handling `addUser` requests ### Returns - **error** - nil if intentionally closed, otherwise error from the listener ``` -------------------------------- ### Embedding the daemon usage example Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/daemon-app.md Usage example for embedding the daemon. Creates a FileStateStore, constructs a daemon with daemon.New, and runs it until cancelled. Handles construction failure for invalid device type or FLAC without plugin. ```go import ( "context" librespot "github.com/devgianlu/go-librespot" "github.com/devgianlu/go-librespot/daemon" ) store := NewFileStateStore("my-state.json", "my-credentials.json", librespot.NullLogger{}) app, err := daemon.New(&daemon.Options{ Logger: librespot.NullLogger{}, Config: &daemon.Config{DeviceName: "kitchen", /* ... */}, StateStore: store, }) if err != nil { // handle construction failure (invalid device type, FLAC without plugin, ...) } defer func() { _ = app.Close() }() _ = app.Run(ctx) // blocks until cancelled ``` -------------------------------- ### Usage example: creating client and sending a request Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/mercury.md Shows creating a Mercury client and sending a GET request with an empty payload, handling the error. Replace the URI and payload with your actual Hermes endpoint and protobuf body. ```go import "github.com/devgianlu/go-librespot/mercury" hg := mercury.NewClient(logger, accesspoint) payload := make([]byte, 0) // e.g. marshal a resumption protobuf body here resp, err := hg.Request(ctx, "GET", "hm://resumption/v1/…", map[string][]byte{}, payload, ) if err != nil { return fmt.Errorf("mercury request failed: %w", err) } ``` -------------------------------- ### Config file example and equivalent CLI invocations Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/configuration.md Example YAML configuration file for go-librespot, showing all main options and their values. The first shell command uses a config directory, the second overrides specific settings via -c flags, showing how to override the YAML config partially. ```yaml log_level: info device_name: kitchen device_type: speaker audio_backend: alsa audio_device: default mixer_device: default mixer_control_name: Master bitrate: 320 crossfade_duration: 8 normalisation_disabled: false volume_steps: 100 zeroconf_enabled: true zeroconf_backend: builtin server: enabled: true address: localhost port: 3678 allow_origin: '' image_size: default cache: enabled: true dir: '' size_limit: 1GB metadata: enabled: true context_sweep: false max_tracks: 800 credentials: type: interactive interactive: callback_port: 0 mpris_enabled: false flac_enabled: false ``` ```shell go-librespot -config_dir /srv/librespot go-librespot -c zeroconf_backend=avahi -c server.port=3678 -c credentials.type=device_auth ``` -------------------------------- ### Usage example for tracks.NewTrackListFromContext Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Shows how to create a track list from context, seek to a specific track (playing it ahead when not found), and take a snapshot to inspect the current and next tracks. ```go import "github.com/devgianlu/go-librespot/tracks" list, err := tracks.NewTrackListFromContext(ctx, logger, spc, spotCtx) if err != nil { return fmt.Errorf("failed initializing context resolver: %w", err) } // skip to a specific track, playing it ahead of the context when not found _ = list.TrySeekTo(ctx, &connectpb.ContextTrack{Uri: "spotify:track:4iObEfGnTmVIBMcuDowJ0"}) snap := list.Snapshot(ctx, nil) fmt.Println("now playing:", snap.Current.Uri, "| next:", len(snap.Next)) ``` -------------------------------- ### Cache usage example Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/cache.md Shows how to initialize the cache and use it to serve a replayed track from disk or fall back to a download, persisting the stream via OnComplete. Caching is best-effort; failures only log a warning. ```go import "github.com/devgianlu/go-librespot/cache" c, err := cache.New(logger, cfg.Cache.Dir, cfg.Cache.SizeLimit) if err != nil { return fmt.Errorf("failed initializing audio cache: %w", err) } // replayed track: no CDN download if reader, ok := c.File(fileId); ok { // reader.Size() bytes of encrypted audio, ready to decrypt rawStream = reader } else { rawStream = httpStream // download path; OnComplete persists below httpStream.OnComplete(func(r io.ReaderAt, size int64) { if err := c.SaveFile(fileId, io.NewSectionReader(r, 0, size)); err != nil { logger.WithError(err).Warnf("failed caching audio file") // best effort } }) } ``` -------------------------------- ### Basic play and status polling with curl Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/endpoints.md Basic play and status polling example. Starts playback of a Spotify playlist, optionally skipping to a specific track, then fetches the current status and the context tracks for the playlist. ```bash curl -X POST http://127.0.0.1:3678/player/play \ -H 'Content-Type: application/json' \ -d '{"uri":"spotify:playlist:37i9bQ...","skip_to_uri":"spotify:track:4iObEfGnTmVIBMcuDowJ0","paused":false}' curl -s http://127.0.0.1:3678/status | python3 -m json.tool curl -s 'http://127.0.0.1:3678/context/tracks?uri=spotify:playlist:37i9bQ...' | python3 -m json.tool ``` -------------------------------- ### Install build dependencies on Debian-based systems Source: https://github.com/devgianlu/go-librespot/blob/master/README.md Installs the required decode libraries and libasound2 on Debian-based systems (Debian, Ubuntu, Raspbian) for building go-librespot from source. ```shell sudo apt-get install libogg-dev libvorbis-dev libflac-dev libmpg123-dev libasound2-dev ``` -------------------------------- ### main Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/daemon-app.md Entry point for the daemon binary. Seeds the RNG, loads configuration, sets up logging, creates the state store, optionally starts API and MPRIS servers, builds the daemon, and runs it until interrupted. ```APIDOC ## main ### Description Entry point for the daemon binary. Seeds the RNG, loads configuration, sets up logging, creates the state store, optionally starts API and MPRIS servers, builds the daemon, and runs it until interrupted. ### Method N/A (Go function) ### Endpoint N/A ### Parameters None ### Request Example ```go func main() { // ... } ``` ### Response No return value. ``` -------------------------------- ### Config load error handling example Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/daemon-app.md Error handling example for config load as done by the binary. Checks for errAlreadyRunning (lockfile owned by another instance) and exits with code 1, otherwise returns the error. Also wraps daemon.New errors with fmt.Errorf. ```go var cfg cliConfig if err := loadCLIConfig(&cfg); err != nil { if errors.Is(err, errAlreadyRunning) { // another instance owns the lockfile fmt.Println("could not start:", err) os.Exit(1) } return err } app, err := daemon.New(&daemon.Options{ Logger: logEntry, Config: cfg.toDaemonConfig(), StateStore: store, APIServer: apiServer, MediaPlayer: mediaPlayer, }) if err != nil { return fmt.Errorf("failed creating app: %w", err) } ``` -------------------------------- ### Install vcpkg dependencies for Windows MinGW Source: https://github.com/devgianlu/go-librespot/blob/master/CROSS_COMPILE.md Installs the required decode libraries (libogg, libvorbis, libflac, mpg123) for Windows cross-compilation using the x64-mingw-static triplet. ```shell vcpkg install --triplet x64-mingw-static ``` -------------------------------- ### Usage example for NewOutput with ALSA backend Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/output.md Example of creating an ALSA output with the NewOutput factory. The VolumeUpdate channel must be buffered; the helper sendVolumeUpdate drops a stale buffered value before sending and panics if the channel is unbuffered. The player daemon rate-limits these updates (100 ms timer) before pushing them to Spotify. ```go import "github.com/devgianlu/go-librespot/output" out, err := output.NewOutput(&output.NewOutputOptions{ Log: logger, Backend: "alsa", Reader: source, // librespot.Float32Reader (e.g. SwitchingAudioSource) SampleRate: 44100, ChannelCount: 2, Device: "default", Mixer: "default", Control: "Master", InitialVolume: 0.8, VolumeUpdate: volumeCh, // buffered chan float32 }) if err != nil { return fmt.Errorf("failed opening output: %w", err) } defer func() { _ = out.Close() }() _ = out.Resume() _ = out.SetVolume(0.5) delay, err := out.DelayMs() ``` -------------------------------- ### Install MinGW build dependencies on Windows with MSYS2 Source: https://github.com/devgianlu/go-librespot/blob/master/README.md Installs the MinGW packages required for building go-librespot on Windows with MSYS2. The default backend is WASAPI, and MSVC .lib files will not link with CGO. ```shell pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-pkg-config mingw-w64-x86_64-libogg mingw-w64-x86_64-libvorbis mingw-w64-x86_64-flac mingw-w64-x86_64-mpg123 ``` -------------------------------- ### Error handling example Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/login5.md Demonstrates how to inspect errors from Login for retryable HTTP errors and login errors with a protobuf code. ```go if err := c.Login(ctx, creds); err != nil { var he *login5.HTTPError if errors.As(err, &he) && he.Retryable() { // server outage / rate limit — retry later } var le *login5.LoginError if errors.As(err, &le) { return fmt.Errorf("bad credentials (code %v)", le.Code) } return err } ``` -------------------------------- ### go-librespot AP Package Usage Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/ap.md Usage examples for the go-librespot AP (accesspoint) package, demonstrating how to create an accesspoint client, connect with stored credentials or a Spotify token, subscribe to packets, send packets, and close the connection. ```APIDOC ## [Go] NewAccesspoint ### Description Creates a new accesspoint client instance with the provided logger, address function, and device ID. ### Method Constructor ### Signature ```go func NewAccesspoint(logger *slog.Logger, addrFunc func() (string, error), deviceId string) *Accesspoint ``` ### Parameters - **logger** (*slog.Logger) - Required - Logger for logging. - **addrFunc** (func() (string, error)) - Required - Function that returns the accesspoint address. - **deviceId** (string) - Required - Device identifier. ### Returns - **\*Accesspoint** - A new accesspoint client instance. ### Example ```go apc := ap.NewAccesspoint(logger, addrFunc, deviceId) ``` ## [Go] ConnectStored ### Description Connects to the accesspoint using stored credentials. Returns an error if the connection fails, including a specific error type for login failures. ### Method Method ### Signature ```go func (apc *Accesspoint) ConnectStored(ctx context.Context, username string, stored *StoredCredentials) error ``` ### Parameters - **ctx** (context.Context) - Required - Context for the connection. - **username** (string) - Required - Username for authentication. - **stored** (*StoredCredentials) - Required - Stored credentials for authentication. ### Returns - **error** - Returns nil on success, or an error. If the error is of type `ap.AccesspointLoginError`, credentials were refused. ### Example ```go if err := apc.ConnectStored(ctx, "username", stored); err != nil { if errors.As(err, &loginErr) { // ap.AccesspointLoginError: credentials refused } return err } ``` ## [Go] Receive ### Description Subscribes to receive packets of the specified types from the accesspoint. ### Method Method ### Signature ```go func (apc *Accesspoint) Receive(packetTypes ...ap.PacketType) <-chan *ap.Packet ``` ### Parameters - **packetTypes** (variadic ap.PacketType) - Required - Packet types to subscribe to. ### Returns - **<-chan \*ap.Packet** - A channel that receives packets of the subscribed types. ### Example ```go ch := apc.Receive(ap.PacketTypeProductInfo, ap.PacketTypeCountryCode) ``` ## [Go] Send ### Description Sends a packet of the specified type with the given payload to the accesspoint. ### Method Method ### Signature ```go func (apc *Accesspoint) Send(ctx context.Context, packetType ap.PacketType, payload []byte) error ``` ### Parameters - **ctx** (context.Context) - Required - Context for the send operation. - **packetType** (ap.PacketType) - Required - Type of packet to send. - **payload** ([]byte) - Required - Payload of the packet. ### Returns - **error** - Returns nil on success, or an error. ### Example ```go if err := apc.Send(ctx, ap.PacketTypePing, nil); err != nil { return err } ``` ## [Go] Close ### Description Closes the accesspoint client connection. ### Method Method ### Signature ```go func (apc *Accesspoint) Close() ``` ### Example ```go defer apc.Close() ``` ## [Go] ConnectSpotifyToken ### Description Connects to the accesspoint using a Spotify token (device-flow token). The username is derived by the accesspoint. ### Method Method ### Signature ```go func (apc *Accesspoint) ConnectSpotifyToken(ctx context.Context, username string, token string) error ``` ### Parameters - **ctx** (context.Context) - Required - Context for the connection. - **username** (string) - Required - Username (can be empty, derived by AP). - **token** (string) - Required - Spotify token for authentication. ### Returns - **error** - Returns nil on success, or an error. ### Example ```go _ = apc.ConnectSpotifyToken(ctx, "", token) // device-flow token, username derived by the AP ``` ``` -------------------------------- ### Error handling example (shuffle toggle) Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Error handling example for toggling shuffle and moving to the next track. ToggleShuffle returns an error if a context walk fails (state rolled back); GoNext returns false when the context is empty, indicating autoplay or stop. ```go if err := list.ToggleShuffle(ctx, true); err != nil { // a context walk failed; the list state was rolled back return err } if !list.GoNext(ctx) { // context empty; autoplay or stop return tracks.ErrNoContext } ``` -------------------------------- ### player.Play Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/player.md Starts or resumes playback. ```APIDOC ## player.Play ### Description Starts or resumes playback. ### Method Play ### Parameters None. ### Request Example ```go if err := player.Play(); err != nil { if errors.Is(err, player.ErrPlayerClosed) { // player was closed underneath; recreate it } return err } ``` ### Response Returns an error if the operation fails. Possible error: ErrPlayerClosed. ``` -------------------------------- ### GET / Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/endpoints.md The root endpoint returns API root information, including whether the daemon is ready for playback. ```APIDOC ## GET / ### Description Returns the API root object indicating whether the daemon is fully bootstrapped and ready to accept playback requests. ### Method GET ### Endpoint / ### Parameters None ### Response #### Success Response (200) - **playback_ready** (boolean) - Daemon fully bootstrapped (AP welcome + initial connect state + country code received) and ready to accept `/player/play`. #### Response Example { "playback_ready": true } ``` -------------------------------- ### Run go-librespot daemon from source Source: https://github.com/devgianlu/go-librespot/blob/master/README.md Runs the go-librespot daemon from source after cloning the repository and installing prerequisites. Requires Go 1.25 or higher and the listed libraries. ```shell go run ./cmd/daemon ``` -------------------------------- ### Usage example: key request and decryptor creation Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/audio.md Shows how to request an audio key and create a decryptor. The key request may return a KeyProviderError with code 1 indicating the key is refused for restricted files. The decryptor is created with the raw stream and the key. ```go import "github.com/devgianlu/go-librespot/audio" // audio key key, err := keys.Request(ctx, gid, fileId) if err != nil { var ke *audio.KeyProviderError if errors.As(err, &ke) { if ke.Code == 1 { /* key refused for this file (restricted) */ } } return err } // decrypt dec, err := audio.NewAesAudioDecryptor(rawStream, key) if err != nil { return err } ``` -------------------------------- ### Configure go-librespot with YAML options Source: https://github.com/devgianlu/go-librespot/blob/master/README.md YAML configuration options for go-librespot. Set `prefer_firewall_friendly_ports: true` to try accesspoints on 443 and 80 before 4070 for restrictive firewalls. Set `audio_output_pipe_wait_for_reader: true` to wait for a reader before starting playback with the pipe backend. ```yaml log_level: info # Log level configuration (trace, debug, info, warn, error) log_disable_timestamp: false # Whether to disable timestamps in log output device_id: '' # Spotify device ID (auto-generated) device_name: '' # Spotify device name device_type: computer # Spotify device type (icon) audio_backend: alsa # Default: audio-toolbox on macOS, wasapi on Windows, alsa elsewhere. Can also use pipe or pulseaudio. audio_backend_runtime_socket: '' # Audio backends' runtime socket to use, if backend is pulseaudio audio_device: default # ALSA audio device to use for playback mixer_device: '' # ALSA mixer device for volume synchronization mixer_control_name: Master # ALSA mixer control name for volume synchronization audio_buffer_time: 500000 # Audio buffer time in microseconds, ALSA only audio_period_count: 4 # Number of periods to request, ALSA only audio_output_pipe: '' # Path to a named pipe for audio output audio_output_pipe_format: s16le # Audio output pipe format (s16le, s32le, f32le) audio_output_pipe_wait_for_reader: false # Whether to wait for a reader to connect to the FIFO before starting playback (see below) bitrate: 160 # Playback bitrate (96, 160, 320) crossfade_duration: 0 # Crossfade duration between tracks in milliseconds (0 to disable) skip_debounce_ms: 600 # Coalesce rapid next/prev presses; the selected track loads once presses stop (0 to disable) volume_steps: 100 # Volume steps count initial_volume: 100 # Initial volume in steps (not applied to the mixer device) ignore_last_volume: false # Whether to ignore the last saved volume and always use initial_volume external_volume: false # Whether volume is controlled externally disable_autoplay: false # Whether autoplay of more songs should be disabled prefer_firewall_friendly_ports: false # Whether to try accesspoints on 443 and 80 before the default 4070 ``` -------------------------------- ### GET / Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/zeroconf.md HTTP endpoint for Spotify client communication. ```APIDOC ## GET / ### Description Handles Spotify client requests with `action=getInfo` or `action=addUser`. ### Method GET ### Endpoint `/` ### Query Parameters - **action** (string) - Required - `getInfo` or `addUser` ### Response #### Success Response (200) - **Content-Type**: application/json #### getInfo Response Example ```json { "status": 101, "status_string": "OK", "spotify_error": 0, "version": "2.7.1", "library_version": "librespot.VersionNumberString()", "account_req": "PREMIUM", "brand_display_name": "devgianlu", "model_display_name": "go-librespot", "voice_support": "NO", "token_type": "default", "group_status": "NONE", "scope": "streaming,client-authorization-universal", "device_id": "", "remote_name": "", "public_key": "", "device_type": "SPEAKER", "active_user": "" } ``` #### addUser Response Example ```json { "status": 101, "status_string": "OK", "spotify_error": 0 } ``` ### Error Responses - **400 Bad Request** - Missing fields, invalid base64, short blob, or bad checksum - **403 Forbidden** - Another user is already authenticating, or handler returns false ``` -------------------------------- ### NewSessionFromOptions Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/session.md Creates a new Session from the provided options. It performs a series of initialization steps including device validation, client token acquisition, accesspoint connection, authentication, and setup of internal components. Returns an error if any step fails. ```APIDOC ## NewSessionFromOptions ### Description Creates a new Session from the provided options. It performs a series of initialization steps including device validation, client token acquisition, accesspoint connection, authentication, and setup of internal components. Returns an error if any step fails. ### Signature `func NewSessionFromOptions(ctx context.Context, opts *Options) (*Session, error)` ### Parameters - `ctx` (context.Context) - Required - The context for the session initialization. - `opts` (*Options) - Required - The options for the session, including device type, device ID, and credentials. ### Returns - `*Session` - The initialized session. - `error` - An error if initialization fails. ### Bootstrap Order and Failure Modes | Step | Failure | |---|---| | Validate device type/id | `"missing device type"`; `"invalid device id: %w"` / `"invalid device id length: "` | | Obtain client token | `"failed obtaining client token: %w"` | | Connect accesspoint (`ap.NewAccesspoint`) | `"failed getting accesspoint from resolver: %w"` | | Authenticate per credentials type (switch on `opts.Credentials`) | `"failed authenticating accesspoint …"`, or error forms of `ap.AccesspointLoginError` | | Login5 (`s.login5.Login`) | `"failed authenticating with login5: %w"` | | spclient + dealer addresses | `"failed getting spclient/dealer from resolver: %w"` | | Mercury, audio-key provider, event manager | `"failed initializing event sender: %w"` | ``` -------------------------------- ### Streaming example with HttpChunkedReader Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/audio.md Shows how to stream audio from a CDN using HttpChunkedReader. OnComplete is used to persist the encrypted file to cache. Reads are performed with a 4096-byte buffer. ```go reader, err := audio.NewHttpChunkedReader(logger, client, cdnUrl) if err != nil { return fmt.Errorf("failed creating chunked reader: %w", err) } reader.OnComplete(func(r io.ReaderAt, size int64) { _ = cache.SaveFile(fileId, io.NewSectionReader(r, 0, size)) }) defer func() { _ = reader.Close() }() buf := make([]byte, 4096) n, err := reader.Read(buf) if err != nil { return fmt.Errorf("download failed: %w", err) } ``` -------------------------------- ### Daemon binary wiring function signatures Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/daemon-app.md Function signatures for the daemon binary wiring. main() seeds the RNG, loads CLI config, sets up logrus, creates the FileStateStore, optionally starts API and MPRIS servers, builds daemon.New, and runs until SIGINT/SIGTERM. loadCLIConfig binds flags and defaults; defaultAudioBackend returns wasapi (windows), audio-toolbox (darwin), or alsa; parseSize understands TB/GB/MB/KB/B suffixes and plain byte counts. ```go func main() func loadCLIConfig(cfg *cliConfig) error func defaultAudioBackend() string func parseSize(s string) (int64, error) func NewFileStateStore(statePath, credentialsPath string, log librespot.Logger) *FileStateStore ``` -------------------------------- ### Configure interactive mode Source: https://github.com/devgianlu/go-librespot/blob/master/README.md Configure interactive mode for authentication. Requires manual steps: start the daemon, open the authorization link, and optionally copy the URL from the browser if running on another device. ```yaml zeroconf_enabled: false # Whether to keep the device discoverable at all times credentials: type: interactive ``` -------------------------------- ### Error handling example for FetchAll Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/apresolve.md Shows how to handle a fetch error from FetchAll. The error is transient and the daemon retries at the next connect; no custom error type is used, and the error is wrapped with fmt.Errorf. ```go if err := resolver.FetchAll(ctx); err != nil { // transient — the daemon retries at the next connect; no custom error type return fmt.Errorf("resolver unreachable: %w", err) } ``` -------------------------------- ### PlaylistRevision Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/spclient.md Gets the current revision of a playlist. Sends a GET request to the playlist revision endpoint. ```APIDOC ## PlaylistRevision ### Description Gets the current revision of a playlist. ### Method GET ### Endpoint `/playlist/v2/playlist/` ### Parameters #### Path Parameters - `playlist` (librespot.SpotifyId) - Required - The playlist ID. ### Response #### Success Response (200) - Returns `[]byte` - The revision data. ``` -------------------------------- ### Using go-librespot player.NewPlayer and stream Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/player.md Creates a player with options like normalisation, crossfade, and ALSA backend, then receives eventsharely, changes the primary stream, and performs later commands. Shows prerequisites: spc (Spclient), keyProvider (AudioKey), eventManager (Events), and a buffered channel for volume updates. NewStream may return ErrMediaRestricted or ErrNoSupportedFormats; SetPrimaryStream or subsequent commands may fail; Close closes output and source. ```go import "github.com/devgianlu/go-librespot/player" player, err := player.NewPlayer(&player.Options{ Spclient: spc, AudioKey: keyProvider, Events: eventManager, Log: librespot.NullLogger{}, NormalisationEnabled: true, NormalisationPregain: -1.5, CrossfadeDuration: 8 * time.Second, AudioBackend: "alsa", AudioDevice: "default", MixerDevice: "default", MixerControlName: "Master", VolumeUpdate: volumeCh, // buffered chan float32 }) if err != nil { return err } evCh := player.Receive() stream, err := player.NewStream(ctx, client, id, 320, 0) if err != nil { return err // ErrMediaRestricted / ErrNoSupportedFormats / network... } if err := player.SetPrimaryStream(stream.Source, false, false); err != nil { return err } // later _ = player.Pause() _ = player.SeekMs(120_000) _ = player.ReopenOutput("bluealsa:DEV=XX:XX:XX:XX") defer player.Close() // closes output + source; commands afterwards return ErrPlayerClosed ``` -------------------------------- ### New(opts *Options) (*App, error) Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/daemon-app.md Creates a new App instance, validating options and initializing internal components such as device ID, API server, MPRIS server, and caches. ```APIDOC ## New(opts *Options) (*App, error) ### Description Creates a new App instance, validating options and initializing internal components such as device ID, API server, MPRIS server, and caches. ### Method New ### Signature `func New(opts *Options) (*App, error)` ### Parameters #### Path Parameters - **opts** (*Options) - Required - Construction options; must be non-nil. ### Request Example ```go app, err := daemon.New(&daemon.Options{ Logger: logger, Config: config, StateStore: store, }) ``` ### Response #### Success Response (200) - **app** (*App) - The initialized App instance. - **err** (error) - nil on success. #### Error Response - **error** - One of: "daemon: Options is required", "daemon: Options.Logger is required", "daemon: Options.Config is required", "daemon: Options.StateStore is required", "invalid device type: ", "FLAC playback requires a PlapPlay implementation". ``` -------------------------------- ### Initialize and serve zeroconf with avahi Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/zeroconf.md Initializes a zeroconf instance with avahi enabled and serves it, accepting or rejecting Spotify app connection requests via the callback. The callback receives a NewUserRequest with Username, DeviceName, and AuthBlob; return false to refuse. Close the instance with defer z.Close(). ```go import "github.com/devgianlu/go-librespot/zeroconf" z, err := zeroconf.NewZeroconf(logger, 0, "raspberry", deviceId, devicespb.DeviceType_SPEAKER, []string{}, true /* avahi */) if err != nil { return fmt.Errorf("failed initializing zeroconf: %w", err) } if err := z.Serve(func(req zeroconf.NewUserRequest) bool { // Called for every Spotify app that tries to claim the device. // Hand req.AuthBlob to the session layer; return false to refuse. fmt.Println("new user:", req.Username, "from:", req.DeviceName) accepted := authenticate(req.Username, req.AuthBlob) return accepted }); err != nil { return err } defer z.Close() ``` -------------------------------- ### Error handling example with ap.ErrAccesspointClosed check Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/mercury.md Example of error handling for a POST request, specifically checking for the accesspoint-closed error to signal connection loss. Use this pattern to distinguish fatal transport errors from other failures. ```go b, err := hg.Request(ctx, "POST", "hm://gabo-receiver-service/v3/events", fields, payload) if err != nil { if errors.Is(err, ap.ErrAccesspointClosed) { return // connection lost; session tear-down in progress } return err } ``` -------------------------------- ### Connect with stored credentials and subscribe to packets Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/ap.md Shows how to create an accesspoint client, connect with stored credentials, and handle login errors. It subscribes to product info and country code packets, sends a ping, and closes the connection. ```go import "github.com/devgianlu/go-librespot/ap" import pb "github.com/devgianlu/go-librespot/proto/spotify" apc := ap.NewAccesspoint(logger, addrFunc, deviceId) if err := apc.ConnectStored(ctx, "username", stored); err != nil { if errors.As(err, &loginErr) { // ap.AccesspointLoginError: credentials refused } return err } // subscribe to the two packets the daemon needs ch := apc.Receive(ap.PacketTypeProductInfo, ap.PacketTypeCountryCode) if err := apc.Send(ctx, ap.PacketTypePing, nil); err != nil { return err } defer apc.Close() ``` -------------------------------- ### List.CurrentTrack Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Gets the track currently at the cursor. ```APIDOC ## [Method] List.CurrentTrack ### Description Returns the track at the current cursor position. ### Signature ```go func (l *List) CurrentTrack() *connectpb.ProvidedTrack ``` ### Returns - **\*connectpb.ProvidedTrack** - The current track. ``` -------------------------------- ### List.Index Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Gets the current index within the context. ```APIDOC ## [Method] List.Index ### Description Returns the page/track index within the context. Empty when playing the queue or an injected track. ### Signature ```go func (l *List) Index() *connectpb.ContextIndex ``` ### Returns - **\*connectpb.ContextIndex** - The context index. ``` -------------------------------- ### List.GoStart Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Moves the cursor to the beginning of the track list. ```APIDOC ## [Method] List.GoStart ### Description Moves the cursor to the start of the context. ### Signature ```go func (l *List) GoStart(ctx context.Context) bool ``` ### Parameters - **ctx** (context.Context) - Required - The context for the operation. ### Returns - **bool** - True if the cursor was moved successfully. ``` -------------------------------- ### Error handling example for output device failure Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/output.md Error handling example: blocks on the error channel until the device dies, prints the error, and closes the output. The player loop reacts by emitting EventTypeStop with Err set and reopening the output on demand (player.ReopenOutput). ```go errCh := out.Error() err := <-errCh // blocks until the device dies fmt.Println("output device failed:", err) // the player loop reacts by emitting EventTypeStop with Err set and // reopening the output on demand (player.ReopenOutput). _ = out.Close() ``` -------------------------------- ### Options Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/session.md Configuration options for creating a new Spotify session. ```APIDOC ## Options ### Description Options configures a new Spotify session. It bundles everything a connected device needs: an encrypted accesspoint connection, a login5-issued bearer token, the spclient HTTP backend, the dealer WebSocket, the Mercury client, the audio key provider, and the gabito event manager. ### Fields | Field | Type | Required | Default | Description | |---|---|---|---|---| | `Log` | `librespot.Logger` | yes | — | Base logger | | `DeviceType` | `devicespb.DeviceType` | yes | — | Must not be `DeviceType_UNKNOWN` (else `"missing device type"`) | | `DeviceId` | `string` | yes | — | Hex; must decode to exactly 20 bytes | | `Credentials` | `any` (one of the credential structs below) | yes | — | Authentication source | | `ClientToken` | `string` | no | `""` (retrieved from `clienttoken.spotify.com`) | Spotify client token | | `Resolver` | `*apresolve.ApResolver` | no | `NewApResolver(log, client, PreferFirewallFriendlyPorts)` | Endpoint resolver | | `PreferFirewallFriendlyPorts` | `bool` | no | `false` | Only consulted when `Resolver` is nil | | `Client` | `*http.Client` | no | `&http.Client{Timeout: 30s}` | HTTP client | | `StateStore` | `librespot.StateStore` | yes | — | For the event manager | ### Example ```go type Options struct { Log librespot.Logger DeviceType devicespb.DeviceType DeviceId string Credentials any ClientToken string Resolver *apresolve.ApResolver PreferFirewallFriendlyPorts bool Client *http.Client StateStore librespot.StateStore } ``` ``` -------------------------------- ### List.UpcomingQueue Source: https://github.com/devgianlu/go-librespot/blob/master/_autodocs/api-reference/tracks.md Gets the upcoming queue, excluding the current track. ```APIDOC ## [Method] List.UpcomingQueue ### Description Returns the queue minus the currently playing entry. ### Signature ```go func (l *List) UpcomingQueue() []*connectpb.ContextTrack ``` ### Returns - **[]*connectpb.ContextTrack** - The upcoming queue. ```